package com.ruoyi.oa.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.common.core.domain.model.LoginBody;
import com.ruoyi.common.helper.LoginHelper;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.core.domain.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.oa.domain.bo.SysOaWarehouseDetailBo;
import com.ruoyi.oa.domain.dto.ProjectActivityDTO;
import com.ruoyi.oa.domain.dto.ProjectDataDTO;
import com.ruoyi.oa.domain.vo.*;
import com.ruoyi.oa.service.CodeGeneratorService;
import com.ruoyi.oa.service.IExchangeRateService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.oa.domain.bo.SysOaProjectBo;
import com.ruoyi.oa.domain.SysOaProject;
import com.ruoyi.oa.mapper.SysOaProjectMapper;
import com.ruoyi.oa.service.ISysOaProjectService;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 项目管理Service业务层处理
 *
 * @author huangxing
 * @date 2024-01-11
 */
@RequiredArgsConstructor
@Service
public class SysOaProjectServiceImpl implements ISysOaProjectService {

    private final SysOaProjectMapper baseMapper;

    @Autowired
    private CodeGeneratorService codeGeneratorService;
    @Autowired
    private IExchangeRateService exchangeRateService;


    // 1. 定义常量列表（最好放到某个常量类里）
    private static final Set<String> VALID_PREFIXES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            "PL", "TC", "GI", "GL", "ZAM", "CC", "CR", "STL", "SM", "TH", "RJ"
    )));


    /**
     * 查询项目盈亏分页列表
     *
     * @param projectName       项目名称 (筛选)
     * @param projectNum        项目编号 (筛选)
     * @param projectStatus     项目状态 (筛选)
     * @param minContractAmount 最小合同额 (筛选)
     * @param maxContractAmount 最大合同额 (筛选)
     * @param minProfitLoss     最小盈亏 (筛选)
     * @param maxProfitLoss     最大盈亏 (筛选)
     * @param beginTimeStart    项目开始时间范围 (筛选)
     * @param beginTimeEnd      项目结束时间范围 (筛选)
     * @param profitType        盈亏类型 ("profit" 或 "loss") (筛选)
     * @param pageQuery         分页及排序查询参数
     * @return 分页结果
     */
    @Override
    public TableDataInfo<ProjectProfitLossVO> getProjectProfitLossList(
            String projectName,String projectNum,String projectCode,Integer tradeType,String projectStatus,
            BigDecimal minContractAmount,BigDecimal maxContractAmount,BigDecimal minProfitLoss,
            BigDecimal maxProfitLoss,String beginTimeStart,String beginTimeEnd,String profitType,Integer signingCompany,
            PageQuery pageQuery
    ) {
        // 1. 获取实时汇率
        BigDecimal currentExchangeRate = exchangeRateService.getCurrentUsdExchangeRate();

        // 2. 构建基础查询条件 (WHERE子句)
        QueryWrapper<SysOaProject> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(projectName), "p.project_name", projectName);
        wrapper.like(StringUtils.isNotBlank(projectNum), "p.project_num", projectNum);
        wrapper.like(StringUtils.isNotBlank(projectCode), "p.project_code", projectCode);
        wrapper.eq(tradeType != null, "p.trade_type", tradeType);
        wrapper.eq(StringUtils.isNotBlank(projectStatus), "p.project_status", projectStatus);
        wrapper.ge(minContractAmount != null, "p.funds", minContractAmount);
        wrapper.le(maxContractAmount != null, "p.funds", maxContractAmount);
        wrapper.ge(StringUtils.isNotBlank(beginTimeStart), "p.begin_time", beginTimeStart);
        wrapper.le(StringUtils.isNotBlank(beginTimeEnd), "p.begin_time", beginTimeEnd);
        //把签约公司也作为筛选条件
        wrapper.like(signingCompany!=null, "p.signing_company", signingCompany);

        // 先把 profit_loss 的 CASE 表达式完整地组装成一个字符串
        String profitLossExpr = "(" +
                "CASE " +
                " WHEN p.funds IS NOT NULL AND p.funds > 0 THEN " +
                "   CASE WHEN p.remark LIKE '%美元%' OR p.remark LIKE '%美金%' " +
                "        THEN p.funds * " + currentExchangeRate +
                "        ELSE p.funds END " +
                " ELSE COALESCE(finance_details.total_income, 0) " +
                "END " +
                " - COALESCE(finance_details.total_expenditure, 0)" +
                ")";

        // 不用 wrapper.having(...)
        if ("profit".equals(profitType)) {
            wrapper.apply(profitLossExpr + " > 0");
        } else if ("loss".equals(profitType)) {
            wrapper.apply(profitLossExpr + " < 0");
        }
        if (minProfitLoss != null) {
            wrapper.apply(profitLossExpr + " >= {0}", minProfitLoss);
        }
        if (maxProfitLoss != null) {
            wrapper.apply(profitLossExpr + " <= {0}", maxProfitLoss);
        }

        // 4. 构建分页对象 (排序由PageQuery自动处理)
        Page<ProjectProfitLossVO> page = pageQuery.build();

        // 5. 调用Mapper方法, 传入汇率
        Page<ProjectProfitLossVO> result = baseMapper.selectProfitLossPage(page, wrapper, currentExchangeRate);

        // 6. 再次填充VO中的展示字段, 确保前端获取的数据是完整的
        for (ProjectProfitLossVO vo : result.getRecords()) {
            vo.setExchangeRate(currentExchangeRate); // 填充实时汇率

            // 计算最终的总收入(人民币)用于展示
            if (vo.getOriginalFunds() != null && vo.getOriginalFunds().compareTo(BigDecimal.ZERO) > 0) {
                if (vo.getIsUsd() == 1) {
                    vo.setTotalIncomeCny(vo.getOriginalFunds().multiply(currentExchangeRate));
                } else {
                    vo.setTotalIncomeCny(vo.getOriginalFunds());
                }
            } else {
                vo.setTotalIncomeCny(vo.getDetailIncome());
            }
            // profit_loss 已经由数据库根据最新逻辑计算返回, 无需再次处理
        }

        return TableDataInfo.build(result);
    }

    /**
     * 查询项目管理
     */
    @Override
    public SysOaProjectVo queryById(Long projectId) {
        return baseMapper.selectVoByIdPlus(projectId);
    }

    /**
     * 查询项目管理列表
     */
    @Override
    public TableDataInfo<SysOaProjectVo> queryPageList(SysOaProjectBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOaProject> lqw = buildQueryWrapper(bo);
        Page<SysOaProjectVo> result = baseMapper.selectVoPlus(pageQuery.build(), lqw);
        
        // 获取实时汇率
        BigDecimal currentExchangeRate = exchangeRateService.getCurrentUsdExchangeRate();
        
        // 处理每条记录，转换美元金额为人民币
        for (SysOaProjectVo vo : result.getRecords()) {
            if (vo.getRemark() != null && vo.getFunds() != null) {
                // 检查备注中是否包含"美元"或"美金"
                if (vo.getRemark().contains("美元") || vo.getRemark().contains("美金")) {
                    // 转换为人民币
                    vo.setFundsRmb(vo.getFunds().multiply(currentExchangeRate));
                } else {
                    // 不需要转换，直接使用原金额
                    vo.setFundsRmb(vo.getFunds());
                }
            } else if (vo.getFunds() != null) {
                // 没有备注信息，默认使用原金额
                vo.setFundsRmb(vo.getFunds());
            }
        }
        
        return TableDataInfo.build(result);
    }

    /**
     * 查询项目管理列表
     */
    @Override
    public List<SysOaProjectVo> queryList(SysOaProjectBo bo) {
        LambdaQueryWrapper<SysOaProject> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SysOaProject> buildQueryWrapper(SysOaProjectBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysOaProject> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), SysOaProject::getProjectName, bo.getProjectName());
        lqw.like(StringUtils.isNotBlank(bo.getProjectNum()), SysOaProject::getProjectNum, bo.getProjectNum());
        lqw.like(StringUtils.isNotBlank(bo.getProjectCode()), SysOaProject::getProjectCode, bo.getProjectCode());

        lqw.eq(bo.getTradeType() != null, SysOaProject::getTradeType, bo.getTradeType());
        lqw.gt(bo.getPrePay() != null && bo.getPrePay() > 0, SysOaProject::getPrePay, 0);
        lqw.eq(StringUtils.isNotBlank(bo.getProjectType()), SysOaProject::getProjectType, bo.getProjectType());
        lqw.eq(StringUtils.isNotBlank(bo.getProjectStatus()), SysOaProject::getProjectStatus, bo.getProjectStatus());
        lqw.eq(StringUtils.isNotBlank(bo.getCreateBy()), SysOaProject::getCreateBy, bo.getCreateBy());
        //新增签约公司作为筛选条件
        lqw.eq(bo.getSigningCompany()!= null, SysOaProject::getSigningCompany, bo.getSigningCompany());
        lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
                SysOaProject::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));
        //客户id作为筛选条件
        lqw.eq(bo.getCustomerId() != null, SysOaProject::getCustomerId, bo.getCustomerId());
        lqw.orderByDesc(SysOaProject::getIsTop)  // 置顶项目优先
                .orderByDesc(SysOaProject::getCreateTime);  // 同状态下按创建时间倒序（多个置顶项目按创建时间排序）
        return lqw;
    }

    /**
     * 新增项目管理
     */
    @Override
    public Boolean insertByBo(SysOaProjectBo bo) {
//        if (bo.getProjectCode() != null) {
//            bo.setProjectCode(codeGeneratorService.nextCode(bo.getProjectCode()));
//        }
        SysOaProject add = BeanUtil.toBean(bo, SysOaProject.class);
        validEntityBeforeSave(add);

        add.setOriginalFinishTime(bo.getFinishTime());
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setProjectId(add.getProjectId());
        }
        return flag;
    }

    /**
     * 修改项目管理
     */
    @Override
    public Boolean updateByBo(SysOaProjectBo bo) {
        String prefix = bo.getProjectCode();
        if (prefix != null && VALID_PREFIXES.contains(prefix)) {
            // 只有当前缀在上面列表里，才真正去生成新编号
            bo.setProjectCode(codeGeneratorService.nextCode(prefix));
        }

        SysOaProject update = BeanUtil.toBean(bo, SysOaProject.class);

        // 当 customerId 为 0 时，设置为 null
        if (bo.getCustomerId() != null && bo.getCustomerId() == 0) {
            update.setCustomerId(null);
        }

        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    // 2. 在你的逻辑里做判断

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SysOaProject entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除项目管理
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 查询项目出库记录
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaOutWarehouseListVo> queryOutWarePageList(SysOaWarehouseDetailBo bo, PageQuery pageQuery) {
        Page<SysOaOutWarehouseListVo> page = baseMapper.selectPageOutList(pageQuery.build(), this.buildOutWareQueryWrapper(bo));
        return TableDataInfo.build(page);
    }

    /**
     * 获取当月每个项目的人力资源成本
     *
     * @return
     */
    @Override
    public List<SysOaProjectVo> getProjectDataByMonth(Date date) {
        if (date == null) {
            date = new Date();
        }
        return baseMapper.getProjectDataByMonth(getFirstDay(date), getLastDay(date));
    }

    /**
     * 统计数据需要
     *
     * @return
     */
    @Override
    public List<SysOaProjectVo> getProjectDataByMonthAndDate() {
        Date date = new Date();
        List<SysOaProjectVo> projectVos = baseMapper.getProjectDataByMonthAndDate(getFirstDay(date), getLastDay(date));

        return projectVos;
    }

    @Override
    public TableDataInfo<SysOaProjectVo> queryFiles(SysOaWarehouseDetailBo bo, PageQuery pageQuery) {
        QueryWrapper<SysOaProject> sysOaProjectQueryWrapper = buildOutWareQueryWrapper(bo);
        sysOaProjectQueryWrapper.groupBy("sop.project_id", "sop.project_name", "t.task_id", "t.task_type", "t.status", "nick_name", "ti.item_id", "ti.sign_time", "sop.create_time");
//        sysOaProjectQueryWrapper.having("file_urls IS NOT NULL");
        Page<SysOaProjectVo> result = baseMapper.selectFileVoList(pageQuery.build(), sysOaProjectQueryWrapper);
        List<SysOaProjectVo> projects = result.getRecords();
        // 2) 遍历每个项目，对其 taskList 做补充
        for (SysOaProjectVo project : projects) {
            List<SysOaTaskVo> taskList = project.getTaskList();
            if (taskList == null || taskList.isEmpty()) {
                continue;
            }

            // 2.1 取出已经出现的 taskType
            Set<Long> existingTypes = taskList.stream()
                    .map(SysOaTaskVo::getTaskType)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());

            // 2.2 找出最大 taskType
            Long maxType = existingTypes.stream().max(Long::compareTo).orElse(0L);
            if (maxType <= 1) {
                // 如果最大只有1，且你仍想补齐1..1，那其实啥都不补。
                // 也可以补其它逻辑
                continue;
            }

            // 2.3 在 1..maxType 范围内检查是否缺失
            for (Long type = 1L; type <= maxType; type++) {
                if (!existingTypes.contains(type)) {
                    // 缺失则插入一个空对象
                    SysOaTaskVo emptyTask = new SysOaTaskVo();
                    emptyTask.setTaskType(type);
                    // 例如给 status 默认 0, 或者不赋值
                    emptyTask.setStatus(null);
                    // 任务item列表为空
                    emptyTask.setTaskItemVoList(new ArrayList<>());
                    // 其他字段如 nickName, createTime, etc. 也可设为空或默认值

                    taskList.add(emptyTask);
                }
            }

            // 此时taskList已经包含了 1..maxType 全部类型
            // 如果你想让结果中的 taskType 从大到小排序或从小到大排序，再手动sort一下
            taskList.sort(Comparator.comparing(SysOaTaskVo::getTaskType));
        }
        return TableDataInfo.build(result);
    }

    /**
     * 进度使用的查询接口
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaProjectVo> queryPageList2(SysOaProjectBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<SysOaProject> lqw = buildQueryWrapper(bo);
        Page<SysOaProjectVo> result = baseMapper.selectVoAndProgress(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询项目 进度使用
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaProjectVo> listProjects(SysOaProjectBo bo, PageQuery pageQuery) {
        QueryWrapper<SysOaProject> qw = new QueryWrapper<SysOaProject>();
        qw.like(StringUtils.isNotBlank(bo.getProjectName()), "sop.project_name", bo.getProjectName());
        qw.eq("sop.trade_type", bo.getTradeType());
        qw.like(StringUtils.isNotBlank(bo.getProjectNum()), "sop.project_num", bo.getProjectNum());
        qw.groupBy("sop.project_id");
        Page<SysOaProjectVo> result = baseMapper.selectVoListPage(pageQuery.build(), qw);
        return TableDataInfo.build(result);
    }

    /**
     * 查看存在出库的项目
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<SysOaProjectVo> listWareProject(SysOaProjectBo bo, PageQuery pageQuery) {
        QueryWrapper<SysOaProject> lqw = Wrappers.query();
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), "sop.project_name", bo.getProjectName());
        lqw.like(StringUtils.isNotBlank(bo.getProjectCode()), "sop.project_code", bo.getProjectCode());
        lqw.like(StringUtils.isNotBlank(bo.getProjectNum()), "sop.project_num", bo.getProjectNum());
        Page<SysOaProjectVo> result = baseMapper.listWareProject(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    @Override
    public DashboardMetricsVo getMetrics(Date start, Date end,Long tradeType) {

        // 本期
        BigDecimal curFunds = baseMapper.selectTotalFunds(start, end,tradeType);
        Integer curCount = baseMapper.selectContractCount(start, end,tradeType);
        Integer curExpire = baseMapper.selectExpiringCount(start, end,tradeType);

        // 前期平均
        BigDecimal avgFunds = baseMapper.selectPrevAvgTotalFunds(start, end,tradeType);
        BigDecimal avgCount = baseMapper.selectPrevAvgContractCount(start, end,tradeType);
        BigDecimal avgExpire = baseMapper.selectPrevAvgExpiringCount(start, end,tradeType);

        // 计算增长率 = (cur - avg) / avg * 100
        BigDecimal fundRate = avgFunds.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO
                : curFunds.subtract(avgFunds)
                .divide(avgFunds, 2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        BigDecimal countRate = avgCount.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO
                : BigDecimal.valueOf(curCount).subtract(avgCount)
                .divide(avgCount, 2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        BigDecimal expireRate = avgExpire.compareTo(BigDecimal.ZERO) == 0
                ? BigDecimal.ZERO
                : BigDecimal.valueOf(curExpire).subtract(avgExpire)
                .divide(avgExpire, 2, RoundingMode.HALF_UP)
                .multiply(BigDecimal.valueOf(100));

        // 组装 VO
        DashboardMetricsVo vo = new DashboardMetricsVo();
        vo.setTotalFunds(curFunds);
        vo.setGrowthFunds(fundRate);
        vo.setContractCount(curCount);
        vo.setGrowthCount(countRate);
        vo.setExpiringCount(curExpire);
        vo.setGrowthExpiring(expireRate);

        // 用本月金额 ÷ 历史平均 × 100 得到 completionRate
        BigDecimal completionRate;
        if (avgFunds.compareTo(BigDecimal.ZERO) > 0) {
            completionRate = curFunds
                    .divide(avgFunds, 2, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100));
        } else {
            completionRate = BigDecimal.ZERO;
        }
        vo.setCompletionRate(completionRate);
        return vo;
    }

    @Override
    public DashboardChartsVo getCharts(Date start, Date end,Long tradeType) {
        DashboardChartsVo vo = new DashboardChartsVo();
        vo.setContractAmountTrend(baseMapper.selectContractAmountTrend(start, end,tradeType));
        vo.setProjectStatusDistribution(baseMapper.selectProjectStatusDistribution(tradeType));
        vo.setMonthlyContractComparison(baseMapper.selectMonthlyContractComparison(start, end,tradeType));
        return vo;
    }

    @Override
    public List<SysOaProjectVo> getExpiringForeignProjects(Integer expireDays,Long tradeType) {
        Date now = new Date();
        return baseMapper.selectExpiringForeignProjects(now, expireDays,tradeType);
    }

    @Override
    public List<ProjectDataDTO> getProjectData(String month) {
        return baseMapper.getProjectData(month);
    }

    @Override
    public List<ProjectActivityDTO> getProjectActivities() {
        return baseMapper.getProjectActivities();
    }



    private QueryWrapper<SysOaProject> buildOutWareQueryWrapper(SysOaWarehouseDetailBo bo) {
        QueryWrapper<SysOaProject> lqw = Wrappers.query();
        lqw.like(StringUtils.isNotBlank(bo.getProjectName()), "sop.projec_name", bo.getProjectName());
        lqw.orderByDesc("sowm.create_time");
        return lqw;
    }


    private Date getFirstDay(Date time) {
        String firstDay = DateUtils.parseDateToStr("yyyy-MM-01", time);
        return DateUtils.parseDate(firstDay);
    }

    private Date getLastDay(Date time) {
        String format = "yyyy-MM-";
        // 计算某月的天数
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(time);
        String dateLength = String.valueOf(calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
        // 将字符串拼接
        format += dateLength;

        return DateUtils.parseDate(DateUtils.parseDateToStr(format, time));
    }

    @Override
    public Boolean postponeProject(SysOaProject bo) {
        // 1. 基础参数校验
        if (bo.getProjectId() == null || bo.getFinishTime() == null) {
            throw new RuntimeException("项目ID和延期日期不能为空"); // 也可返回false，根据业务选择
        }

        // 2. 查询项目是否存在
        SysOaProject project = baseMapper.selectById(bo.getProjectId());
        if (project == null) {
            throw new RuntimeException("项目不存在，无法延期");
        }

        // 3. 校验延期日期合理性（延期日期不能早于当前结束日期）
        if (project.getFinishTime() != null && bo.getFinishTime().before(project.getFinishTime())) {
            throw new RuntimeException("延期日期不能早于当前结束日期（当前结束日期：" + project.getFinishTime() + "）");
        }

        // 4. 构建更新条件，执行延期操作
        LambdaUpdateWrapper<SysOaProject> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(SysOaProject::getProjectId, bo.getProjectId()); // 锁定指定项目
        // 关键逻辑：如果原始结束时间为空（首次延期），则将当前finish_time赋值给original_finish_time
        if (project.getOriginalFinishTime() == null) {
            updateWrapper.set(SysOaProject::getOriginalFinishTime, project.getFinishTime());
        }
        updateWrapper.set(SysOaProject::getFinishTime, bo.getFinishTime()) // 延期日期赋值给finish_time
                .set(SysOaProject::getPostponeTime, bo.getFinishTime()) // 同步更新“延期至日期”
                .set(SysOaProject::getIsPostpone, 1) // 标记为已延期（1=是）
                .setSql("postpone_count = postpone_count + 1") // 延期次数+1
                .set(SysOaProject::getUpdateTime, new Date()) // 更新时间
                .set(SysOaProject::getUpdateBy, LoginHelper.getUsername()); // 获取当前操作用户

        // 5. 执行更新，返回操作结果（更新行数>0则视为成功）
        return baseMapper.update(null, updateWrapper) > 0;
    }

    @Override
    public SysOaProjectVo getMaxCode(String prefix) {
        return    baseMapper.getMaxCodeProject(prefix);
    }

}
