package com.awspaas.user.apps.donenow_ctt.controller;

import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
import com.actionsoft.bpms.bo.engine.BO;
import com.actionsoft.bpms.commons.database.RowMap;
import com.actionsoft.bpms.commons.mvc.view.ResponseObject;
import com.actionsoft.bpms.server.UserContext;
import com.actionsoft.bpms.server.bind.annotation.Controller;
import com.actionsoft.bpms.server.bind.annotation.Mapping;
import com.actionsoft.bpms.util.DBSql;
import com.actionsoft.sdk.local.SDK;
import com.actionsoft.sdk.local.api.Logger;
import com.awspaas.user.apps.donenow_ctt.cttConstant;
import com.awspaas.user.apps.donenow_ctt.service.contractService;
import org.apache.commons.lang3.StringUtils;

import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.text.ParseException;
import static com.awspaas.user.apps.donenow_ctt.service.contractService.*;
import java.net.URLDecoder;
@Controller
public class contractApproveController {
    private static final Logger contractLogger = SDK.getLogAPI().getLogger(contractApproveController.class);//记录日志

    /**
     * 定期服务审批
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     */
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.approve_service")
    public ResponseObject approveService(UserContext uc, String approveDate, String approveIds) throws SQLException {
        if (StringUtils.isBlank(approveDate) || StringUtils.isBlank(approveIds))
            return ResponseObject.newErrResponse("参数错误");

        contractService.getInstance().postRecurringServices(uc, approveDate, approveIds);

        return ResponseObject.newOkResponse();
    }


    /**
     * 里程碑审批
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_milestone")
    public ResponseObject approveMilestone(UserContext uc, String approveDate, String approveIds) throws SQLException {
        if (StringUtils.isBlank(approveDate) || StringUtils.isBlank(approveIds))
            return ResponseObject.newErrResponse("参数错误");

        contractService.getInstance().postMilestone(uc, approveDate, approveIds);
        return ResponseObject.newOkResponse();
    }

    /**
     * / 调整订阅周期、定期服务（服务、服务调整、服务包、服务包调整、初始费用）、成本的计费总价
     * @param uc
     * @param periodPrice
     * @param id
     * @param type
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_updateExtendedPrice")
    public ResponseObject updateExtendedPrice(UserContext uc, Double periodPrice, String id, int type) throws SQLException {
        if (type == cttConstant.APPROVE_SUBSCRIPTIONS)//订阅
        {

        } else if (type == cttConstant.APPROVE_RECURRING_SERVICES)//定期服务审批
        {

            BO ccBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", id);//合同
            if (ccBO != null) {
                ccBO.set("ADJUST_SETUP_FEE", periodPrice);//调整后合同初始费用
                SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT", ccBO);
            } else {
                BO ccsaBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", id);//定期服务调整
                if (ccsaBO != null) {
                    ccsaBO.set("ADJUST_PRORATED_PRICE_CHANGE", periodPrice);//调整后合同初始费用
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", ccsaBO);
                } else {
                    BO ccspBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", id);//周期服务
                    if (ccspBO != null) {
                        ccspBO.set("PERIOD_ADJUSTED_PRICE", periodPrice);//调整后合同初始费用
                        SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", ccspBO);
                    }
                }
            }
        }

        return ResponseObject.newOkResponse();
    }

    /**
     * 审批初始费用
     * @param uc
     * @param ids
     * @param type
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_restoreInitialPrice")
    public ResponseObject restoreInitialPrice(UserContext uc, String ids, int type) throws SQLException {
        if (type == cttConstant.APPROVE_SUBSCRIPTIONS)//订阅
        {

        } else if (type == cttConstant.APPROVE_RECURRING_SERVICES)//定期服务审批
        {
            for (String id : ids.split(",")) {

                BO ccBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", id);//合同
                if (ccBO != null) {
                    ccBO.set("ADJUST_SETUP_FEE", ccBO.get("SETUP_FEE"));//调整后合同初始费用
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT", ccBO);
                } else {
                    BO ccsaBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", id);//定期服务调整
                    if (ccsaBO != null) {
                        ccsaBO.set("ADJUST_PRORATED_PRICE_CHANGE", ccsaBO.get("PRORATED_PRICE_CHANGE"));//调整后合同初始费用
                        SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", ccsaBO);
                    } else {
                        BO ccspBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", id);//周期服务
                        if (ccspBO != null) {
                            ccspBO.set("PERIOD_ADJUSTED_PRICE", ccspBO.get("PERIOD_PRICE"));//调整后合同初始费用
                            SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", ccspBO);
                        }
                    }
                }
            }
        }
        return ResponseObject.newOkResponse();
    }

    /**
     * 审批合同产品
     * @param uc
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_cost")
    public ResponseObject cost(UserContext uc, String approveDate, String approveIds) throws SQLException {

        boolean result = contractService.getInstance().postCost(uc, approveDate, approveIds);

        return result ? ResponseObject.newOkResponse() : ResponseObject.newErrResponse("审批失败");
    }

    /**
     * 撤销定期服务
     * @param uc
     * @param ids
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_recurring_services")
    public ResponseObject revokeRecurringServices(UserContext uc, String ids) throws SQLException {
        String result = contractService.getInstance().revokeRecurringServices(uc, ids);
        if (StringUtils.isNotBlank(result))
            return ResponseObject.newErrResponse(result);

        contractLogger.info("撤销定期服务成功");
        return ResponseObject.newOkResponse();
    }

    /**
     * 撤销里程碑审批
     * @param uc
     * @param ids
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_milestones")
    public ResponseObject revokeMilestones(UserContext uc, String ids) throws SQLException {
        String result = contractService.getInstance().revokeMilestones(uc, ids);
        if (StringUtils.isNotBlank(result)) return ResponseObject.newErrResponse(result);

        contractLogger.info("撤销里程碑成功");
        return ResponseObject.newOkResponse();
    }


    /**
     * 撤销合同成本
     * @param uc
     * @param ids
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_cost")
    public ResponseObject revokeCost(UserContext uc, String ids) throws SQLException {
        String result = contractService.getInstance().revokeCost(uc, ids);
        if (StringUtils.isNotBlank(result))
            return ResponseObject.newErrResponse(result);

        contractLogger.info("撤销合同成本成功");
        return ResponseObject.newOkResponse();
    }

    /**
     * 修改税
     * @param uc
     * @param id
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.modify_taxRate")
    public ResponseObject modifyTaxRate(UserContext uc, String id) throws SQLException {

        BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);

        String account_id = bo.getString("ACCOUNT_ID");
        String extended_price = bo.getString("EXTENDED_PRICE");
        String tax_category_id = bo.getString("TAX_CATEGORY_ID");
        String effective_tax_rate = bo.getString("EFFECTIVE_TAX_RATE");
        String tax_dollars = bo.getString("TAX_DOLLARS");

        List<RowMap> effectiveTaxRateList = DBSql.getMaps("select tax_cate_id as taxCateId,total_effective_tax_rate as effectiveTaxRate from bo_eu_dnd_tax_region_cate where tax_region_id IN (select tax_region_id from bo_eu_dncrm_account where id=?)", new Object[]{account_id});

        List<RowMap> taxCateList = DBSql.getMaps("SELECT oid,NAME FROM bo_eu_dnd_general WHERE general_table_id=44 AND CLOSED=0 AND ORGID=? AND oid in(select tax_cate_id from bo_eu_dnd_tax_region_cate where tax_region_id in (select tax_region_id from bo_eu_dncrm_account where id=?))", new Object[]{uc.getCompanyModel().getId(), account_id});

        Map<String, Object> map = new HashMap<>();
        map.put("taxCateList", taxCateList);
        map.put("effectiveTaxRateList", effectiveTaxRateList);

        map.put("taxCateId", tax_category_id);
        map.put("effectiveTaxRate", effective_tax_rate);
        map.put("taxDollars", tax_dollars);
        map.put("extendedPrice", extended_price);

        ResponseObject responseObject = ResponseObject.newOkResponse();
        responseObject.setData(map);

        return responseObject;
    }

    /**
     * 修改税保存
     * @param uc
     * @param id
     * @param taxCateId
     * @param effectiveTaxRate
     * @param taxDollars
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.modify_taxRate_save")
    public ResponseObject modifyTaxRateSave(UserContext uc, String id, String taxCateId, String effectiveTaxRate, String taxDollars) throws SQLException {
        BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
        bo.set("TAX_CATEGORY_ID", taxCateId);
        bo.set("tax_category_name", DBSql.getString("SELECT NAME FROM bo_eu_dnd_general WHERE general_table_id=44 AND oid=?", new Object[]{taxCateId}));
        bo.set("EFFECTIVE_TAX_RATE", effectiveTaxRate);
        bo.set("TAX_DOLLARS", taxDollars);
        SDK.getBOAPI().update("BO_EU_DNCRM_ACCOUNT_DEDUCTION", bo);
        return ResponseObject.newOkResponse();
    }

    /**
     * 批量审批人工
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_labor")
    public ResponseObject laborApprove(UserContext uc, String approveDate, String approveIds) {
        String[] IdArr = approveIds.split(",");
        String sql = "SELECT * FROM VIEW_EU_DNV_PENDING_LABOR WHERE ID IN(";
        for (int i = 0; IdArr.length > i; i++) {
            sql += "?,";
        }
        sql = sql.substring(0, sql.length() - 1) + ")";
        List<RowMap> list = DBSql.getMaps(sql, Arrays.stream(IdArr).toArray());
        String ORGID = uc.getCompanyModel().getId();
        LocalDate thisDate = getLocalDate(approveDate);//审批时间
        for (RowMap thisWorkEntry : list) {

            for (String key : thisWorkEntry.keySet()) {
                thisWorkEntry.put(key.toUpperCase(), thisWorkEntry.get(key) == null ? "" : thisWorkEntry.get(key));
            }

            BO oldEntry = SDK.getBOAPI().get("BO_EU_DNSDK_WORK_ENTRY", thisWorkEntry.getString("ID"));
            String COST_CODE_ID = oldEntry.getString("COST_CODE_ID");//获取成本代码ID

            System.out.println("COST_CODE_ID:" + COST_CODE_ID);

            RowMap thisTaxCate = null;//税收种类
            if (StringUtils.isNotBlank(COST_CODE_ID)) {
                RowMap costCode = DBSql.getMap("SELECT * FROM BO_EU_DND_COST_CODE WHERE OID=? AND ORGID=?", new Object[]{COST_CODE_ID, ORGID});
                if (costCode != null && StringUtils.isNotBlank(costCode.getString("TAX_CATEGORY_ID"))) {
                    thisTaxCate = DBSql.getMap("SELECT * FROM BO_EU_DND_GENERAL WHERE OID=?  AND ORGID=?", new Object[]{costCode.getString("TAX_CATEGORY_ID"), ORGID});
                }
            }

            System.out.println("TAX_CATEGORY_ID:" + thisTaxCate.getString("OID"));

            Double tax_rate = 0.0;
            RowMap thisTaxRegion = null;
            RowMap thisAccount = DBSql.getMap("SELECT * FROM BO_EU_DNCRM_ACCOUNT WHERE ID=?", new Object[]{thisWorkEntry.getString("ACCOUNT_ID")});
            // 获取税收信息
            if (thisAccount != null && StringUtils.isNotBlank(thisAccount.getString("TAX_REGION_ID"))) {
                thisTaxRegion = DBSql.getMap("SELECT * FROM BO_EU_DND_GENERAL WHERE OID=? AND ORGID=?", new Object[]{thisAccount.getString("TAX_REGION_ID"), ORGID});
            }

            System.out.println("TAX_REGION_ID:" + thisTaxRegion.getString("OID"));

            if (thisTaxCate != null && thisTaxRegion != null) {

                RowMap thisTax = DBSql.getMap("SELECT * FROM BO_EU_DND_TAX_REGION_CATE WHERE TAX_REGION_ID=? AND TAX_CATEGORY_ID=? AND ORGID=?", new Object[]{thisTaxRegion.getString("OID"), thisTaxCate.getString("OID"), ORGID});
                if (thisTax != null) {
                    tax_rate = thisTax.getDouble("TOTAL_EFFECTIVE_TAX_RATE");
                }
            }


            Double block_hour_multiplier = 0.0;

            RowMap thisConRole = DBSql.getMap("SELECT * FROM BO_EU_DNCTT_CONTRACT_RATE WHERE CONTRACT_ID=? AND ROLE_ID=?", new Object[]{thisWorkEntry.getString("CONTRACT_ID"), thisWorkEntry.getString("ROLE_ID")});

            if (thisConRole != null) {
                block_hour_multiplier = thisConRole.getDouble("BLOCK_HOUR_MULTIPLIER");//乘数
            } else {
                RowMap thisRole = DBSql.getMap("SELECT * FROM BO_EU_DNSYS_ROLE WHERE ID=?", new Object[]{thisWorkEntry.getString("ROLE_ID")});
                if (thisRole != null) {
                    block_hour_multiplier = thisRole.getDouble("HOURLY_FACTOR");//角色时间系数
                }
            }


            BO thisDed = new BO();
            thisDed.set("TYPE_ID", "1318");
            thisDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
            thisDed.set("POSTED_DATE", thisDate);
            thisDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
            thisDed.set("EXTENDED_PRICE", thisWorkEntry.getDouble("DOLLARS"));

            thisDed.set("ACCOUNT_ID", thisWorkEntry.getString("ACCOUNT_ID"));
            thisDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
            thisDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
            thisDed.set("BILL_ACCOUNT_ID", thisWorkEntry.getString("BILL_ACCOUNT_ID"));

            thisDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
            thisDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));

            thisDed.set("EFFECTIVE_TAX_RATE", tax_rate);

            thisDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
            thisDed.set("QUANTITY", thisWorkEntry.get("QUANTITY"));
            thisDed.set("RATE", thisWorkEntry.get("RATE"));
            thisDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
            thisDed.set("EXTENDED_COST", thisWorkEntry.get("COST"));
            if (thisTaxCate != null)
                thisDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("OID"));

            //是否免税
            if ((thisAccount == null || !thisAccount.getString("IS_TAX_EXEMPT").equals("1")) && tax_rate > 0) {
                thisDed.set("TAX_DOLLARS", thisWorkEntry.getDouble("DOLLARS") * (1 - 1 / (1 + tax_rate)));
            }
            if (StringUtils.isBlank(thisDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisDed.getString("ACCOUNT_ID")))
                thisDed.set("BILL_ACCOUNT_ID", thisDed.get("ACCOUNT_ID"));

            dn.recordFormChanges.record(uc, thisDed, "审批提交工时");
            SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisDed, uc);

            oldEntry.set("APPROVE_AND_POST_DATE", thisDate);
            oldEntry.set("APPROVE_AND_POST_USER_ID", uc.getUID());
            dn.recordFormChanges.record(uc, oldEntry, "审批提交工时");
            SDK.getBOAPI().update("BO_EU_DNSDK_WORK_ENTRY", oldEntry);
        }
        return ResponseObject.newOkResponse();
    }

    /**
     * 工时审批
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_labor2")
    public ResponseObject laborApprove2(UserContext uc, String approveDate, String approveIds) {
        String[] IdArr = approveIds.split(",");
        String sql = "SELECT * FROM VIEW_EU_DNV_PENDING_LABOR WHERE ID IN(";
        for (int i = 0; IdArr.length > i; i++) {
            sql += "?,";
        }
        sql = sql.substring(0, sql.length() - 1) + ")";
        List<RowMap> list = DBSql.getMaps(sql, Arrays.stream(IdArr).toArray());
        String ORGID = uc.getCompanyModel().getId();
        for (RowMap rowMap : list) {
            String COST_CODE_ID = getString(rowMap, "COST_CODE_ID");//获取成本代码ID
            RowMap thisTaxCate = null;//税收种类
            if (StringUtils.isNotBlank(COST_CODE_ID)) {
                RowMap costCode = DBSql.getMap("SELECT * FROM BO_EU_DND_COST_CODE WHERE OID=? AND ORGID=?", new Object[]{COST_CODE_ID, ORGID});
                if (costCode != null && StringUtils.isNotBlank(costCode.getString("TAX_CATEGORY_ID"))) {
                    thisTaxCate = DBSql.getMap("SELECT * FROM BO_EU_DND_GENERAL WHERE OID=?  AND ORGID=?", new Object[]{costCode.getString("TAX_CATEGORY_ID"), ORGID});
                }
            }

            RowMap thisContract = null;//合同
            RowMap thisAccount = null;
            String CONTRRACT_ID = getString(rowMap, "CONTRACT_ID");

            if (StringUtils.isNotBlank(CONTRRACT_ID)) {
                thisContract = DBSql.getMap("SELECT * FROM BO_EU_DNCTT_CONTRACT WHERE ID=?", new Object[]{CONTRRACT_ID});

                RowMap thatAccount = DBSql.getMap("SELECT * FROM BO_EU_DNCRM_ACCOUNT WHERE ID IN (SELECT ACCOUNT_ID FROM BO_EU_DNCTT_CONTRACT WHERE ID=?)", new Object[]{CONTRRACT_ID});
                if (thatAccount != null)
                    thisAccount = thatAccount;
            }


            RowMap thisTaxRegion = null;
            Double tax_rate = 0.0;//税率
            String TASK_ID = rowMap.getString("TASK_ID");
            if (StringUtils.isNotBlank(TASK_ID)) {
                RowMap thatAccount = DBSql.getMap("SELECT * FROM BO_EU_DNCRM_ACCOUNT WHERE ID IN (SELECT ACCOUNT_ID FROM BO_EU_DNSDK_TASK WHERE ID=?)", new Object[]{TASK_ID});
                if (thatAccount != null)
                    thisAccount = thatAccount;
            }

            String thisContractId = thisContract == null ? "null" : thisContract.getString("ID");
            String ROLE_ID = rowMap.getString("ROLE_ID");

            Double laborRate = 0.0;
            //该员工的费率
            try {
                laborRate = DBSql.getDouble("SELECT f_get_labor_rate(" + thisContractId + "," + COST_CODE_ID + "," + ROLE_ID + "," + ORGID + ") as laborRate", "laborRate");
            } catch (Exception e) {
                System.out.println("获取费率异常" + e.getMessage());
            }


            // 获取税收信息
            if (thisAccount != null && StringUtils.isNotBlank(thisAccount.getString("TAX_REGION_ID"))) {
                thisTaxRegion = DBSql.getMap("SELECT * FROM BO_EU_DND_GENERAL WHERE OID=? AND ORGID=?", new Object[]{thisAccount.getString("TAX_REGION_ID"), ORGID});
            }
            if (thisTaxCate != null && thisTaxRegion != null) {

                RowMap thisTax = DBSql.getMap("SELECT * FROM BO_EU_DND_TAX_REGION_CATE WHERE TAX_REGION_ID=? AND TAX_CATEGORY_ID=? AND ORGID=?", new Object[]{thisTaxRegion.getString("OID"), thisTaxCate.getString("OID"), ORGID});
                if (thisTax != null) {
                    tax_rate = thisTax.getDouble("TOTAL_EFFECTIVE_TAX_RATE");
                }
            }

            LocalDate thisDate = getLocalDate(approveDate);//审批时间
            Double block_hour_multiplier = 0.0;
            if (thisContract != null && StringUtils.isNotBlank(ROLE_ID)) {
                //var thisConRole = new ctt_contract_rate_dal(_connect).GetSinRate(thisContract.id, (long)oldEntry.role_id);
                //return FindSignleBySql<ctt_contract_rate>($"SELECT * from ctt_contract_rate where delete_time = 0 and contract_id = {contract_id} and //role_id ={role_id}");
                RowMap thisConRole = DBSql.getMap("SELECT * FROM BO_EU_DNCTT_CONTRACT_RATE WHERE CONTRACT_ID=? AND ROLE_ID=?", new Object[]{thisContract.getString("ID"), ROLE_ID});

                if (thisConRole != null) {
                    block_hour_multiplier = thisConRole.getDouble("BLOCK_HOUR_MULTIPLIER");//乘数
                } else {
                    RowMap thisRole = DBSql.getMap("SELECT * FROM BO_EU_DNSYS_ROLE WHERE ID=?", new Object[]{ROLE_ID});
                    if (thisRole != null) {
                        block_hour_multiplier = thisRole.getDouble("HOURLY_FACTOR");//角色时间系数
                    }
                }
            }

            Map<String, Double> block = new HashMap<>();//存储预付id，和总价
            Map<String, Double> blockRate = new HashMap<>();//存储预付id，和总价

            RowMap thisWorkEntry = DBSql.getMap("select * from BO_EU_DNSDK_WORK_ENTRY where ID=?", new Object[]{getString(rowMap, "ID")});
            LocalDate thisEntryDate = getLocalDate(getString(thisWorkEntry, "START_TIME"));
            LocalDate thisEntryEndDate = StringUtils.isBlank(getString(thisWorkEntry, "START_TIME")) ? thisEntryDate : getLocalDate(getString(thisWorkEntry, "END_TIME"));

            LocalDate thisContractStartDate = thisContract == null ? null : thisContract.getDate("START_DATE").toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();
            LocalDate thisContractEndDate = thisContract == null ? null : thisContract.getDate("END_DATE").toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDate();

            Double totalHours = StringUtils.isBlank(thisWorkEntry.getString("HOURS_BILLED_DEDUCTION")) ? thisWorkEntry.getDouble("HOURS_BILLED") : thisWorkEntry.getDouble("HOURS_BILLED_DEDUCTION");
            if (thisContract != null && thisEntryDate != null && thisContractStartDate.compareTo(thisEntryDate) <= 0 && thisContractEndDate.compareTo(thisEntryEndDate) >= 0) {

                String endDate = thisEntryDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

                // 这个工时的总额
                Double totalMoney = StringUtils.isBlank(thisWorkEntry.getString("HOURS_BILLED_DEDUCTION")) ? thisWorkEntry.getDouble("HOURS_BILLED") : thisWorkEntry.getDouble("HOURS_BILLED_DEDUCTION");
                totalMoney = totalMoney * (StringUtils.isBlank(thisWorkEntry.getString("HOURS_RATE_DEDUCTION")) ? laborRate : thisWorkEntry.getDouble("HOURS_RATE_DEDUCTION"));

                //不计费的工时
                if (laborRate == null || laborRate <= 0) {
                    BO thisBlockDed = new BO();
                    thisBlockDed.set("TYPE_ID", "1318");//工时
                    thisBlockDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                    thisBlockDed.set("POSTED_DATE", thisDate);
                    thisBlockDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                    thisBlockDed.set("ACCOUNT_ID", thisAccount.getString("ID"));
                    thisBlockDed.set("QUANTITY", totalHours);
                    thisBlockDed.set("BILL_CREATE_USER_ID", thisWorkEntry.getString("CREATEUSER"));
                    thisBlockDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                    thisBlockDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                    thisBlockDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                    thisBlockDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                    thisBlockDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                    thisBlockDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));
                    thisBlockDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                    if (thisTaxCate != null)
                        thisBlockDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("OID"));

                    thisBlockDed.set("EXTENDED_PRICE", 0);
                    thisBlockDed.set("BILL_ACCOUNT_ID", thisWorkEntry.get("BILL_ACCOUNT_ID"));


                    if (StringUtils.isBlank(thisBlockDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisBlockDed.getString("ACCOUNT_ID")))
                        thisBlockDed.set("BILL_ACCOUNT_ID", thisBlockDed.get("ACCOUNT_ID"));

                    dn.recordFormChanges.record(uc, thisBlockDed, "审批提交工时");
                    SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisBlockDed, uc);
                } else if (thisContract != null && thisContract.getString("TYPE_ID").equals("1203")) {
                    //预付费合同

                    //获取工时
                    Double extended_price = DBSql.getDouble("SELECT sum(round(b.rate/" + laborRate + " - ifnull((SELECT sum(extended_price)FROM bo_eu_dncrm_account_deduction WHERE contract_block_id = b.id ),0),2)) AS RATE FROM bo_eu_dnctt_contract_block b WHERE b.contract_id='" + thisContract.getString("ID") + "' and b.status_id=1 and b.start_date <='" + thisEntryDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "' and b.end_date >='" + endDate + "'", "RATE");
                    extended_price = extended_price * laborRate;

                    //  Double thisEntryRate = GetRateByCodeAndRole(COST_CODE_ID, ROLE_ID);

                    List<RowMap> block_hours = DBSql.getMaps("SELECT b.ID,(round(b.rate/" + laborRate + " - ifnull((SELECT sum(extended_price)FROM bo_eu_dncrm_account_deduction WHERE contract_block_id = b.id ),0),2)) AS RATE FROM bo_eu_dnctt_contract_block b WHERE b.contract_id='" + thisContract.getString("ID") + "' and b.status_id=1 and b.start_date <='" + thisEntryDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + "' and b.end_date >='" + endDate + "'");

                    for (RowMap block_hour : block_hours) {
                        if (block_hour.get("RATE") != null && block_hour.getDouble("RATE") > 0) {
                            block.put(block_hour.getString("ID"), block_hour.getDouble("RATE"));
                        }
                        blockRate.put(block_hour.getString("ID"), block_hour.getDouble("RATE"));
                    }

                    if (totalMoney == null || totalMoney <= extended_price) {
                        if (totalMoney != null) {
                            Double extend = 0.0;
                            for (String key : block.keySet()) {
                                extend += block.get(key);
                                if (extend <= totalMoney) {
                                    BO thisBlockDed = new BO();
                                    thisBlockDed.set("TYPE_ID", "1318");
                                    thisBlockDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                                    thisBlockDed.set("POSTED_DATE", thisDate);
                                    thisBlockDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                                    thisBlockDed.set("EXTENDED_PRICE", block.get(key));
                                    thisBlockDed.set("ACCOUNT_ID", thisWorkEntry.getString("ACCOUNT_ID"));
                                    thisBlockDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                                    thisBlockDed.set("BILL_CREATE_USER_ID", thisWorkEntry.getString("CREATEUSER"));
                                    thisBlockDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                                    thisBlockDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                                    thisBlockDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                                    thisBlockDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                                    thisBlockDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                                    thisBlockDed.set("QUANTITY", totalHours);
                                    thisBlockDed.set("RATE", blockRate.get(key));
                                    thisBlockDed.set("CONTRACT_BLOCK_ID", key);
                                    thisBlockDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
                                    thisBlockDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));

                                    if (thisTaxCate != null)
                                        thisBlockDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("ID"));


                                    if (thisAccount.getString("IS_TAX_EXEMPT").equals("1") == false) {
                                        thisBlockDed.set("TAX_DOLLARS", thisWorkEntry.getDouble("HOURS_BILLED") * laborRate * (1 - 1 / (1 + tax_rate)));
                                    }
                                    if (thisWorkEntry.getString("IS_BILLABLE").equals("0"))
                                        thisBlockDed.set("EXTENDED_PRICE", 0);


                                    if (StringUtils.isBlank(thisBlockDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisBlockDed.getString("ACCOUNT_ID")))
                                        thisBlockDed.set("BILL_ACCOUNT_ID", thisBlockDed.get("ACCOUNT_ID"));

                                    dn.recordFormChanges.record(uc, thisBlockDed, "审批提交工时");
                                    SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisBlockDed, uc);

                                    //修改相应的预付费信息
                                    BO thisBlock = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_BLOCK", key);
                                    if (thisBlock != null) {
                                        thisBlock.set("STATUS_ID", 0);
                                        dn.recordFormChanges.record(uc, thisBlock, "修改预付费信息");
                                        SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_BLOCK", thisBlock);
                                    }
                                    if (extend == totalMoney) {
                                        break;
                                    }


                                } else {
                                    BO thisBlockDed = new BO();
                                    thisBlockDed.set("TYPE_ID", "1318");
                                    thisBlockDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                                    thisBlockDed.set("POSTED_DATE", thisDate);
                                    thisBlockDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                                    thisBlockDed.set("EXTENDED_PRICE", block.get(key));
                                    thisBlockDed.set("ACCOUNT_ID", thisWorkEntry.getString("ACCOUNT_ID"));
                                    thisBlockDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                                    thisBlockDed.set("BILL_CREATE_USER_ID", thisWorkEntry.getString("CREATEUSER"));
                                    thisBlockDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                                    thisBlockDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                                    thisBlockDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                                    thisBlockDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                                    thisBlockDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                                    thisBlockDed.set("QUANTITY", totalHours);
                                    thisBlockDed.set("RATE", blockRate.get(key));
                                    thisBlockDed.set("CONTRACT_BLOCK_ID", key);
                                    thisBlockDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
                                    thisBlockDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));
                                    if (thisTaxCate != null)
                                        thisBlockDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("ID"));

                                    thisBlockDed.set("extended_price", block.get(key) - (extend - totalMoney));
                                    if (thisWorkEntry.getString("IS_BILLABLE").equals("0"))
                                        thisBlockDed.set("EXTENDED_PRICE", 0);


                                    if (StringUtils.isBlank(thisBlockDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisBlockDed.getString("ACCOUNT_ID")))
                                        thisBlockDed.set("BILL_ACCOUNT_ID", thisBlockDed.get("ACCOUNT_ID"));

                                    dn.recordFormChanges.record(uc, thisBlockDed, "审批提交工时");
                                    SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisBlockDed, uc);
                                    break;
                                }
                            }
                        } else {

                            BO thisBlockDed = new BO();
                            thisBlockDed.set("TYPE_ID", "1318");
                            thisBlockDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                            thisBlockDed.set("POSTED_DATE", thisDate);
                            thisBlockDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                            thisBlockDed.set("ACCOUNT_ID", thisWorkEntry.getString("ACCOUNT_ID"));
                            thisBlockDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                            thisBlockDed.set("BILL_CREATE_USER_ID", thisWorkEntry.getString("CREATEUSER"));
                            thisBlockDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                            thisBlockDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                            thisBlockDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                            thisBlockDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                            thisBlockDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                            thisBlockDed.set("QUANTITY", totalHours);
                            thisBlockDed.set("RATE", thisWorkEntry.get("HOURS_RATE_DEDUCTION") != null ? thisWorkEntry.get("HOURS_RATE_DEDUCTION") : laborRate);
                            thisBlockDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
                            thisBlockDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));
                            if (thisTaxCate != null)
                                thisBlockDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("ID"));
                            if (thisWorkEntry.getString("IS_BILLABLE").equals("0"))
                                thisBlockDed.set("EXTENDED_PRICE", 0);


                            if (StringUtils.isBlank(thisBlockDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisBlockDed.getString("ACCOUNT_ID")))
                                thisBlockDed.set("BILL_ACCOUNT_ID", thisBlockDed.get("ACCOUNT_ID"));

                            dn.recordFormChanges.record(uc, thisBlockDed, "审批提交工时");
                            SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisBlockDed, uc);
                        }
                    } else {
                        RowMap ccnr = DBSql.getMap("select * from BO_EU_DNCTT_CONTRACT_NOTIFY_RULE where contract_id=?", new Object[]{thisContractId});
                        if (ccnr != null && ccnr.get("RATE") != null && ccnr.getDouble("RATE") > 0) {
                            Double extend = 0.0;
                            for (String key : block.keySet()) {
                                extend = extend + block.get(key);
                                BO thisBlockDed = new BO();
                                thisBlockDed.set("TYPE_ID", "1318");
                                thisBlockDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                                thisBlockDed.set("POSTED_DATE", thisDate);
                                thisBlockDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                                thisBlockDed.set("EXTENDED_PRICE", block.get(key));
                                thisBlockDed.set("ACCOUNT_ID", thisWorkEntry.getString("ACCOUNT_ID"));
                                thisBlockDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                                thisBlockDed.set("BILL_CREATE_USER_ID", thisWorkEntry.getString("CREATEUSER"));
                                thisBlockDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                                thisBlockDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                                thisBlockDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                                thisBlockDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                                thisBlockDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                                thisBlockDed.set("QUANTITY", totalHours);
                                thisBlockDed.set("RATE", blockRate.get(key));
                                thisBlockDed.set("CONTRACT_BLOCK_ID", key);
                                thisBlockDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
                                thisBlockDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));
                                if (thisTaxCate != null)
                                    thisBlockDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("ID"));

                                if (thisWorkEntry.getString("IS_BILLABLE").equals("0"))
                                    thisBlockDed.set("EXTENDED_PRICE", 0);

                                if (thisAccount.getString("IS_TAX_EXEMPT").equals("1") == false)
                                    thisBlockDed.set("TAX_DOLLARS", thisWorkEntry.getDouble("HOURS_BILLED") * laborRate * (1 - 1 / (1 + tax_rate)));

                                if (StringUtils.isBlank(thisBlockDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisBlockDed.getString("ACCOUNT_ID")))
                                    thisBlockDed.set("BILL_ACCOUNT_ID", thisBlockDed.get("ACCOUNT_ID"));

                                dn.recordFormChanges.record(uc, thisBlockDed, "审批提交工时");
                                SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisBlockDed, uc);

                                //修改相应的预付费信息
                                BO thisBlock = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_BLOCK", key);
                                if (thisBlock != null) {
                                    thisBlock.set("STATUS_ID", 0);
                                    dn.recordFormChanges.record(uc, thisBlock, "修改合同成本预付时间或费用");
                                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_BLOCK", thisBlock);
                                }
                            }

                            if (extend - totalMoney <= 0) {
                                //???  什么逻辑
                                System.out.println("合同金额不足");
                                System.out.println("后面怎么处理。。。。。。。。");
                            }


                        } else {
                            BO thisDed = new BO();
                            thisDed.set("TYPE_ID", "1318");
                            thisDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                            thisDed.set("POSTED_DATE", thisDate);
                            thisDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                            thisDed.set("EXTENDED_PRICE", thisWorkEntry.getDouble("HOURS_BILLED") * laborRate * block_hour_multiplier);
                            thisDed.set("ACCOUNT_ID", thisAccount.getString("ID"));
                            thisDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                            //thisDed.set("QUANTITY", thisWorkEntry.getDouble("HOURS_BILLED"));
                            thisDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                            thisDed.set("BILL_ACCOUNT_ID", thisWorkEntry.getString("BILL_ACCOUNT_ID"));
                            thisDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                            thisDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                            thisDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                            thisDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                            thisDed.set("QUANTITY", totalHours);
                            thisDed.set("RATE", thisWorkEntry.get("HOURS_RATE_DEDUCTION") != null ? thisWorkEntry.get("HOURS_RATE_DEDUCTION") : laborRate);
                            thisDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
                            thisDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));
                            if (thisTaxCate != null)
                                thisDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("ID"));

                            if (thisWorkEntry.getString("IS_BILLABLE").equals("0"))
                                thisDed.set("EXTENDED_PRICE", 0);

                            if (thisAccount.getString("IS_TAX_EXEMPT").equals("1") == false)
                                thisDed.set("TAX_DOLLARS", thisWorkEntry.getDouble("HOURS_BILLED") * laborRate * (1 - 1 / (1 + tax_rate)));

                            if (StringUtils.isBlank(thisDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisDed.getString("ACCOUNT_ID")))
                                thisDed.set("BILL_ACCOUNT_ID", thisDed.get("ACCOUNT_ID"));

                            dn.recordFormChanges.record(uc, thisDed, "审批提交工时");
                            SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisDed, uc);
                        }
                    }
                }
            }

            if (thisContract == null || (thisContract.getString("type_id").equals("1203") == false && thisContract.getString("TYPE_ID").equals("1202") == false) || thisContractStartDate.compareTo(thisEntryDate) > 0 || thisContractEndDate.compareTo(thisEntryEndDate) < 0) {
                BO thisDed = new BO();
                thisDed.set("TYPE_ID", "1318");
                thisDed.set("OBJECT_ID", thisWorkEntry.getString("ID"));
                thisDed.set("POSTED_DATE", thisDate);
                thisDed.set("TASK_ID", thisWorkEntry.getString("TASK_ID"));
                thisDed.set("EXTENDED_PRICE", thisWorkEntry.getDouble("HOURS_BILLED") * laborRate * block_hour_multiplier);
                thisDed.set("ACCOUNT_ID", thisAccount == null ? null : thisAccount.getString("ID"));
                thisDed.set("CONTRACT_ID", thisWorkEntry.getString("CONTRACT_ID"));
                //thisDed.set("QUANTITY", thisWorkEntry.getDouble("HOURS_BILLED"));
                thisDed.set("PURCHASE_ORDER_NO", thisWorkEntry.getString("PURCHASE_ORDER_NO"));
                thisDed.set("BILL_ACCOUNT_ID", thisWorkEntry.getString("BILL_ACCOUNT_ID"));
                thisDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
                thisDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
                thisDed.set("EFFECTIVE_TAX_RATE", tax_rate);
                thisDed.set("ROLE_ID", thisWorkEntry.getString("ROLE_ID"));
                thisDed.set("QUANTITY", totalHours);
                thisDed.set("RATE", thisWorkEntry.get("HOURS_RATE_DEDUCTION") != null ? thisWorkEntry.get("HOURS_RATE_DEDUCTION") : laborRate);
                thisDed.set("BLOCK_HOUR_MULTIPLIER", block_hour_multiplier);
                thisDed.set("EXTENDED_COST", GetEntryCost(thisWorkEntry.getString("ID")));
                if (thisTaxCate != null)
                    thisDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("ID"));

                if (thisWorkEntry.getString("IS_BILLABLE").equals("0"))
                    thisDed.set("EXTENDED_PRICE", 0);

                if (thisAccount.getString("IS_TAX_EXEMPT").equals("1") == false)
                    thisDed.set("TAX_DOLLARS", thisWorkEntry.getDouble("HOURS_BILLED") * laborRate * (1 - 1 / (1 + tax_rate)));

                if (StringUtils.isBlank(thisDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisDed.getString("ACCOUNT_ID")))
                    thisDed.set("BILL_ACCOUNT_ID", thisDed.get("ACCOUNT_ID"));

                dn.recordFormChanges.record(uc, thisDed, "审批提交工时");
                SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisDed, uc);
            }


            BO oldEntry = SDK.getBOAPI().get("BO_EU_DNSDK_WORK_ENTRY", thisWorkEntry.getString("ID"));
            oldEntry.set("APPROVE_AND_POST_DATE", thisDate);
            oldEntry.set("APPROVE_AND_POST_USER_ID", uc.getUID());
            dn.recordFormChanges.record(uc, oldEntry, "审批提交工时");
            SDK.getBOAPI().update("BO_EU_DNSDK_WORK_ENTRY", oldEntry);

            //通知
            // 待完善

        }

        return ResponseObject.newOkResponse();
    }

    /**
     * 获取字段值
     * @param rowMap
     * @param key
     * @return
     */
    private String getString(RowMap rowMap, String key) {
        if (rowMap.containsKey(key))
            return rowMap.getString(key);
        else if (rowMap.containsKey(key.toUpperCase()))
            return rowMap.getString(rowMap.getString(key.toUpperCase()));
        else if (rowMap.containsKey(key.toLowerCase()))
            return rowMap.getString(rowMap.getString(key.toLowerCase()));

        key = key.toUpperCase();
        for (String s : rowMap.keySet()) {
            if (s.toUpperCase().equals(key))
                return rowMap.getString(s);
        }
        return null;
    }


    /**
     * 费用审批
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.approve_expense")
    public ResponseObject expenseApprove(UserContext uc, String approveDate, String approveIds) {
        String[] IdArr = approveIds.split(",");
        String sql = "SELECT * FROM VIEW_EU_DNV_PENDING_EXPENSE WHERE ID IN(";
        for (int i = 0; IdArr.length > i; i++) {
            sql += "?,";
        }
        sql = sql.substring(0, sql.length() - 1) + ")";
        List<RowMap> list = DBSql.getMaps(sql, Arrays.stream(IdArr).toArray());
        for (RowMap rowMap : list) {
            String expenseId = getString(rowMap, "ID");
            RowMap oldExp = DBSql.getMap("SELECT * FROM BO_EU_DNSDK_EXPENSE WHERE ID=?", expenseId);
            RowMap thisTaxCate = null;//税收种类
            RowMap thisTaxRegion = null;//税区
            Double tax_rate = 0.0;//税率
            String contract_id = null;

            if (StringUtils.isNotBlank("COST_CODE_ID")) {
                RowMap thisCost = DBSql.getMap("SELECT * FROM BO_EU_DND_COST_CODE WHERE OID=? AND ORGID=?", new Object[]{getString(rowMap, "COST_CODE_ID"), uc.getCompanyModel().getId()});
                if (thisCost != null && StringUtils.isNotBlank(thisCost.getString("TAX_CATEGORY_ID"))) {
                    thisTaxCate = DBSql.getMap("SELECT * FROM BO_EU_DND_GENERAL WHERE OID=?  AND ORGID=?", new Object[]{thisCost.getString("TAX_CATEGORY_ID"), uc.getCompanyModel().getId()});
                }
            }


            RowMap thisAccount = DBSql.getMap("SELECT * FROM BO_EU_DNCRM_ACCOUNT WHERE ID=?", getString(rowMap, "ACCOUNT_ID"));
            if (thisAccount != null && StringUtils.isNotBlank(thisAccount.getString("TAX_REGION_ID"))) {
                thisTaxRegion = DBSql.getMap("SELECT * FROM BO_EU_DND_GENERAL WHERE OID=? AND ORGID=?", new Object[]{thisAccount.getString("TAX_REGION_ID"), uc.getCompanyModel().getId()});
            }


            if (thisTaxRegion != null && thisTaxCate != null) {
                tax_rate = DBSql.getDouble("SELECT TOTAL_EFFECTIVE_TAX_RATE FROM BO_EU_DND_TAX_REGION_CATE WHERE tax_region_id='" + thisTaxRegion.getString("OID") + "' and tax_cate_id='" + thisTaxCate.getString("OID") + "' and ORGID='" + uc.getCompanyModel().getId() + "'", "TOTAL_EFFECTIVE_TAX_RATE");
            }

            LocalDate thisDate = getLocalDate(approveDate);//审批时间

            if (StringUtils.isNotBlank(oldExp.getString("PROJECT_ID"))) {
                String projectRateContractId = DBSql.getString("SELECT CONTRACT_ID FROM BO_EU_DNPRO_PROJECT WHERE ID=?", new Object[]{oldExp.getString("PROJECT_ID")});
                if (StringUtils.isNotBlank(projectRateContractId)) {
                    contract_id = projectRateContractId;
                }
            }

            if (StringUtils.isNotBlank(oldExp.getString("TASK_ID"))) {
                String taskRateContractId = DBSql.getString("SELECT CONTRACT_ID FROM BO_EU_DNSDK_TASK WHERE ID=?", new Object[]{oldExp.getString("TASK_ID")});
                if (StringUtils.isNotBlank(taskRateContractId)) {
                    contract_id = taskRateContractId;
                }
            }


            Double extended_price = 0.0;
            if (oldExp.getString("IS_BILLABLE").equals("1")) {
                if (oldExp.get("AMOUNT_DEDUCTION") != null && oldExp.getDouble("AMOUNT_DEDUCTION") > 0) {
                    extended_price = oldExp.getDouble("AMOUNT_DEDUCTION");
                } else if (oldExp.get("AMOUNT") != null && oldExp.getDouble("AMOUNT") > 0) {
                    extended_price = oldExp.getDouble("AMOUNT");
                }
            }

            BO thisDed = new BO();
            thisDed.set("CONTRACT_ID", contract_id);
            thisDed.set("OBJECT_ID", oldExp.getString("ID"));
            thisDed.set("TYPE_ID", "1322");
            thisDed.set("POSTED_DATE", thisDate);
            thisDed.set("TASK_ID", oldExp.getString("TASK_ID"));
            thisDed.set("EXTENDED_PRICE", extended_price);
            thisDed.set("EXTENDED_COST", extended_price);
            thisDed.set("ACCOUNT_ID", oldExp.getString("ACCOUNT_ID"));
            thisDed.set("QUANTITY", 1);
            thisDed.set("BILL_CREATE_USER_ID", oldExp.getString("CREATEUSER"));
            thisDed.set("PURCHASE_ORDER_NO", oldExp.getString("PURCHASE_ORDER_NO"));
            thisDed.set("TAX_CATEGORY_NAME", thisTaxCate == null ? null : thisTaxCate.getString("NAME"));
            thisDed.set("TAX_REGION_NAME", thisTaxRegion == null ? null : thisTaxRegion.getString("NAME"));
            thisDed.set("EFFECTIVE_TAX_RATE", tax_rate);

            if ((thisAccount == null || !thisAccount.getString("IS_TAX_EXEMPT").equals("1")) && tax_rate > 0) {
                thisDed.set("TAX_DOLLARS", extended_price * (1 - 1 / (1 + tax_rate)));
            }

            if (thisTaxCate != null)
                thisDed.set("TAX_CATEGORY_ID", thisTaxCate.getString("OID"));

            if (StringUtils.isBlank(thisDed.getString("BILL_ACCOUNT_ID")) && StringUtils.isNotBlank(thisDed.getString("ACCOUNT_ID")))
                thisDed.set("BILL_ACCOUNT_ID", thisDed.get("ACCOUNT_ID"));

            dn.recordFormChanges.record(uc, thisDed, "审批提交费用");
            SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", thisDed, uc);

            BO oldExpense = SDK.getBOAPI().get("BO_EU_DNSDK_EXPENSE", oldExp.getString("ID"));
            oldExpense.set("APPROVE_AND_POST_DATE", thisDate);
            oldExpense.set("APPROVE_AND_POST_USER_ID", uc.getUID());
            dn.recordFormChanges.record(uc, oldExpense, "审批提交费用");
            SDK.getBOAPI().update("BO_EU_DNSDK_EXPENSE", oldExpense);


            //通知
        }


        return ResponseObject.newOkResponse();
    }


    /**
     * 撤销工时审批
     * @param ids
     * @return
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_labor")
    public ResponseObject revokeLabor(UserContext uc, String ids) throws SQLException {
        String result = contractService.getInstance().revokeLabor(uc, ids);
        if (StringUtils.isNotBlank(result))
            return ResponseObject.newErrResponse(result);
        return ResponseObject.newOkResponse();
    }


    /**
     * 撤销费用审批
     * @param uc
     * @param ids
     * @return
     * @throws SQLException
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_expense")
    public ResponseObject revokeExpense(UserContext uc, String ids) throws SQLException {
        String result = contractService.getInstance().revokeExpense(uc, ids);
        if (StringUtils.isNotBlank(result))
            return ResponseObject.newErrResponse(result);
        return ResponseObject.newOkResponse();
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.updateCommissionPeriodEndDate")
    public ResponseObject updateCommissionPeriodEndDate(UserContext uc, String id, String periodEndDate) {
        // 1. 参数校验
        if (StringUtils.isBlank(id)) {
            return ResponseObject.newErrResponse("id 不能为空");
        }
        if (StringUtils.isBlank(periodEndDate)) {
            return ResponseObject.newErrResponse("PERIOD_END_DATE 不能为空");
        }

        try {

            // 3. 构建更新 SQL
            String updateSql = "UPDATE BO_EU_DNCTT_COMMISSION SET PERIOD_END_DATE = ? WHERE ID = ?";

            // 4. 执行更新操作
            int rows = DBSql.update(updateSql, new Object[]{periodEndDate, id});

            // 5. 构造返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("id", id);
            result.put("periodEndDate", periodEndDate);
            result.put("updatedRows", rows);

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);

            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("更新失败：" + e.getMessage());
        }
    }

    // 按月补贴：周期增量=1个月
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.insertMonthlyCommission")
    public ResponseObject insertMonthlyCommission(UserContext uc,
                                                  String bindid,
                                                  String startDate,
                                                  String endDate,
                                                  String amount) {
        return insertCommissionByPeriod(uc, bindid, startDate, endDate, amount, 1);
    }

    // 按季度补贴：周期增量=3个月
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.insertQuarterlyCommission")
    public ResponseObject insertQuarterlyCommission(UserContext uc,
                                                    String bindid,
                                                    String startDate,
                                                    String endDate,
                                                    String amount) {
        return insertCommissionByPeriod(uc, bindid, startDate, endDate, amount, 3);
    }

    // 按年补贴：周期增量=12个月
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.insertYearlyCommission")
    public ResponseObject insertYearlyCommission(UserContext uc,
                                                 String bindid,
                                                 String startDate,
                                                 String endDate,
                                                 String amount) {
        return insertCommissionByPeriod(uc, bindid, startDate, endDate, amount, 12);
    }

    // 一次性补贴：只生成一个周期（开始日期到结束日期）
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.insertOneTimeCommission")
    public ResponseObject insertOneTimeCommission(UserContext uc,
                                                  String bindid,
                                                  String startDate,
                                                  String endDate,
                                                  String amount) {
        // 1. 参数校验
        if (StringUtils.isBlank(bindid)) return ResponseObject.newErrResponse("bindid不能为空");
        if (StringUtils.isBlank(startDate)) return ResponseObject.newErrResponse("开始日期不能为空");
        if (StringUtils.isBlank(endDate)) return ResponseObject.newErrResponse("结束日期不能为空");
        if (StringUtils.isBlank(amount)) return ResponseObject.newErrResponse("金额不能为空");

        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            // 校验开始日期是否早于结束日期
            Date start = sdf.parse(startDate);
            Date end = sdf.parse(endDate);
            if (start.after(end)) {
                return ResponseObject.newErrResponse("开始日期不能晚于结束日期");
            }

            // 2. 插入SQL（仅插入一条记录）
            String insertSql = "INSERT INTO BO_EU_DNCTT_COMMISSION_PERIOD " +
                    "(ID, ORGID, BINDID, PERIOD_BEGIN_DATE, PERIOD_END_DATE, " +
                    "COMMISSION_DOLLARS, PERIOD_ADJUSTED_PRICE) " +
                    "VALUES (UUID(), ?, ?, ?, ?, ?, ?)";

            // 3. 执行插入（仅一次，不循环）
            int rows = DBSql.update(insertSql, new Object[]{
                    uc.getCompanyModel().getId(),
                    bindid,
                    startDate, // 直接用用户输入的开始日期
                    endDate,   // 直接用用户输入的结束日期
                    amount,
                    amount
            });

            // 4. 返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("bindid", bindid);
            result.put("rows", rows); // 应为1（成功插入1条）
            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);

            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("插入失败：" + e.getMessage());
        }
    }

    // 通用方法：按周期增量（1/3/12个月）生成多个周期
    private ResponseObject insertCommissionByPeriod(UserContext uc,
                                                    String bindid,
                                                    String startDate,
                                                    String endDate,
                                                    String amount,
                                                    int periodIncrement) {
        // 参数校验（同上，略）
        if (StringUtils.isBlank(bindid)) return ResponseObject.newErrResponse("bindid不能为空");
        // ... 其他校验

        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Calendar cal = Calendar.getInstance();
            cal.setTime(sdf.parse(startDate));
            int settleDay = cal.get(Calendar.DATE); // 固定结算日

            Calendar endCal = Calendar.getInstance();
            endCal.setTime(sdf.parse(endDate));

            String insertSql = "INSERT INTO BO_EU_DNCTT_COMMISSION_PERIOD " +
                    "(ID, ORGID, BINDID, PERIOD_BEGIN_DATE, PERIOD_END_DATE, " +
                    "COMMISSION_DOLLARS, PERIOD_ADJUSTED_PRICE) " +
                    "VALUES (UUID(), ?, ?, ?, ?, ?, ?)";

            int rows = 0;
            Calendar currentCal = Calendar.getInstance();
            currentCal.setTime(sdf.parse(startDate));

            // 循环生成周期（按传入的增量：1/3/12个月）
            while (true) {
                Date periodStart = currentCal.getTime();
                if (periodStart.after(endCal.getTime())) break;

                currentCal.add(Calendar.MONTH, periodIncrement);
                currentCal.set(Calendar.DATE, settleDay);
                currentCal.add(Calendar.DATE, -1);
                Date periodEnd = currentCal.getTime();

                if (periodEnd.after(endCal.getTime())) periodEnd = endCal.getTime();

                rows += DBSql.update(insertSql, new Object[]{
                        uc.getCompanyModel().getId(),
                        bindid,
                        sdf.format(periodStart),
                        sdf.format(periodEnd),
                        amount,
                        amount
                });

                currentCal.add(Calendar.DATE, 1);
                currentCal.set(Calendar.DATE, settleDay);
            }

            Map<String, Object> result = new HashMap<>();
            result.put("rows", rows);
            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);

            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("插入失败：" + e.getMessage());
        }
    }

    // 通用查询代理列表（不变）
    private List<Map<String, Object>> queryAgentList(String bindid) {
        List<Map<String, Object>> agentList = new ArrayList<>();
        int offset = 0;
        while (true) {
            String querySql = "SELECT AGENT, RATIO FROM BO_EU_DNCTT_COMMISSION_AGENT WHERE bindid = ? LIMIT 1 OFFSET ?";
            RowMap agentMap = DBSql.getMap(querySql, new Object[]{bindid, offset});
            if (agentMap == null) break;

            String agent = agentMap.getString("AGENT");
            BigDecimal ratio = parseRatio(agentMap.get("RATIO"));
            if (ratio == null) {
                throw new RuntimeException("代理 " + agent + " 的比例为空");
            }

            Map<String, Object> agentInfo = new HashMap<>();
            agentInfo.put("AGENT", agent);
            agentInfo.put("RATIO", ratio);
            agentList.add(agentInfo);
            offset++;
        }
        return agentList;
    }

    // 通用比例解析（不变）
    private BigDecimal parseRatio(Object ratioObj) {
        if (ratioObj == null) return null;
        try {
            if (ratioObj instanceof BigDecimal) return (BigDecimal) ratioObj;
            if (ratioObj instanceof Double) return BigDecimal.valueOf((Double) ratioObj);
            if (ratioObj instanceof Float) return BigDecimal.valueOf(((Float) ratioObj).doubleValue());
            if (ratioObj instanceof Integer) return BigDecimal.valueOf(((Integer) ratioObj).longValue());
            return new BigDecimal(ratioObj.toString());
        } catch (Exception e) {
            throw new RuntimeException("比例格式错误：" + ratioObj, e);
        }
    }

    // 通用周期生成方法（按月/季度/年复用，不变）
    private ResponseObject batchInsertAgentByPeriod(UserContext uc,
                                                    String bindid,
                                                    String startDate,
                                                    String endDate,
                                                    String amount,
                                                    int periodIncrement) {
        try {
            // 参数校验
            if (StringUtils.isBlank(bindid) || StringUtils.isBlank(startDate) ||
                    StringUtils.isBlank(endDate) || StringUtils.isBlank(amount)) {
                return ResponseObject.newErrResponse("bindid、日期、金额不能为空");
            }

            // 查询代理列表
            List<Map<String, Object>> agentList = queryAgentList(bindid);
            if (agentList.isEmpty()) {
                return ResponseObject.newErrResponse("未找到二级代理数据");
            }

            // 日期与金额处理
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Calendar cal = Calendar.getInstance();
            cal.setTime(sdf.parse(startDate));
            int settleDay = cal.get(Calendar.DATE);
            Calendar endCal = Calendar.getInstance();
            endCal.setTime(sdf.parse(endDate));
            BigDecimal baseAmount = new BigDecimal(amount);

            // 插入SQL
            String insertSql = "INSERT INTO BO_EU_DNCTT_COMMISSION_PERIOD_AGENT " +
                    "(ID, AGENT, ORGID, BINDID, PERIOD_BEGIN_DATE, PERIOD_END_DATE, " +
                    "COMMISSION_DOLLARS, PERIOD_ADJUSTED_PRICE) " +
                    "VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?)";

            int totalRows = 0;
            for (Map<String, Object> agentInfo : agentList) {
                String agent = (String) agentInfo.get("AGENT");
                BigDecimal ratio = (BigDecimal) agentInfo.get("RATIO");
                BigDecimal realCommission = baseAmount.multiply(ratio.divide(new BigDecimal("100")));

                // 按周期增量生成子周期
                Calendar currentCal = Calendar.getInstance();
                currentCal.setTime(sdf.parse(startDate));
                while (true) {
                    Date periodStart = currentCal.getTime();
                    if (periodStart.after(endCal.getTime())) break;

                    // 计算周期结束日
                    currentCal.add(Calendar.MONTH, periodIncrement);
                    currentCal.set(Calendar.DATE, settleDay);
                    currentCal.add(Calendar.DATE, -1);
                    Date periodEnd = currentCal.getTime();
                    if (periodEnd.after(endCal.getTime())) periodEnd = endCal.getTime();

                    // 插入记录
                    totalRows += DBSql.update(insertSql, new Object[]{
                            agent, uc.getCompanyModel().getId(), bindid,
                            sdf.format(periodStart), sdf.format(periodEnd),
                            realCommission.toPlainString(), realCommission.toPlainString()
                    });

                    // 准备下一个周期
                    currentCal.add(Calendar.DATE, 1);
                    currentCal.set(Calendar.DATE, settleDay);
                }
            }

            // 返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("bindid", bindid);
            result.put("rows", totalRows);
            result.put("agentCount", agentList.size());
            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("插入失败：" + e.getMessage());
        }
    }

    // 1. 按月生成（周期增量=1个月）
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.batchInsertAgentMonthly")
    public ResponseObject batchInsertAgentMonthly(UserContext uc,
                                                  String bindid,
                                                  String startDate,
                                                  String endDate,
                                                  String amount) {
        return batchInsertAgentByPeriod(uc, bindid, startDate, endDate, amount, 1);
    }

    // 2. 按季度生成（周期增量=3个月）
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.batchInsertAgentQuarterly")
    public ResponseObject batchInsertAgentQuarterly(UserContext uc,
                                                    String bindid,
                                                    String startDate,
                                                    String endDate,
                                                    String amount) {
        return batchInsertAgentByPeriod(uc, bindid, startDate, endDate, amount, 3);
    }

    // 3. 按年生成（新增，周期增量=12个月）
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.batchInsertAgentYearly")
    public ResponseObject batchInsertAgentYearly(UserContext uc,
                                                 String bindid,
                                                 String startDate,
                                                 String endDate,
                                                 String amount) {
        return batchInsertAgentByPeriod(uc, bindid, startDate, endDate, amount, 12); // 12个月=1年
    }

    // 4. 一次性生成（无周期拆分）
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.batchInsertAgentOneTime")
    public ResponseObject batchInsertAgentOneTime(UserContext uc,
                                                  String bindid,
                                                  String startDate,
                                                  String endDate,
                                                  String amount) {
        try {
            // 参数校验
            if (StringUtils.isBlank(bindid) || StringUtils.isBlank(startDate) ||
                    StringUtils.isBlank(endDate) || StringUtils.isBlank(amount)) {
                return ResponseObject.newErrResponse("bindid、日期、金额不能为空");
            }

            // 日期合法性校验
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            Date start = sdf.parse(startDate);
            Date end = sdf.parse(endDate);
            if (start.after(end)) {
                return ResponseObject.newErrResponse("开始日期不能晚于结束日期");
            }

            // 查询代理列表
            List<Map<String, Object>> agentList = queryAgentList(bindid);
            if (agentList.isEmpty()) {
                return ResponseObject.newErrResponse("未找到二级代理数据");
            }

            // 插入SQL
            String insertSql = "INSERT INTO BO_EU_DNCTT_COMMISSION_PERIOD_AGENT " +
                    "(ID, AGENT, ORGID, BINDID, PERIOD_BEGIN_DATE, PERIOD_END_DATE, " +
                    "COMMISSION_DOLLARS, PERIOD_ADJUSTED_PRICE) " +
                    "VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?)";

            int totalRows = 0;
            BigDecimal baseAmount = new BigDecimal(amount);
            for (Map<String, Object> agentInfo : agentList) {
                String agent = (String) agentInfo.get("AGENT");
                BigDecimal ratio = (BigDecimal) agentInfo.get("RATIO");
                BigDecimal realCommission = baseAmount.multiply(ratio.divide(new BigDecimal("100")));

                // 直接插入“开始→结束”的1条记录
                totalRows += DBSql.update(insertSql, new Object[]{
                        agent, uc.getCompanyModel().getId(), bindid,
                        startDate, endDate,
                        realCommission.toPlainString(), realCommission.toPlainString()
                });
            }

            // 返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("bindid", bindid);
            result.put("rows", totalRows);
            result.put("agentCount", agentList.size());
            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("插入失败：" + e.getMessage());
        }
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.checkCommissionPeriodExists2")
    public ResponseObject checkCommissionPeriodExists2(UserContext uc,
                                                       String bindid,
                                                       String startDate,
                                                       String endDate) {
        try {
            String checkSql = "SELECT COUNT(1) FROM BO_EU_DNCTT_COMMISSION_PERIOD " +
                    "WHERE BINDID = ? " +
                    "AND PERIOD_BEGIN_DATE <= ? " +
                    "AND PERIOD_END_DATE >= ?";

            int existCount = DBSql.getInt(checkSql, new Object[]{bindid, endDate, startDate});

            Map<String, Object> result = new HashMap<>();
            result.put("exists", existCount > 0);

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("查询失败：" + e.getMessage());
        }
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.checkAgentCommissionPeriodExists")
    public ResponseObject checkAgentCommissionPeriodExists(UserContext uc,
                                                           String bindid,
                                                           String startDate,
                                                           String endDate) {
        try {
            String checkSql = "SELECT COUNT(1) FROM BO_EU_DNCTT_COMMISSION_PERIOD_AGENT " +
                    "WHERE BINDID = ?";

            int existCount = DBSql.getInt(checkSql, new Object[]{bindid});

            Map<String, Object> result = new HashMap<>();
            result.put("exists", existCount > 0);

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("查询失败：" + e.getMessage());
        }
    }

    @Mapping("com.awspaas.user.apps.donenow_ctt.updateAgentCommissionById")
    public ResponseObject updateAgentCommissionById(UserContext uc,
                                                    String ids,
                                                    String commissionPayId,
                                                    String agent,
                                                    String ACCOUNT_PAY_DATE,
                                                    BigDecimal totalPayAmount, // 前端传递的“总支付金额”
                                                    String payer,
                                                    String remark,
                                                    String invoiceTypeId,
                                                    String agentInvoiceNo,
                                                    String invoiceFile) throws SQLException {
        Connection connUpdate = null;
        try {
            // 1. 参数校验
            if (StringUtils.isBlank(ids)) {
                return ResponseObject.newErrResponse("ids不能为空");
            }
            if (StringUtils.isBlank(commissionPayId)) {
                return ResponseObject.newErrResponse("commissionPayId不能为空");
            }
            if (totalPayAmount == null || totalPayAmount.compareTo(BigDecimal.ZERO) <= 0) {
                return ResponseObject.newErrResponse("总支付金额不能为空且需大于0");
            }
            String[] idArr = ids.split(",");

            // 2. 查询每个ID的“周期金额”（假设字段为 PERIOD_AMOUNT）
            String querySql = "SELECT ID, PERIOD_ADJUSTED_PRICE FROM BO_EU_DNCTT_COMMISSION_PERIOD_AGENT WHERE ID IN ("
                    + String.join(",", Collections.nCopies(idArr.length, "?")) + ")";
            List<Object> queryParams = new ArrayList<>(Arrays.asList(idArr));
            List<RowMap> periodList = DBSql.getMaps(querySql, queryParams.toArray());

            // 3. 预处理：统计总周期金额 + 校验数据
            Map<String, BigDecimal> idPeriodAmountMap = new HashMap<>();
            BigDecimal totalPeriodAmount = BigDecimal.ZERO;
            for (RowMap row : periodList) {
                String id = row.getString("ID");
                // 处理周期金额：先转String，再转BigDecimal（兼容类型）
                String periodAmountStr = row.getString("PERIOD_ADJUSTED_PRICE");
                BigDecimal periodAmount = null;
                if (periodAmountStr != null && !periodAmountStr.trim().isEmpty()) {
                    try {
                        periodAmount = new BigDecimal(periodAmountStr.trim());
                    } catch (NumberFormatException e) {
                        System.out.println("周期金额格式错误，ID：" + id + "，值：" + periodAmountStr);
                        continue;
                    }
                }
                if (periodAmount == null || periodAmount.compareTo(BigDecimal.ZERO) <= 0) {
                    System.out.println("周期金额非法（空或<=0），ID：" + id);
                    continue;
                }
                idPeriodAmountMap.put(id, periodAmount);
                totalPeriodAmount = totalPeriodAmount.add(periodAmount);
            }

            // 校验总周期金额（避免除零）
            if (totalPeriodAmount.compareTo(BigDecimal.ZERO) <= 0) {
                Map<String, Object> result = new HashMap<>();
                result.put("totalMatched", periodList.size());
                result.put("updateCount", 0);
                result.put("message", "选中周期的总金额为0，无法计算比例");
                ResponseObject responseObject = ResponseObject.newOkResponse();
                responseObject.setData(result);
                return responseObject;
            }

            // 4. 初始化事务连接
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);

            int updateCount = 0;

            // 5. 循环按比例分配并更新 BO_EU_DNCTT_COMMISSION_PERIOD_AGENT 表
            for (String id : idArr) {
                if (!idPeriodAmountMap.containsKey(id)) {
                    System.out.println("跳过未查询到周期金额的ID：" + id);
                    continue;
                }

                // 按比例计算单周期应分配金额
                BigDecimal periodAmount = idPeriodAmountMap.get(id);
                BigDecimal ratio = periodAmount.divide(totalPeriodAmount, 6, BigDecimal.ROUND_HALF_UP); // 比例保留6位小数
                BigDecimal allocatedAmount = totalPayAmount.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);

                // 构建更新SQL（累加 PAY_AMOUNT）
                StringBuilder updateSql = new StringBuilder("UPDATE BO_EU_DNCTT_COMMISSION_PERIOD_AGENT  SET ");
                List<Object> updateParams = new ArrayList<>();

                updateSql.append("COMMISSION_PAY_ID = ?, ");
                updateSql.append("AGENT = ?, ");
                updateSql.append("ACCOUNT_PAY_DATE = ?, ");
                updateSql.append("PAY_AMOUNT =  ?, "); // 核心：更新单周期分配金额
                updateSql.append("PAYER = ?, ");
                updateParams.add(commissionPayId.trim());
                updateParams.add(agent);
                updateParams.add(ACCOUNT_PAY_DATE);
                updateParams.add(allocatedAmount);
                updateParams.add(payer);

                // 可选字段（有值才更新）
                if (remark != null && !remark.trim().isEmpty()) {
                    updateSql.append("REMARK = ?, ");
                    updateParams.add(remark.trim());
                }
                if (invoiceTypeId != null && !invoiceTypeId.trim().isEmpty()) {
                    updateSql.append("INVOICE_TYPE_ID = ?, ");
                    updateParams.add(invoiceTypeId.trim());
                }
                if (agentInvoiceNo != null && !agentInvoiceNo.trim().isEmpty()) {
                    updateSql.append("AGENT_INVOICE_NO = ?, ");
                    updateParams.add(agentInvoiceNo.trim());
                }
                if (invoiceFile != null && !invoiceFile.trim().isEmpty()) {
                    updateSql.append("INVOICE_FILE = ?, ");
                    updateParams.add(invoiceFile.trim());
                }

                // 移除最后多余的逗号
                if (updateSql.charAt(updateSql.length() - 2) == ',') {
                    updateSql.setLength(updateSql.length() - 2);
                }
                updateSql.append(" WHERE ID = ?");
                updateParams.add(id.trim());

                // 执行更新
                int affectedRows = DBSql.update(connUpdate, updateSql.toString(), updateParams.toArray());
                if (affectedRows > 0) {
                    updateCount++;
                }
            }

            // 新增：更新 BO_EU_DNCTT_COMMISSION_PAY 表的 PAY_AMOUNT 字段
            String updatePayTableSql = "UPDATE BO_EU_DNCTT_COMMISSION_PAY SET PAY_AMOUNT = ? WHERE ID = ?";
            List<Object> payTableParams = new ArrayList<>();
            payTableParams.add(totalPayAmount); // 设置总支付金额
            payTableParams.add(commissionPayId.trim()); // 按 commissionPayId 定位记录
            int payTableAffectedRows = DBSql.update(connUpdate, updatePayTableSql, payTableParams.toArray());
            System.out.println("更新 BO_EU_DNCTT_COMMISSION_PAY 表影响行数：" + payTableAffectedRows);

            // 6. 提交事务（所有更新在同一事务中提交）
            connUpdate.commit();

            // 7. 返回结果
            Map<String, Object> result = new HashMap<>();
            result.put("totalMatched", idArr.length);
            result.put("updateCount", updateCount);
            result.put("totalPayAmount", totalPayAmount);
            result.put("totalPeriodAmount", totalPeriodAmount);
            result.put("payTableAffectedRows", payTableAffectedRows); // 新增：返回目标表更新行数
            result.put("message", "按比例分配及目标表更新完成，共更新" + updateCount + "条周期记录，目标表更新" + payTableAffectedRows + "条记录");

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (SQLException e) {
            // 异常回滚（所有更新一同回滚）
            if (connUpdate != null && !connUpdate.isClosed()) {
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
            return ResponseObject.newErrResponse("更新失败：" + e.getMessage());
        } finally {
            // 关闭连接
            if (connUpdate != null && !connUpdate.isClosed()) {
                DBSql.close(connUpdate);
            }
        }
    }

    @Mapping("com.awspaas.user.apps.donenow_ctt.updateInvoiceReceiveAmountByBindId")
    public ResponseObject updateInvoiceReceiveAmountByBindId(UserContext uc,
                                                             String bindid, // 前端传递：用于定位明细记录的bindid
                                                             BigDecimal totalReceiveAmount // 前端传递：总收款金额（用于按比例分配）
    ) throws SQLException {
        Connection connUpdate = null;
        try {
            // 1. 参数校验
            if (StringUtils.isBlank(bindid)) {
                return ResponseObject.newErrResponse("bindid不能为空");
            }
            if (totalReceiveAmount == null || totalReceiveAmount.compareTo(BigDecimal.ZERO) <= 0) {
                return ResponseObject.newErrResponse("总收款金额不能为空且需大于0");
            }
            // 强制总收款金额保留2位小数，符合财务金额规范，杜绝前端传入多位小数
            totalReceiveAmount = totalReceiveAmount.setScale(2, BigDecimal.ROUND_HALF_UP);

            // 2. 查询bindid关联的明细记录（核心：查询字段为INVOICE_AMOUNT）
            String querySql = "SELECT ID, INVOICE_AMOUNT FROM BO_EU_DNCTT_INVOICE_PLAN_DETAIL WHERE BINDID = ?";
            List<Object> queryParams = new ArrayList<>();
            queryParams.add(bindid.trim());
            List<RowMap> detailList = DBSql.getMaps(querySql, queryParams.toArray());

            // 3. 预处理：统计总发票金额 + 校验数据（变量名同步改为发票金额相关）
            Map<String, BigDecimal> idInvoiceAmountMap = new HashMap<>(); // 明细ID-发票金额映射
            BigDecimal totalInvoiceAmount = BigDecimal.ZERO; // 总发票金额（比例计算基数）
            for (RowMap row : detailList) {
                String id = row.getString("ID");
                // 处理发票金额：从INVOICE_AMOUNT字段获取
                String invoiceAmountStr = row.getString("INVOICE_AMOUNT");
                BigDecimal invoiceAmount = null;
                if (invoiceAmountStr != null && !invoiceAmountStr.trim().isEmpty()) {
                    try {
                        invoiceAmount = new BigDecimal(invoiceAmountStr.trim());
                        // 强制发票金额保留2位小数，杜绝多位小数导致的比例偏差、单条固定少0.02
                        invoiceAmount = invoiceAmount.setScale(2, BigDecimal.ROUND_HALF_UP);
                    } catch (NumberFormatException e) {
                        System.out.println("发票金额格式错误，ID：" + id + "，值：" + invoiceAmountStr);
                        continue;
                    }
                }
                if (invoiceAmount == null || invoiceAmount.compareTo(BigDecimal.ZERO) <= 0) {
                    System.out.println("发票金额非法（空或<=0），ID：" + id);
                    continue;
                }
                idInvoiceAmountMap.put(id, invoiceAmount);
                totalInvoiceAmount = totalInvoiceAmount.add(invoiceAmount);
            }

            // 校验总发票金额（避免除零，提示信息同步修改）
            if (totalInvoiceAmount.compareTo(BigDecimal.ZERO) <= 0) {
                Map<String, Object> result = new HashMap<>();
                result.put("totalMatched", detailList.size());
                result.put("updateCount", 0);
                result.put("message", "bindid关联明细的总发票金额为0，无法计算比例");
                ResponseObject responseObject = ResponseObject.newOkResponse();
                responseObject.setData(result);
                return responseObject;
            }

            // 4. 初始化事务连接
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);

            int updateCount = 0;
            // 转换为有序列表，保证最后一条兜底逻辑生效
            List<String> detailIdList = new ArrayList<>(idInvoiceAmountMap.keySet());
            int detailSize = detailIdList.size();
            BigDecimal sumAllocatedAmount = BigDecimal.ZERO; // 前N-1条累计分配金额

            // 5. 循环按比例分配并更新 RECEIVE_AMOUNT 字段（核心修复：最后一条兜底，彻底解决金额偏差）
            for (int i = 0; i < detailSize; i++) {
                String id = detailIdList.get(i);
                BigDecimal invoiceAmount = idInvoiceAmountMap.get(id);
                BigDecimal allocatedReceiveAmount;

                if (i == detailSize - 1) {
                    // ========== 核心：最后1条兜底 ==========
                    // 用总收款金额 - 前N-1条累计金额，补足所有差额，总额100%精准无损耗
                    allocatedReceiveAmount = totalReceiveAmount.subtract(sumAllocatedAmount);
                } else {
                    // 前N-1条：比例精度拉满至12位，最小化单条偏差（从0.02→0.01）
                    BigDecimal ratio = invoiceAmount.divide(totalInvoiceAmount, 12, BigDecimal.ROUND_HALF_UP);
                    allocatedReceiveAmount = totalReceiveAmount.multiply(ratio).setScale(2, BigDecimal.ROUND_HALF_UP);
                    sumAllocatedAmount = sumAllocatedAmount.add(allocatedReceiveAmount);
                }

                // 构建更新SQL（无拼接冗余逗号，彻底解决SQL语法错误）
                String updateSql = "UPDATE BO_EU_DNCTT_INVOICE_PLAN_DETAIL SET RECEIVE_AMOUNT = ? WHERE ID = ?";
                List<Object> updateParams = new ArrayList<>();
                updateParams.add(allocatedReceiveAmount);
                updateParams.add(id.trim());

                // 执行更新
                int affectedRows = DBSql.update(connUpdate, updateSql, updateParams.toArray());
                if (affectedRows > 0) {
                    updateCount++;
                }
            }

            // 6. 提交事务
            connUpdate.commit();

            // 7. 返回结果（字段同步改为总发票金额，返回精准分配数据）
            Map<String, Object> result = new HashMap<>();
            result.put("totalMatched", detailList.size());
            result.put("updateCount", updateCount);
            result.put("totalReceiveAmount", totalReceiveAmount);
            result.put("totalInvoiceAmount", totalInvoiceAmount);
            result.put("actualAllocatedTotal", totalReceiveAmount); // 实际分配总额=总收款，绝对一致
            result.put("message", "按比例更新RECEIVE_AMOUNT完成，共更新" + updateCount + "条明细记录，金额无差额");

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (SQLException e) {
            // 事务回滚：增强容错，确保异常时数据回滚
            if (connUpdate != null && !connUpdate.isClosed()) {
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
            return ResponseObject.newErrResponse("更新RECEIVE_AMOUNT失败：" + e.getMessage());
        } finally {
            // 关闭连接：安全兜底，避免连接泄漏
            if (connUpdate != null && !connUpdate.isClosed()) {
                DBSql.close(connUpdate);
            }
        }
    }

    /**
     * 佣金审批
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     */
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.approve_commission")
    public ResponseObject approveCommission(UserContext uc, String approveDate, String approveIds) throws SQLException {
        if (StringUtils.isBlank(approveDate) || StringUtils.isBlank(approveIds))
            return ResponseObject.newErrResponse("参数错误");
        contractService.getInstance().approveCommission(uc, approveDate, approveIds);
        return ResponseObject.newOkResponse();
    }


    /**
     * 佣金调整
     * @param uc
     * @return
     */
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.approve_updateCommissionPrice")
    public ResponseObject updateCommissionPrice(UserContext uc, String id, String periodPrice) {
/*
        BO bo = SDK.getBOAPI().get("BO_EU_DNCTT_COMMISSION_PERIOD", id);//佣金

        String APPROVE_AND_POST_DATE = bo.getString("APPROVE_AND_POST_DATE");
        System.out.println("APPROVE_AND_POST_DATE------------" + APPROVE_AND_POST_DATE);
        if (StringUtils.isNotBlank(APPROVE_AND_POST_DATE))
            return ResponseObject.newErrResponse("已审批不允许修改");

        bo.set("PERIOD_ADJUSTED_PRICE", periodPrice);
        SDK.getBOAPI().update("BO_EU_DNCTT_COMMISSION_PERIOD", bo);
*/
        RowMap period = DBSql.getMap("select * from BO_EU_DNCTT_COMMISSION_PERIOD where ID=?", new Object[]{id});
        String APPROVE_AND_POST_DATE = period.getString("APPROVE_AND_POST_DATE");
        System.out.println("APPROVE_AND_POST_DATE------------" + APPROVE_AND_POST_DATE);
        if (StringUtils.isNotBlank(APPROVE_AND_POST_DATE))
            return ResponseObject.newErrResponse("已审批不允许修改");

        BigDecimal rate = divideToBigDecimal(periodPrice, period.get("PERIOD_ADJUSTED_PRICE"));

        String COMMISSION_BINDID = period.getString("BINDID");

        String PERIOD_BEGIN_DATE = period.getDate("PERIOD_BEGIN_DATE").toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));

        List<BO> commissionSalesBOs = SDK.getBOAPI().query("BO_EU_DNCTT_COMMISSION_PERIOD_SALES").addQuery("BINDID =", COMMISSION_BINDID).addQuery("PERIOD_BEGIN_DATE =", PERIOD_BEGIN_DATE).list();
        for (BO commissionSalesBO : commissionSalesBOs) {
            commissionSalesBO.set("PERIOD_ADJUSTED_PRICE", multiply(commissionSalesBO.get("PERIOD_ADJUSTED_PRICE"), rate));
            SDK.getBOAPI().update("BO_EU_DNCTT_COMMISSION_PERIOD_SALES", commissionSalesBO);
        }

        List<BO> commissionAGENTBOs = SDK.getBOAPI().query("BO_EU_DNCTT_COMMISSION_PERIOD_AGENT").addQuery("BINDID =", COMMISSION_BINDID).addQuery("PERIOD_BEGIN_DATE =", PERIOD_BEGIN_DATE).list();
        for (BO commissionAGENTBO : commissionAGENTBOs) {
            commissionAGENTBO.set("PERIOD_ADJUSTED_PRICE", multiply(commissionAGENTBO.get("PERIOD_ADJUSTED_PRICE"), rate));
            SDK.getBOAPI().update("BO_EU_DNCTT_COMMISSION_PERIOD_AGENT", commissionAGENTBO);
        }

        DBSql.update("update BO_EU_DNCTT_COMMISSION_PERIOD set PERIOD_ADJUSTED_PRICE=? where ID=?", new Object[]{periodPrice, id});

        return ResponseObject.newOkResponse();
    }


    /**
     * 佣金调整，恢复初始价格
     * @param uc
     * @return
     */
    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.approve_initCommissionPrice")
    public ResponseObject initCommissionPrice(UserContext uc, String ids) {

        for (String id : ids.split(",")) {
            /*
            BO bo = SDK.getBOAPI().get("BO_EU_DNCTT_COMMISSION_PERIOD", id);//佣金

            String APPROVE_AND_POST_DATE = bo.getString("APPROVE_AND_POST_DATE");
            System.out.println("APPROVE_AND_POST_DATE------------" + APPROVE_AND_POST_DATE);
            if (StringUtils.isNotBlank(APPROVE_AND_POST_DATE))
                return ResponseObject.newErrResponse("已审批不允许修改");

            bo.set("PERIOD_ADJUSTED_PRICE", bo.get("COMMISSION_DOLLARS"));
            SDK.getBOAPI().update("BO_EU_DNCTT_COMMISSION_PERIOD", bo);

             */

            RowMap period = DBSql.getMap("select * from BO_EU_DNCTT_COMMISSION_PERIOD where ID=?", new Object[]{id});
            String APPROVE_AND_POST_DATE = period.getString("APPROVE_AND_POST_DATE");
            System.out.println("APPROVE_AND_POST_DATE------------" + APPROVE_AND_POST_DATE);
            if (StringUtils.isNotBlank(APPROVE_AND_POST_DATE))
                return ResponseObject.newErrResponse("已审批不允许修改");

            DBSql.update("update BO_EU_DNCTT_COMMISSION_PERIOD set PERIOD_ADJUSTED_PRICE=COMMISSION_DOLLARS where ID=?", new Object[]{id});

        }

        return ResponseObject.newOkResponse();
    }


    /**
     * 撤销佣金审批
     * @param ids
     * @return
     */
    @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_commission")
    public ResponseObject revokeCommission(UserContext uc, String ids) throws SQLException {
        String result = contractService.getInstance().revokeCommission(uc, ids);
        if (StringUtils.isNotBlank(result))
            return ResponseObject.newErrResponse(result);
        return ResponseObject.newOkResponse();
    }

    @Mapping("com.awspaas.user.apps.donenow_ctt.matchBillByHeadquartersFeePeriod2")
    public ResponseObject matchBillByHeadquartersFeePeriod2(UserContext uc, String headquartersFeePeriod) throws SQLException {
        Connection connUpdate = null;
        try {
            // 参数校验（不变）
            if (StringUtils.isBlank(headquartersFeePeriod)) {
                return ResponseObject.newErrResponse("总部费用账期不能为空");
            }
            String feePeriod = headquartersFeePeriod.trim();

            // 1. 查询需要更新的数据：新增查询a表ID（a.ID AS a_id）
            String querySql = "SELECT " +
                    "a.ID AS a_id, " +
                    "a.COMMISSION_DOLLARS AS 佣金, " +
                    "a.TOTAL_DOLLARS AS 基数, " +
                    "a.RATIO AS 比例, " +
                    "a.ZB_BILLING_DATE AS 账单日期, " +
                    "c.ID AS c_id  " +
                    "FROM BO_EU_DNCTT_COMMISSION_IMPORT a " +
                    "JOIN BO_EU_DNCTT_COMMISSION b " +
                    "ON a.ACCOUNT_USER_NO = b.NEW_ACCOUNT AND a.DEVICE_ID = b.DEVICE_ID " +
                    "JOIN BO_EU_DNCTT_COMMISSION_PERIOD c " +
                    "ON a.ZB_FEE_DATE = c.ZB_FEE_DATE AND b.bindid = c.bindid " +
                    "WHERE a.ZB_FEE_DATE = ?";

            List<RowMap> matchRecords = DBSql.getMaps(querySql, new Object[]{feePeriod});

            if (matchRecords == null || matchRecords.isEmpty()) {
                Map<String, Object> result = new HashMap<>();
                result.put("totalMatched", 0);
                result.put("message", "未找到对应账期的记录");
                ResponseObject responseObject = ResponseObject.newOkResponse();
                responseObject.setData(result);
                return responseObject;
            }

            // 2. 初始化连接 + 事务控制（不变）
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);

            int updateCount = 0;

            // 3. 循环处理每条记录
            for (RowMap record : matchRecords) {
                // 键值处理（不变）
                for (String key : record.keySet()) {
                    record.put(key.toUpperCase(), record.get(key) == null ? "" : record.get(key));
                }

                // 【修改2：提取a表ID（a_id），用于更新到c表COMMISSION_IMPORT_ID】
                String aId = record.getString("a_id"); // 从查询结果中获取a表ID

                // 提取c表ID（不变）
                String cId = record.getString("c_id");
                if (StringUtils.isBlank(cId)) {
                    System.out.println("跳过无c表ID的记录：" + record);
                    continue;
                }

                // 提取其他字段（不变）
                Double commission = record.getDouble("佣金");
                Double total = record.getDouble("基数");
                String ratioStr = record.getString("比例");
                String zbBillingDate = record.getString("账单日期");
                if (commission != null) {
                    BigDecimal commissionBd = new BigDecimal(commission);
                    BigDecimal resultBd = commissionBd.multiply(new BigDecimal("1.06"));
                    resultBd = resultBd.setScale(2, BigDecimal.ROUND_HALF_UP);
                    commission = resultBd.doubleValue();
                }

                // 处理比例字段（不变）
                Double ratio = 0.0;
                if (StringUtils.isNotBlank(ratioStr)) {
                    String pureRatio = ratioStr.replace("%", "").trim();
                    if ((StringUtils.isNumeric(pureRatio) || pureRatio.contains("."))) {
                        try {
                            ratio = Double.parseDouble(pureRatio);
                        } catch (NumberFormatException e) {
                            System.out.println("比例格式错误（使用默认值0.0）：" + ratioStr);
                        }
                    } else {
                        System.out.println("比例非数字（使用默认值0.0）：" + ratioStr);
                    }
                }

                // 4. 执行更新：新增COMMISSION_IMPORT_ID字段的更新
                DBSql.update(connUpdate,
                        "UPDATE BO_EU_DNCTT_COMMISSION_PERIOD " +
                                "SET COMMISSION_DOLLARS_IMPORT = ?, " +
                                "TOTAL_DOLLARS_IMPORT = ?, " +
                                "RATIO_IMPORT = ?, " +
                                "ZB_BILLING_DATE = ?, " +
                                "COMMISSION_IMPORT_ID = ? " + // 【修改3：新增字段，存a表ID】
                                "WHERE ID = ?",
                        new Object[]{
                                commission,   // 1. 佣金
                                total,        // 2. 基数
                                ratio,        // 3. 处理后的比例
                                zbBillingDate,// 4. 账单日期
                                aId,          // 5. 【修改4：新增参数，传入a表ID】
                                cId           // 6. 条件：c表ID
                        }
                );

                updateCount++;
            }

            // 5. 提交事务（不变）
            connUpdate.commit();

            // 6. 响应结果（不变）
            Map<String, Object> result = new HashMap<>();
            result.put("totalMatched", matchRecords.size());
            result.put("updateCount", updateCount);
            result.put("message", "已按 revoke_commission 风格完成更新");

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (SQLException e) {
            // 异常回滚（不变）
            if (connUpdate != null && !connUpdate.isClosed()) {
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
            return ResponseObject.newErrResponse("处理失败：" + e.getMessage());
        } finally {
            // 关闭连接（不变）
            if (connUpdate != null && !connUpdate.isClosed()) {
                DBSql.close(connUpdate);
            }
        }
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.getOrderProductContractInfo")
    public ResponseObject getOrderProductContractInfo(UserContext uc, String id) {
        try {
            String querySql = "SELECT CONTRACT_SERVICE_ID, CONTRACT_ID " +
                    "FROM BO_EU_DNIVT_ORDER_PRODUCT " +
                    "WHERE id = ?";

            Map<String, Object> orderProductInfo = DBSql.getMap(querySql, new Object[]{id});

            Map<String, Object> result = new HashMap<>();
            if (orderProductInfo != null) {
                result.put("contractServiceId", orderProductInfo.get("CONTRACT_SERVICE_ID"));
                result.put("contractId", orderProductInfo.get("CONTRACT_ID"));
                result.put("exists", true); // 标记数据是否存在
            } else {
                result.put("exists", false);
                result.put("contractServiceId", null);
                result.put("contractId", null);
            }

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("查询订单产品合同信息失败：" + e.getMessage());
        }
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ivt.insertPayRequestDetail")
    public ResponseObject insertPayRequestDetail(UserContext uc, String paymentPlanId) {
        try {
            String[] planIds = paymentPlanId.split(",");
            if (planIds.length == 0) {
                return ResponseObject.newErrResponse("支付计划ID不能为空");
            }

            // -------------------------- 关键修正 --------------------------
            // 1. 首先检查所有待创建的支付计划ID是否已存在于明细表中
            List<String> existingPlanIds = new ArrayList<>();
            for (String pid : planIds) {
                String checkSql = "SELECT PAYMENT_PLAN_ID FROM BO_EU_DNIVT_PAY_REQUEST_DETAIL WHERE PAYMENT_PLAN_ID = ?";
                Map<String, Object> existingDetail = DBSql.getMap(checkSql, new Object[]{pid});
                if (existingDetail != null) {
                    existingPlanIds.add(pid);
                }
            }

            // 如果有任何一个ID已存在，则不创建任何记录，并返回错误信息
            if (!existingPlanIds.isEmpty()) {
                return ResponseObject.newErrResponse("支付计划ID " + String.join(", ", existingPlanIds) + " 已存在于支付请求明细中，无法重复创建。");
            }
            // -------------------------- 关键修正结束 --------------------------

            // 2. 计算所有选中支付计划的总金额
            BigDecimal requestAmountTotal = BigDecimal.ZERO;
            Map<String, Map<String, Object>> allPlanData = new HashMap<>();

            for (String pid : planIds) {
                String querySql = "SELECT " +
                        "PROJECT_LOCATION, PERIOD_BEGIN_DATE, PERIOD_END_DATE, LOCATION, " +
                        "PAY_DESC, PLAN_AMOUNT, PURCHASE_ORDER_NO, REFERENCE_NUMBER, PLAN_DATE, PAY_AMOUNT, REMARK, " +
                        "PLAN_NO, ORDER_ID, CONTRACT_SERVICE_ID, CONTRACT_COST_ID " +
                        "FROM VIEW_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE ID = ?";
                Map<String, Object> planData = DBSql.getMap(querySql, new Object[]{pid});

                if (planData == null) {
                    return ResponseObject.newErrResponse("支付计划ID " + pid + " 不存在或已被删除。");
                }

                allPlanData.put(pid, planData); // 缓存查询结果，pid作为键

                // 累加总金额
                Object amountObj = planData.get("PLAN_AMOUNT");
                if (amountObj != null) {
                    BigDecimal amount;
                    if (amountObj instanceof BigDecimal) {
                        amount = (BigDecimal) amountObj;
                    } else if (amountObj instanceof Number) {
                        amount = BigDecimal.valueOf(((Number) amountObj).doubleValue());
                    } else {
                        continue;
                    }
                    requestAmountTotal = requestAmountTotal.add(amount);
                }
            }

            // =========================================================================
            // 新增：计算中文大写金额
            // =========================================================================
            String requestAmountCn = convertNumberToChinese(requestAmountTotal);

            // 3. 创建支付请求单主表（Process Instance + BO）
            String mainPayRequestId = planIds[0];
            ProcessInstance processInstance = SDK.getProcessAPI().createProcessInstance(
                    "obj_8cf0923593b7465490c4e1f73e98d4d3",
                    uc.getUID(),
                    "支付请求单-" + mainPayRequestId
            );

            BO mainPayRequestBO = new BO();
            mainPayRequestBO.setBindId(processInstance.getId());
            mainPayRequestBO.set("REQUEST_AMOUNT", requestAmountTotal);
            // =========================================================================
            // 新增：为主表BO设置中文大写金额字段
            // =========================================================================
            mainPayRequestBO.set("REQUEST_AMOUNT_CN", requestAmountCn);

            SDK.getBOAPI().create("BO_EU_DNIVT_PAY_REQUEST", mainPayRequestBO, processInstance, uc);
            SDK.getProcessAPI().start(processInstance);

            String mainRecordId = mainPayRequestBO.getId();
            String mainBindId = mainPayRequestBO.getBindId();
            List<String> detailIds = new ArrayList<>();

            for (String pid : planIds) {
                Map<String, Object> planData = allPlanData.get(pid);

                BO detailBO = new BO();
                detailBO.setBindId(mainBindId);

                detailBO.set("PAYMENT_PLAN_ID", pid);

                detailBO.set("PROJECT_LOCATION", planData.get("PROJECT_LOCATION"));
                detailBO.set("PERIOD_BEGIN_DATE", planData.get("PERIOD_BEGIN_DATE"));
                detailBO.set("PERIOD_END_DATE", planData.get("PERIOD_END_DATE"));
                detailBO.set("LOCATION", planData.get("LOCATION"));
                detailBO.set("PAY_DESC", planData.get("PAY_DESC"));
                detailBO.set("PLAN_AMOUNT", planData.get("PLAN_AMOUNT"));
                detailBO.set("PLAN_DATE", planData.get("PLAN_DATE"));
                detailBO.set("AMOUNT", planData.get("PLAN_AMOUNT"));
                detailBO.set("REMARK", planData.get("REMARK"));
                detailBO.set("PLAN_NO", planData.get("PLAN_NO"));
                detailBO.set("ORDER_ID", planData.get("ORDER_ID"));
                detailBO.set("CONTRACT_SERVICE_ID", planData.get("CONTRACT_SERVICE_ID"));
                detailBO.set("CONTRACT_COST_ID", planData.get("CONTRACT_COST_ID"));
                detailBO.set("ORDER_NO", planData.get("PURCHASE_ORDER_NO"));
                detailBO.set("EXTTEXT3", planData.get("REFERENCE_NUMBER"));

                SDK.getBOAPI().createDataBO("BO_EU_DNIVT_PAY_REQUEST_DETAIL", detailBO, uc);
                detailIds.add(detailBO.getId());
            }

            // 5. 组织并返回成功结果
            Map<String, Object> result = new HashMap<>();
            result.put("isCreated", true);
            result.put("mainRecordId", mainRecordId);
            result.put("mainBindId", mainBindId);
            result.put("detailRecordIds", detailIds);
            result.put("requestAmountTotal", requestAmountTotal);
            result.put("message", String.format("成功创建支付请求单及 %d 条明细记录，请求总金额：%s", detailIds.size(), requestAmountTotal));

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (Exception e) {
            e.printStackTrace(); // 在生产环境中，建议使用日志框架记录异常
            return ResponseObject.newErrResponse("创建支付请求单及明细失败：" + e.getMessage());
        }
    }

    /**
     * 将 BigDecimal 金额转换为中文大写金额字符串
     * @param amount 金额
     * @return 中文大写金额
     */
    private static String convertNumberToChinese(BigDecimal amount) {
        if (amount == null) {
            throw new IllegalArgumentException("输入金额不能为null");
        }
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new IllegalArgumentException("金额不能为负数");
        }
        if (amount.compareTo(new BigDecimal("999999999999.99")) > 0) {
            throw new IllegalArgumentException("金额超出范围（最大支持到 999,999,999,999.99）");
        }

        // 处理整数和小数部分
        BigDecimal integerPart = amount.setScale(0, BigDecimal.ROUND_DOWN);
        BigDecimal decimalPart = amount.subtract(integerPart).multiply(new BigDecimal("100")).setScale(0, BigDecimal.ROUND_HALF_UP);

        final String[] chineseDigits = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
        final String[] chineseUnits = {"", "拾", "佰", "仟"};
        final String[] chineseSections = {"", "万", "亿"};

        String integerStr = convertInteger(integerPart, chineseDigits, chineseUnits, chineseSections);
        String decimalStr = convertDecimal(decimalPart, chineseDigits);

        if (decimalStr.isEmpty()) {
            decimalStr = "整";
        }

        return integerStr + (integerStr.equals("零") ? "" : "元") + decimalStr;
    }

    /**
     * 转换整数部分
     */
    private static String convertInteger(BigDecimal num, String[] digits, String[] units, String[] sections) {
        if (num.compareTo(BigDecimal.ZERO) == 0) return "零";

        StringBuilder result = new StringBuilder();
        int sectionIndex = 0;

        while (num.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal section = num.remainder(new BigDecimal("10000"));
            if (section.compareTo(BigDecimal.ZERO) != 0) {
                String sectionStr = convertSection(section, digits, units);
                if (sectionIndex > 0) {
                    sectionStr += sections[sectionIndex];
                }
                result.insert(0, sectionStr);
            } else if (result.length() > 0 && !result.toString().startsWith("零")) {
                result.insert(0, "零");
            }
            num = num.divide(new BigDecimal("10000"), 0, BigDecimal.ROUND_DOWN);
            sectionIndex++;
        }

        String res = result.toString().replaceAll("^零+|零+$", "").replaceAll("零{2,}", "零");
        return res.isEmpty() ? "零" : res;
    }

    /**
     * 转换单个四位数
     */
    private static String convertSection(BigDecimal section, String[] digits, String[] units) {
        StringBuilder result = new StringBuilder();
        boolean zeroNeeded = false;
        String numStr = String.format("%04d", section.intValue()); // 格式化为4位字符串，不足补0

        for (int i = 0; i < 4; i++) {
            int digit = Character.getNumericValue(numStr.charAt(i));
            int power = 3 - i; // 仟、佰、拾、个
            if (digit != 0) {
                if (zeroNeeded) {
                    result.append(digits[0]);
                    zeroNeeded = false;
                }
                result.append(digits[digit]).append(units[power]);
            } else {
                if (result.length() > 0 && power < 3) {
                    zeroNeeded = true;
                }
            }
        }
        return result.toString();
    }

    /**
     * 转换小数部分
     */
    private static String convertDecimal(BigDecimal dec, String[] digits) {
        if (dec.compareTo(BigDecimal.ZERO) == 0) return "";
        int jiao = dec.divide(new BigDecimal("10"), 0, BigDecimal.ROUND_DOWN).intValue();
        int fen = dec.remainder(new BigDecimal("10")).intValue();
        StringBuilder result = new StringBuilder();
        if (jiao > 0) {
            result.append(digits[jiao]).append("角");
        }
        if (fen > 0) {
            result.append(digits[fen]).append("分");
        }
        return result.toString();
    }

    @Mapping("com.awspaas.user.apps.donenow_ctt.getAllPaymentAmounts")
    public ResponseObject getAllPaymentAmounts(UserContext uc) throws SQLException {
        Connection conn = null;
        try {
            // 1. 执行查询（获取所有付款计划数据）
            String querySql = "SELECT " +
                    "PLAN_AMOUNT AS 计划付款金额, " +
                    "PAY_AMOUNT AS 实际付款金额 " +
                    "FROM VIEW_EU_DNV_PAYMENT_PLAN";

            List<RowMap> paymentRecords = DBSql.getMaps(querySql, new Object[]{});
            System.out.println("查询到付款记录数量：" + (paymentRecords != null ? paymentRecords.size() : 0));

            // 2. 处理空结果
            if (paymentRecords == null || paymentRecords.isEmpty()) {
                Map<String, Object> result = new HashMap<>();
                result.put("totalCount", 0);
                result.put("message", "未找到任何付款计划数据");
                ResponseObject responseObject = ResponseObject.newOkResponse();
                responseObject.setData(result);
                return responseObject;
            }

            // 3. 初始化连接（保持事务控制风格，此处仅查询用）
            conn = DBSql.open();
            conn.setAutoCommit(false);  // 保持事务控制代码结构

            // 4. 循环计算总额
            BigDecimal totalPlan = BigDecimal.ZERO;
            BigDecimal totalPay = BigDecimal.ZERO;

            for (RowMap record : paymentRecords) {
                // 统一处理键值（转为大写）
                for (String key : record.keySet()) {
                    record.put(key.toUpperCase(), record.get(key) == null ? "" : record.get(key));
                }

                // 提取计划付款金额（处理空值和类型转换）
                Double planDouble = record.getDouble("计划付款金额");
                BigDecimal planAmount = planDouble != null
                        ? BigDecimal.valueOf(planDouble)
                        : BigDecimal.ZERO;

                // 提取实际付款金额（处理空值和类型转换）
                Double payDouble = record.getDouble("实际付款金额");
                BigDecimal payAmount = payDouble != null
                        ? BigDecimal.valueOf(payDouble)
                        : BigDecimal.ZERO;

                // 累加总额
                totalPlan = totalPlan.add(planAmount);
                totalPay = totalPay.add(payAmount);
                System.out.println("累计计划付款：" + totalPlan + "，累计实际付款：" + totalPay);
            }

            // 5. 计算待付款金额（确保非负）
            BigDecimal totalPending = totalPlan.subtract(totalPay);
            if (totalPending.compareTo(BigDecimal.ZERO) < 0) {
                totalPending = BigDecimal.ZERO;
            }

            // 6. 提交事务（保持代码结构完整性）
            conn.commit();

            // 7. 组装响应结果
            Map<String, Object> result = new HashMap<>();
            result.put("totalPlanAmount", totalPlan);
            result.put("totalPayAmount", totalPay);
            result.put("totalPendingAmount", totalPending);
            result.put("totalCount", paymentRecords.size());
            result.put("message", "付款金额汇总完成");

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (SQLException e) {
            // 异常回滚处理
            if (conn != null && !conn.isClosed()) {
                try {
                    conn.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            }
            e.printStackTrace();
            return ResponseObject.newErrResponse("处理失败：" + e.getMessage());
        } finally {
            // 关闭连接
            if (conn != null && !conn.isClosed()) {
                DBSql.close(conn);
            }
        }
    }


    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.updateAttachmentUrlByPhoneLast4")
    public ResponseObject updateAttachmentUrlByPhoneLast4(UserContext uc, String certificateContent, String attachmentUrl, String feeDate) {
        Map<String, Object> result = new HashMap<>();
        try {
            // 1. 合并参数校验（仅1个提前返回）
            String errorMsg = "";
            if (certificateContent == null || certificateContent.trim().isEmpty()) {
                errorMsg = "凭证内容不能为空";
            } else if (attachmentUrl == null || attachmentUrl.trim().isEmpty()) {
                errorMsg = "图片URL不能为空";
            } else if (feeDate == null || feeDate.trim().isEmpty()) {
                errorMsg = "账期不能为空";
            }
            if (!errorMsg.isEmpty()) {
                result.put("result", "ok");
                result.put("message", errorMsg);
                result.put("proxyPayUpdatedCount", 0);
                result.put("periodUpdatedCount", 0);
                ResponseObject responseObject = ResponseObject.newOkResponse();
                responseObject.setData(result);
                return responseObject;
            }

            // ========== 仅新增：账期转换为周期起止日期（用于更新条件） ==========
            String periodBeginDate = ""; // 账期年月的当月1日（如2025-03 → 2025-03-01）
            String periodEndDate = "";   // 账期年月的当月最后1日（如2025-03 → 2025-03-31）
            try {
                // 兼容账期格式：2025-03 或 202503
                SimpleDateFormat sdfInput = feeDate.contains("-") ?
                        new SimpleDateFormat("yyyy-MM") : new SimpleDateFormat("yyyyMM");
                Date feeDateObj = sdfInput.parse(feeDate);
                Calendar cal = Calendar.getInstance();
                cal.setTime(feeDateObj);

                // 当月1日
                cal.set(Calendar.DAY_OF_MONTH, 1);
                periodBeginDate = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime());

                // 当月最后1日
                cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
                periodEndDate = new SimpleDateFormat("yyyy-MM-dd").format(cal.getTime());
            } catch (ParseException e) {
                result.put("result", "ok");
                result.put("message", "账期格式错误（需为yyyy-MM或yyyyMM，如2025-03）");
                result.put("proxyPayUpdatedCount", 0);
                result.put("periodUpdatedCount", 0);
                ResponseObject responseObject = ResponseObject.newOkResponse();
                responseObject.setData(result);
                return responseObject;
            }

            // 2. 提取fileName
            String fileName = "";
            Pattern fileNamePattern = Pattern.compile("fileName=([^&]+)");
            Matcher fileNameMatcher = fileNamePattern.matcher(attachmentUrl);
            if (fileNameMatcher.find()) {
                fileName = fileNameMatcher.group(1);
                try { fileName = URLDecoder.decode(fileName, "UTF-8"); } catch (Exception e) {e.printStackTrace();}
            }

            // 3. 提取手机号后四位
            Pattern phonePattern = Pattern.compile("\\d{3}\\*{4}(\\d{4})");
            Matcher phoneMatcher = phonePattern.matcher(certificateContent);
            Set<String> lastFourSet = new HashSet<>();
            while (phoneMatcher.find()) { lastFourSet.add(phoneMatcher.group(1)); }
            List<String> lastFourList = new ArrayList<>(lastFourSet);

            // 4. 更新PROXY_PAY表（原有逻辑，已匹配FEE_DATE）
            int proxyPayUpdatedCount = 0;
            if (!lastFourList.isEmpty() && !fileName.isEmpty()) {
                StringBuilder updateProxyPaySql = new StringBuilder("UPDATE BO_EU_DNCTT_PROXY_PAY SET ATTACHMENT_URL = ?, PAY_PROOF = ?, ATTACHMENT = ? WHERE RIGHT(PHONE, 4) IN (");
                for (int i = 0; i < lastFourList.size(); i++) { updateProxyPaySql.append(i == 0 ? "?" : ",?"); }
                updateProxyPaySql.append(") AND FEE_DATE = ?");

                Object[] proxyPayParams = new Object[lastFourList.size() + 4];
                proxyPayParams[0] = attachmentUrl;
                proxyPayParams[1] = certificateContent;
                proxyPayParams[2] = fileName;
                for (int i = 0; i < lastFourList.size(); i++) { proxyPayParams[i + 3] = lastFourList.get(i); }
                proxyPayParams[lastFourList.size() + 3] = feeDate;
                proxyPayUpdatedCount = DBSql.update(updateProxyPaySql.toString(), proxyPayParams);
            }

            // 5. 计算总金额（查询时返回账期+匹配账期，原有逻辑）
            Map<String, BigDecimal> phoneAmountMap = new HashMap<>();
            Map<String, String> phoneFeeDateMap = new HashMap<>();
            if (!lastFourList.isEmpty()) {
                StringBuilder queryAmountSql = new StringBuilder("SELECT " +
                        "RIGHT(PHONE,4) AS PHONE_LAST4, " +
                        "FEE_DATE, " +
                        "IFNULL(EXTENDED_PRICE,0)+IFNULL(TAX_DOLLARS,0) AS TOTAL_AMOUNT " +
                        "FROM BO_EU_DNCTT_PROXY_PAY " +
                        "WHERE RIGHT(PHONE,4) IN (");
                for (int i = 0; i < lastFourList.size(); i++) { queryAmountSql.append(i == 0 ? "?" : ",?"); }
                queryAmountSql.append(") AND FEE_DATE = ?");

                Object[] amountParams = new Object[lastFourList.size() + 1];
                for (int i = 0; i < lastFourList.size(); i++) { amountParams[i] = lastFourList.get(i); }
                amountParams[lastFourList.size()] = feeDate;

                List<RowMap> amountList = DBSql.getMaps(queryAmountSql.toString(), amountParams);
                for (RowMap row : amountList) {
                    String phoneLast4 = row.getString("PHONE_LAST4");
                    String feeDateFromDb = row.getString("FEE_DATE");
                    Object totalAmountObj = row.get("TOTAL_AMOUNT");
                    BigDecimal totalAmount = BigDecimal.ZERO;
                    if (totalAmountObj != null) {
                        totalAmount = new BigDecimal(totalAmountObj.toString());
                    }
                    phoneAmountMap.put(phoneLast4, totalAmount);
                    phoneFeeDateMap.put(phoneLast4, feeDateFromDb);
                }
            }

            // 6. 匹配合同服务ID + 更新周期价格（仅新增周期日期条件）
            int periodUpdatedCount = 0;
            if (!phoneAmountMap.isEmpty() && !lastFourList.isEmpty()) {
                List<RowMap> contractList = DBSql.getMaps("SELECT RIGHT(REFERENCE_NUMBER,4) AS REF_LAST4, CONTRACT_SERVICE_ID FROM BO_EU_DNCRM_INSTALLED_PRODUCT WHERE RIGHT(REFERENCE_NUMBER,4) IN (" + String.join(",", Collections.nCopies(lastFourList.size(), "?")) + ")", lastFourList.toArray());
                Map<String, String> phoneContractMap = new HashMap<>();
                for (RowMap row : contractList) {
                    String refLast4 = row.getString("REF_LAST4");
                    String contractId = row.getString("CONTRACT_SERVICE_ID");
                    if (contractId != null && !contractId.trim().isEmpty()) { phoneContractMap.put(refLast4, contractId); }
                }

                if (!phoneContractMap.isEmpty()) {
                    StringBuilder batchUpdateSql = new StringBuilder("UPDATE BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD SET PERIOD_PRICE = CASE CONTRACT_SERVICE_ID ");
                    List<Object> batchParams = new ArrayList<>();
                    List<String> contractIds = new ArrayList<>();
                    for (Map.Entry<String, String> entry : phoneContractMap.entrySet()) {
                        batchUpdateSql.append("WHEN ? THEN ? ");
                        batchParams.add(entry.getValue());
                        batchParams.add(phoneAmountMap.getOrDefault(entry.getKey(), BigDecimal.ZERO));
                        contractIds.add(entry.getValue());
                    }
                    // ========== 核心修改：新增周期日期条件 ==========
                    batchUpdateSql.append("END WHERE CONTRACT_SERVICE_ID IN (").append(String.join(",", Collections.nCopies(contractIds.size(), "?"))).append(") ");
                    batchUpdateSql.append("AND PERIOD_BEGIN_DATE >= ? AND PERIOD_END_DATE <= ?"); // 新增周期条件

                    // 添加合同ID参数 + 周期起止日期参数
                    for (String id : contractIds) { batchParams.add(id); }
                    batchParams.add(periodBeginDate); // 账期当月1日
                    batchParams.add(periodEndDate);   // 账期当月最后1日

                    periodUpdatedCount = DBSql.update(batchUpdateSql.toString(), batchParams.toArray());
                }
            }

            // 7. 成功返回（原有逻辑，可选携带账期映射）
            result.put("result", "ok");
            result.put("message", fileName.isEmpty() ? "未提取到fileName" : (lastFourList.isEmpty() ? "未提取到手机号" : "处理完成"));
            result.put("proxyPayUpdatedCount", proxyPayUpdatedCount);
            result.put("periodUpdatedCount", periodUpdatedCount);
            result.put("phoneFeeDateMap", phoneFeeDateMap); // 可选：返回查询到的账期
            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (Exception e) {
            // 8. 失败返回（原有逻辑）
            e.printStackTrace();
            result.put("result", "error");
            result.put("message", "更新失败：" + e.getMessage());
            result.put("proxyPayUpdatedCount", 0);
            result.put("periodUpdatedCount", 0);
            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;
        }
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.insertPumaDataByQuarter")
    public ResponseObject insertPumaDataByQuarter(UserContext uc) {
        try {
            ProcessInstance processInstance = SDK.getProcessAPI().createProcessInstance(
                    "obj_516c3984931b4153b18f5445a2ed30d0",
                    uc.getUID(),
                    "彪马客户对账单-季度"
            );
            String querySql = "SELECT item_quarter, " +
                    "sum(onetime_dollars) as onetime_dollars, " +
                    "sum(tube_dollars) as tube_dollars, " +
                    "sum(property_manage_dollars) as property_manage_dollars, " +
                    "sum(construction_dollars) as construction_dollars, " +
                    "sum(phone_dollars) as phone_dollars, " +
                    "sum(datacard_dollars) as datacard_dollars, " +
                    "sum(datacard_personal_dollars) as datacard_personal_dollars, " +
                    "sum(vats_service_dollars) as vats_service_dollars, " +
                    "sum(product_dollars) as product_dollars, " +
                    "sum(adsl_dollars) as adsl_dollars, " +
                    "sum(total_dollars) as total_dollars, " +
                    "sum(VATS_SERVICE_DOLLARS) as VATS_SERVICE_DOLLARS " +
                    "FROM VIEW_EU_DNV_RPT_MONTH_PUMA " +
                    "GROUP BY item_quarter";

            List<RowMap> queryResult = DBSql.getMaps(querySql);
            if (queryResult.isEmpty()) {
                return ResponseObject.newErrResponse("未查询到可插入的季度数据");
            }

            List<String> detailIds = new ArrayList<>();
            List<String> duplicateQuarters = new ArrayList<>();
            for (RowMap data : queryResult) {
                String itemQuarter = data.get("item_quarter").toString();
                String checkDuplicateSql = "SELECT COUNT(1) FROM BO_EU_DNRPT_MONTH_PUMA WHERE ITEM_QUARTER = ?";
                int count = DBSql.getInt(checkDuplicateSql, new Object[]{itemQuarter});
                if (count > 0) {
                    duplicateQuarters.add(itemQuarter);
                    continue;
                }

                String dateRange = convertQuarterToDateRange(itemQuarter);

                BigDecimal totalDollars = convertToBigDecimal(data.get("total_dollars"));
                String requestAmountCn = convertNumberToChinese(totalDollars); // 转大写

                BO detailBO = new BO();
                detailBO.setBindId(processInstance.getId());

                detailBO.set("ITEM_QUARTER", itemQuarter);
                detailBO.set("DATE_ITEM", dateRange);
                detailBO.set("ONETIME_DOLLARS", convertToBigDecimal(data.get("onetime_dollars")));
                detailBO.set("TUBE_DOLLARS", convertToBigDecimal(data.get("tube_dollars")));
                detailBO.set("PROPERTY_MANAGE_DOLLARS", convertToBigDecimal(data.get("property_manage_dollars")));
                detailBO.set("CONSTRUCTION_DOLLARS", convertToBigDecimal(data.get("construction_dollars")));
                detailBO.set("PHONE_DOLLARS", convertToBigDecimal(data.get("phone_dollars")));
                detailBO.set("DATACARD_DOLLARS", convertToBigDecimal(data.get("datacard_dollars")));
                detailBO.set("DATACARD_PERSONAL_DOLLARS", convertToBigDecimal(data.get("datacard_personal_dollars")));
                detailBO.set("VATS_SERVICE_DOLLARS", convertToBigDecimal(data.get("vats_service_dollars")));
                detailBO.set("PRODUCT_DOLLARS", convertToBigDecimal(data.get("product_dollars")));
                detailBO.set("ADSL_DOLLARS", convertToBigDecimal(data.get("adsl_dollars")));
                detailBO.set("TOTAL_DOLLARS", totalDollars);
                detailBO.set("VATS_SERVICE_DOLLARS", convertToBigDecimal(data.get("VATS_SERVICE_DOLLARS")));
                detailBO.set("REQUEST_AMOUNT_CN", requestAmountCn);

                SDK.getBOAPI().createDataBO("BO_EU_DNRPT_MONTH_PUMA", detailBO, uc);
                detailIds.add(detailBO.getId());
            }

            Map<String, Object> result = new HashMap<>();
            result.put("isCreated", true);
            result.put("insertCount", detailIds.size());
            result.put("duplicateCount", duplicateQuarters.size());
            result.put("duplicateQuarters", duplicateQuarters);
            result.put("detailRecordIds", detailIds);

            String message;
            if (!duplicateQuarters.isEmpty()) {
                message = String.format("成功创建 %d 条季度汇总明细记录，跳过 %d 条重复季度数据（重复季度：%s）",
                        detailIds.size(), duplicateQuarters.size(), String.join(",", duplicateQuarters));
            } else {
                message = String.format("成功创建 %d 条季度汇总明细记录", detailIds.size());
            }
            result.put("message", message);

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("创建季度汇总明细失败：" + e.getMessage());
        }
    }

    /**
     * 季度转日期范围（如20254 → 20251001-20251231）
     * @param itemQuarter 季度字符串（格式：年+季度，如20251/20254）
     * @return 日期范围字符串（如20251001-20251231）
     */
    private String convertQuarterToDateRange(String itemQuarter) {
        if (StringUtils.isBlank(itemQuarter) || itemQuarter.length() < 5) {
            return ""; // 格式异常返回空
        }
        // 拆分年和季度（如20254 → 年：2025，季度：4）
        String yearStr = itemQuarter.substring(0, 4);
        int quarter = Integer.parseInt(itemQuarter.substring(4));
        int year = Integer.parseInt(yearStr);

        // 定义各季度的起止月份
        int startMonth = 0, endMonth = 0;
        switch (quarter) {
            case 1:
                startMonth = 1;
                endMonth = 3;
                break;
            case 2:
                startMonth = 4;
                endMonth = 6;
                break;
            case 3:
                startMonth = 7;
                endMonth = 9;
                break;
            case 4:
                startMonth = 10;
                endMonth = 12;
                break;
            default:
                return ""; // 无效季度返回空
        }

        // 拼接起止日期（格式：年月日）
        String startDate = yearStr + String.format("%02d", startMonth) + "01";
        // 获取季度最后一天（处理2月/30天月份等）
        String endDate = getQuarterLastDay(year, endMonth);

        return startDate + "-" + endDate;
    }

    /**
     * 获取指定年+月份的最后一天（如2025年2月 → 20250228/20250229）
     * @param year 年
     * @param month 月（1-12）
     * @return 最后一天字符串（如20250228）
     */
    private String getQuarterLastDay(int year, int month) {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.MONTH, month - 1); // Calendar月份从0开始
        cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
        // 格式化为yyyyMMdd
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
        return sdf.format(cal.getTime());
    }

    /**
     * 通用金额类型转换（避免0值/类型异常）
     */
    private BigDecimal convertToBigDecimal(Object value) {
        if (value == null) {
            return BigDecimal.ZERO;
        }
        if (value instanceof BigDecimal) {
            return (BigDecimal) value;
        }
        if (value instanceof Number) {
            return BigDecimal.valueOf(((Number) value).doubleValue());
        }
        if (value instanceof String) {
            try {
                return new BigDecimal((String) value);
            } catch (NumberFormatException e) {
                return BigDecimal.ZERO;
            }
        }
        return BigDecimal.ZERO;
    }

    @Mapping(value = "com.awspaas.user.apps.donenow_ctt.updateContractEndDate")
    public ResponseObject updateContractEndDate(UserContext uc, String ids, String periodEndDate) {
        if (StringUtils.isBlank(ids)) {
            return ResponseObject.newErrResponse("ids 不能为空");
        }
        if (StringUtils.isBlank(periodEndDate)) {
            return ResponseObject.newErrResponse("PERIOD_END_DATE 不能为空");
        }

        try {
            String[] idArray = ids.split(",");
            StringBuilder validIdSb = new StringBuilder();
            int validIdCount = 0;
            for (String id : idArray) {
                if (StringUtils.isNotBlank(id)) {
                    if (validIdSb.length() > 0) {
                        validIdSb.append(",");
                    }
                    validIdSb.append("?");
                    validIdCount++;
                }
            }
            if (validIdCount == 0) {
                return ResponseObject.newErrResponse("无有效合同ID");
            }

            String updateSql = String.format(
                    "UPDATE BO_EU_DNCTT_CONTRACT SET CONTRACT_END_DATE = ?, END_DATE = ? WHERE ID IN (%s)",
                    validIdSb.toString()
            );
            System.out.println("最终执行的SQL：" + updateSql);

            Object[] params = new Object[2 + validIdCount];
            params[0] = periodEndDate;
            params[1] = periodEndDate;
            int paramIndex = 2;
            for (String id : idArray) {
                if (StringUtils.isNotBlank(id)) {
                    params[paramIndex++] = id.trim();
                }
            }
            System.out.println("参数数组长度：" + params.length + "，占位符数量：" + (2 + validIdCount)); // 调试用

            int updatedRows = DBSql.update(updateSql, params);

            Map<String, Object> result = new HashMap<>();
            result.put("ids", ids);
            result.put("periodEndDate", periodEndDate);
            result.put("updatedRows", updatedRows);
            result.put("validIdCount", validIdCount);

            ResponseObject responseObject = ResponseObject.newOkResponse();
            responseObject.setData(result);
            return responseObject;

        } catch (Exception e) {
            e.printStackTrace();
            return ResponseObject.newErrResponse("更新失败：" + e.getMessage());
        }
    }


}

