zhubaomin
昨天 6f2c512bffe6faaa8536fa98fbf2b2e552b6a2ac
轮灌功能PC端5个接口
5个文件已修改
12个文件已添加
2088 ■■■■■ 已修改文件
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/PipIrrIrrigateApplication.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/CommandSv.java 862 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/AutomaticClose.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/DtoBase.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/ValveClose.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/ValveOpen.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/IrrigateGroupCtrl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/IrrigateGroupSv.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/qo/QoGroup.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigatePlanCtrl.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigatePlanSv.java 650 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigationSv.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/IrrigatePlan.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/IrrigateSchedule.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/PlanSimple.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/enums/OperateTypeENUM.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/resources/application-self.yml 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/PipIrrIrrigateApplication.java
@@ -18,7 +18,7 @@
                })
        }
)
@MapperScan({"com.dy.pipIrrGlobal.daoIr", "com.dy.pipIrrGlobal.daoIr","com.dy.pipIrrGlobal.daoFi"})
@MapperScan({"com.dy.pipIrrGlobal.daoRm", "com.dy.pipIrrGlobal.daoPr", "com.dy.pipIrrGlobal.daoSe", "com.dy.pipIrrGlobal.daoBa", "com.dy.pipIrrGlobal.daoIr", "com.dy.pipIrrGlobal.daoIr","com.dy.pipIrrGlobal.daoFi"})
public class PipIrrIrrigateApplication {
    public static void main(String[] args) {
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/CommandSv.java
New file
@@ -0,0 +1,862 @@
package com.dy.pipIrrIrrigate.command;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.multiDataSource.DataSourceContext;
import com.dy.common.mw.protocol.Command;
import com.dy.common.mw.protocol.p206V1.CodeV1;
import com.dy.common.mw.protocol.p206V1.ProtocolConstantV206V1;
import com.dy.common.mw.protocol.p206V1.downVos.Com97Vo;
import com.dy.common.mw.protocol.p206V1.downVos.Com98Vo;
import com.dy.common.mw.protocol.p206V1.downVos.ComA1Vo;
import com.dy.common.mw.protocol.p206V2.ProtocolConstantV206V2;
import com.dy.common.mw.protocol.p206V202404.CodeV202404;
import com.dy.common.mw.protocol.p206V202404.ProtocolConstantV206V202404;
import com.dy.common.mw.protocol.p206V202404.downVos.ComCd92_A2Vo;
import com.dy.common.mw.protocol.p206V202404.downVos.ComCd93_A3Vo;
import com.dy.common.util.IDLongGenerator;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.QueryResultVo;
import com.dy.pipIrrGlobal.command.ComSupport;
import com.dy.pipIrrGlobal.command.dto.Param;
import com.dy.pipIrrGlobal.daoIr.IrIntakeOperateMapper;
import com.dy.pipIrrGlobal.daoPr.PrIntakeControllerMapper;
import com.dy.pipIrrGlobal.daoPr.PrIntakeMapper;
import com.dy.pipIrrGlobal.daoPr.PrIntakeVcMapper;
import com.dy.pipIrrGlobal.daoPr.PrWaterPriceMapper;
import com.dy.pipIrrGlobal.daoRm.RmCommandHistoryMapper;
import com.dy.pipIrrGlobal.daoRm.RmIrrigateProfileMapper;
import com.dy.pipIrrGlobal.daoRm.RmOpenCloseValveLastMapper;
import com.dy.pipIrrGlobal.daoSe.SeClientCardMapper;
import com.dy.pipIrrGlobal.daoSe.SeVirtualCardMapper;
import com.dy.pipIrrGlobal.pojoIr.IrIntakeOperate;
import com.dy.pipIrrGlobal.pojoRm.RmCommandHistory;
import com.dy.pipIrrGlobal.pojoSe.SeClientCard;
import com.dy.pipIrrGlobal.voRm.VoIrrigaterProfile;
import com.dy.pipIrrGlobal.voRm.VoRtuAndVc;
import com.dy.pipIrrGlobal.voRm.VoUnclosedValve;
import com.dy.pipIrrGlobal.voSe.VoVirtualCard;
import com.dy.pipIrrIrrigate.command.dto.AutomaticClose;
import com.dy.pipIrrIrrigate.command.dto.ValveClose;
import com.dy.pipIrrIrrigate.command.dto.ValveOpen;
import com.dy.pipIrrIrrigate.irrigatePlan.IrrigatePlanSv;
import com.dy.pipIrrIrrigate.irrigatePlan.IrrigationSv;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
/**
 * @author ZhuBaoMin
 * @date 2025-07-02 9:10
 * @LastEditTime 2025-07-02 9:10
 * @Description
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CommandSv extends ComSupport {
    private final RmCommandHistoryMapper rmCommandHistoryMapper;
    private final SeVirtualCardMapper seVirtualCardMapper;
    private final PrIntakeMapper prIntakeMapper;
    private final PrWaterPriceMapper prWaterPriceMapper;
    private final PrIntakeVcMapper prIntakeVcMapper;
    private final SeClientCardMapper seClientCardMapper;
    private final RmIrrigateProfileMapper rmIrrigateProfileMapper;
    private final RmOpenCloseValveLastMapper rmOpenCloseValveLastMapper;
    //private final PrCommonIntakesMapper prCommonIntakesMapper;
    private final IrIntakeOperateMapper irIntakeOperateMapper;
    //private final IrrigatePlanSv irrigatePlanSv;
    private final IrrigationSv irrigationSv;
    private final PrIntakeControllerMapper prIntakeControllerMapper;
    //@Value("${mw.rtuCallbackUrl_wx}")
    //private String rtuCallbackUrl_wx;
    @Value("${mw.rtuCallbackUrl_rm}")
    private String rtuCallbackUrl_rm;
    /**
     * pro_mw:属性
     * tag从控制器中获取
     * key_mw:url的key
     */
    private Environment env = null;
    private String pro_mw = "mw";
    private String key_mw = "comSendUrl";
    @Autowired
    public CommandSv(RmCommandHistoryMapper rmCommandHistoryMapper, RmOpenCloseValveLastMapper rmOpenCloseValveLastMapper, SeVirtualCardMapper seVirtualCardMapper, PrIntakeMapper prIntakeMapper, PrWaterPriceMapper prWaterPriceMapper, PrIntakeVcMapper prIntakeVcMapper, SeClientCardMapper seClientCardMapper, RmIrrigateProfileMapper rmIrrigateProfileMapper, Environment env, IrIntakeOperateMapper irIntakeOperateMapper, IrrigationSv irrigationSv, PrIntakeControllerMapper prIntakeControllerMapper) {
        this.rmCommandHistoryMapper = rmCommandHistoryMapper;
        this.rmOpenCloseValveLastMapper = rmOpenCloseValveLastMapper;
        //this.prCommonIntakesMapper = prCommonIntakesMapper;
        this.seVirtualCardMapper = seVirtualCardMapper;
        this.prIntakeMapper = prIntakeMapper;
        this.prWaterPriceMapper = prWaterPriceMapper;
        this.prIntakeVcMapper = prIntakeVcMapper;
        this.seClientCardMapper = seClientCardMapper;
        this.rmIrrigateProfileMapper = rmIrrigateProfileMapper;
        this.env = env;
        this.irIntakeOperateMapper = irIntakeOperateMapper;
        this.irrigationSv = irrigationSv;
        this.prIntakeControllerMapper = prIntakeControllerMapper;
    }
    /**
     * 虚拟卡ID换虚拟卡对象
     *
     * @param vcId
     * @return
     */
    public VoVirtualCard getVcById(Long vcId) {
        return seVirtualCardMapper.getVcById(vcId);
    }
    /**
     * 获取水价
     *
     * @return
     */
    public Double getPrice() {
        return prWaterPriceMapper.getPrice();
    }
    /**
     * 根据操作员ID获取未关阀记录(包含在线情况)
     *
     * @param operator
     * @return
     */
    public List<VoUnclosedValve> getUnclosedValves(Long operator) {
        Command com = new Command();
        com.id = Command.defaultId;
        com.code = "LCD0001";
        com.type = "innerCommand";
        comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw);
        JSONObject response = (JSONObject) JSON.toJSON(sendCom2Mw(com));
        String aa = response.toJSONString();
        JSONArray jsonArray = new JSONArray();
        if (response != null && response.getString("code").equals("0001")) {
            JSONObject attachment = response.getJSONObject("content").getJSONObject("attachment");
            if (attachment != null) {
                attachment.forEach((key, value) -> {
                    JSONObject jsonObject = new JSONObject();
                    jsonObject.put("rtuAddr", key);
                    jsonObject.put("isOnLine", value);
                    jsonArray.add(jsonObject);
                });
            }
            //System.out.println(jsonArray.toJSONString());
        }
        /**
         * 获取未关阀的RTU地址和虚拟卡编号对数组
         * 遍历数组获取最后命令日志ID集合
         */
        List<VoRtuAndVc> rtus = rmCommandHistoryMapper.getUnclosedRtus(operator);
        String comIds = "";
        if (rtus != null && rtus.size() > 0) {
            for (int i = 0; i < rtus.size(); i++) {
                Long comId = rmCommandHistoryMapper.getLastComId(rtus.get(i).getRtuAddr(), rtus.get(i).getVcNum());
                if (comId > 0) {
                    comIds = comIds + "\"" + comId + "\",";
                }
            }
        }
        if (comIds.length() > 0) {
            comIds = comIds.substring(0, comIds.length() - 1);
            List<VoUnclosedValve> res = rmCommandHistoryMapper.getUnclosedValves(jsonArray.toJSONString(), comIds);
            if (res != null) {
                return res;
            }
        }
        return new ArrayList<>();
    }
    /**
     * 为指定(阀控器地址、水卡编号、无关阀记录)开关阀最新记录添加关阀时间,使之不在未关阀记录中显示
     *
     * @param rtuAddr
     * @param icCardNo
     * @return
     */
    public Integer addClostTime(String rtuAddr, String icCardNo) {
        return rmOpenCloseValveLastMapper.addCloseTime(rtuAddr, icCardNo);
    }
    /**
     * 根据取水口ID获取与之绑定虚拟卡ID
     *
     * @param intakeId
     * @return
     */
    public Long getVcIdByIntakeId(Long intakeId) {
        return prIntakeVcMapper.getVcIdByIntakeId(intakeId);
    }
    /**
     * 根据水卡编号获取水卡对象,远程充值使用
     *
     * @param cardId
     * @return
     */
    public SeClientCard geClientCardByCardId(Long cardId) {
        return seClientCardMapper.selectByPrimaryKey(cardId);
    }
    /**
     * 添加命令日志
     *
     * @param po 命令日志对象
     * @return 字符串类型的主键
     */
    public Long insert(RmCommandHistory po) {
        rmCommandHistoryMapper.insert(po);
        return po.getComId();
    }
    /**
     * 修改命令日志信息
     *
     * @param po 命令日志对象
     * @return 影响记录数量
     */
    public Integer update(RmCommandHistory po) {
        return rmCommandHistoryMapper.updateByPrimaryKeySelective(po);
    }
    /**
     * 取水口名称换取水口ID,扫码开阀使用
     *
     * @param intakeName
     * @return
     */
    public Long getIntakeIdByName(String intakeName) {
        return prIntakeMapper.getIntakeIdByName(intakeName);
    }
    /**
     * 根据配置类型获取配置值
     *
     * @param type
     * @return
     */
    QueryResultVo<List<VoIrrigaterProfile>> getIrrPro(Integer type) {
        QueryResultVo<List<VoIrrigaterProfile>> rsVo = new QueryResultVo<>();
        rsVo.obj = rmIrrigateProfileMapper.getIrrPro(type);
        return rsVo;
    }
    /**
     * 添加取水口操作记录,轮灌过程命令下发时使用
     *
     * @param po
     * @return
     */
    public Long addIntakeOperate(IrIntakeOperate po) {
        irIntakeOperateMapper.insert(po);
        return po.getId();
    }
    /**
     * 定时关阀式计划开阀
     *
     * @param automaticClose
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map planedOpenTimedClose(AutomaticClose automaticClose, Long planId, Byte operatetype, Date startTime, Integer duration) {
        Long intakeId = automaticClose.getIntakeId();
        Long vcId = automaticClose.getVcId();
        Date plannedOpenTime = automaticClose.getPlannedOpenTime();
        Integer minutes = automaticClose.getMinutes();
        Long operator = automaticClose.getOperator();
        Byte openType = automaticClose.getOpenType();
        Long comId = new IDLongGenerator().generate();
        String intakeName = Optional.ofNullable(prIntakeControllerMapper.getIntakeNameById(intakeId)).orElse("");
        // 如果是轮灌
        Long operateId = null;
        if (openType != null && openType == 1) {
            IrIntakeOperate irIntakeOperate = new IrIntakeOperate();
            irIntakeOperate.setPlanId(planId);
            irIntakeOperate.setOperateType(operatetype);
            irIntakeOperate.setCommandId(comId);
            irIntakeOperate.setIntakeId(intakeId);
            irIntakeOperate.setStartTime(startTime);
            irIntakeOperate.setDuration(duration);
            operateId = addIntakeOperate(irIntakeOperate);
            if (operateId == null) {
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "添加取水口操作记录失败");
                map.put("content", null);
                return map;
            }
        }
        Integer year = Integer.parseInt(String.format("%tY", plannedOpenTime));
        Integer month = Integer.parseInt(String.format("%tm", plannedOpenTime));
        Integer day = Integer.parseInt(String.format("%td", plannedOpenTime));
        Integer hour = Integer.parseInt(String.format("%tH", plannedOpenTime));
        Integer minute = Integer.parseInt(String.format("%tM", plannedOpenTime));
        /**
         * 如果农户选择了虚拟卡,则使用该虚拟卡
         * 如果农户未选择虚拟卡,则根据取水口ID获取与之绑定的虚拟卡
         * 如果取水口没有与之绑定的虚拟卡,则提示农户选择一张虚拟卡
         */
        if (vcId == null) {
            vcId = getVcIdByIntakeId(intakeId);
            if (vcId == null) {
                irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")");
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "请选择一张虚拟卡(" + intakeName + ")");
                map.put("content", null);
                return map;
            }
        }
        // 虚拟卡ID换虚拟卡对象
        VoVirtualCard vc = getVcById(vcId);
        if (vc == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "请选择一张虚拟卡(" + intakeName + ")");
            map.put("content", null);
            return map;
        }
        String vcNum = vc.getVcNum().toString();
        Double moneyRemain = vc.getMoney();
        if (moneyRemain < 100) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "虚拟卡余额不足,请充值" + "(" + intakeName + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "虚拟卡余额不足,请充值");
            map.put("content", null);
            return map;
        }
        // 获取水价
        Double waterPrice = getPrice();
        if (waterPrice == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未取到水价(" + intakeName + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "未取到水价");
            map.put("content", null);
            return map;
        }
        // 取水口ID换阀控器地址及通讯协议
        JSONObject job_rtu = getRtu(intakeId, null);
        if (job_rtu == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "阀控器不存在(" + intakeName + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "阀控器不存在");
            map.put("content", null);
            return map;
        }
        String rtuAddr = job_rtu.getString("rtuAddr");
        String protocol = job_rtu.getString("protocol");
        comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw);
        // 生成订单号
        String orderNo = generateOrderNo();
        if (orderNo == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未生成订单号(" + intakeName + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "未生成订单号");
            map.put("content", null);
            return map;
        }
        // 判断阀控器是否已经开阀
        Boolean isOpen = irrigationSv.getOpenCloseStatus(rtuAddr);
        if (isOpen) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, intakeName + " 阀门为打开状态");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", intakeName + " 阀门为打开状态");
            map.put("content", null);
            return map;
        }
        String commandCode = null;
        if (protocol.equals(ProtocolConstantV206V202404.protocolName)) {
            Map map = new HashMap<>();
            map.put("success", true);
            map.put("msg", "开阀成功");
            map.put("content", null);
            return map;
        } else if (protocol.equals(ProtocolConstantV206V1.protocolName) || protocol.equals(ProtocolConstantV206V2.protocolName)) {
            // 获取功能码
            commandCode = CodeV1.cd_A1;
            // 创建视图
            ComA1Vo param = new ComA1Vo();
            param.setIcCardNo(vcNum);
            param.setMoneyRemain(moneyRemain);
            param.setWaterPrice(waterPrice);
            param.setMinutes(minutes);
            param.setYear(year);
            param.setMonth(month);
            param.setDay(day);
            param.setHour(hour);
            param.setMinute(minute);
            param.setOrderNo(orderNo);
            /**
             * 构造命令、发送命令并处理请求结果及执行结果
             * 1 准备参数
             * 2 调用公共方法
             */
            Param myParam = new Param();
            myParam.setComId(comId);
            myParam.setComType((byte) 1);
            myParam.setCommandCode(commandCode);
            myParam.setCommandName(CodeV1.getCodeName(commandCode));
            myParam.setIntakeId(intakeId);
            myParam.setRtuAddr(rtuAddr);
            myParam.setProtocol(protocol);
            myParam.setVcId(vcId);
            myParam.setParam(param);
            myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm);
            myParam.setOperator(operator);
            myParam.setOpenType(openType);
            BaseResponse<Boolean> result = dealWithCommandResult(myParam);
            // 如果命令执行失败,则再次执行命令
            if (!result.isSuccess()) {
                Long newComId = new IDLongGenerator().generate();
                myParam.setComId(newComId);
                result = dealWithCommandResult(myParam);
                // 根据operateId修改取水口操作记录中的commandId
                irIntakeOperateMapper.updateCommandId(newComId, operateId);
            }
            Map map = new HashMap<>();
            map.put("success", result.isSuccess());
            map.put("msg", result.getMsg());
            map.put("content", result.getContent());
            return map;
        } else {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "系统暂不支持该协议" + "(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "系统暂不支持该协议");
            map.put("content", null);
            return map;
        }
    }
    /**
     * 小程序远程开阀
     *
     * @param valve
     * @param planId
     * @param operatetype
     * @return
     */
    public Map openWx(ValveOpen valve, Long planId, Byte operatetype) {
        Long intakeId = valve.getIntakeId();
        String intakeName = valve.getIntakeName();
        Long vcId = valve.getVcId();
        Boolean forceOpen = valve.getForceOpen();
        Long operator = valve.getOperator();
        Byte openType = valve.getOpenType();
        Long comId = new IDLongGenerator().generate();
        // 如果是轮灌
        if (openType != null && openType == 1) {
            IrIntakeOperate irIntakeOperate = new IrIntakeOperate();
            irIntakeOperate.setPlanId(planId);
            irIntakeOperate.setOperateType(operatetype);
            irIntakeOperate.setCommandId(comId);
            irIntakeOperate.setIntakeId(intakeId);
            Long id = addIntakeOperate(irIntakeOperate);
            if (id == null) {
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "添加取水口操作记录失败");
                map.put("content", null);
                return map;
            }
        }
        if (intakeId == null && intakeName == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一个取水口(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "请选择一个取水口(" + intakeId + ")");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_INTAKE.getMessage());
        }
        // intakeName换intakeId
        if (intakeId == null) {
            intakeId = getIntakeIdByName(intakeName);
            if (intakeId == null) {
                irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一个取水口(" + intakeId + ")");
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "请选择一个取水口(" + intakeId + ")");
                map.put("content", null);
                return map;
                //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_INTAKE.getMessage());
            }
        }
        /**
         * 如果农户选择了虚拟卡,则使用该虚拟卡
         * 如果农户未选择虚拟卡,则根据取水口ID获取与之绑定的虚拟卡
         * 如果取水口没有与之绑定的虚拟卡,则提示农户选择一张虚拟卡
         */
        if (vcId == null) {
            vcId = getVcIdByIntakeId(intakeId);
            if (vcId == null) {
                irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")");
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "请选择一张虚拟卡(" + intakeId + ")");
                map.put("content", null);
                return map;
                //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_VC.getMessage());
            }
        }
        // 虚拟卡ID换虚拟卡对象
        VoVirtualCard vc = getVcById(vcId);
        if (vc == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "请选择一张虚拟卡(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "请选择一张虚拟卡(" + intakeId + ")");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg(WechatResultCode.PLEASE_SELECT_A_VC.getMessage());
        }
        // 虚拟卡状态为使用中,且不是强制开阀时提示
        if (vc.getInUse() && !forceOpen) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "使用中虚拟卡不能用来开阀(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "使用中虚拟卡不能用来开阀(" + intakeId + ")");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildCodeMsg(WechatResultCode.IN_USE_VC_CANNOT_OPEN_VALVE.getCode(), WechatResultCode.IN_USE_VC_CANNOT_OPEN_VALVE.getMessage());
        }
        // 获取水价
        Double waterPrice = getPrice();
        if (waterPrice == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未取到水价(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "未取到水价(" + intakeId + ")");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg(WechatResultCode.NO_WATER_PRICE.getMessage());
        }
        // 取水口ID换阀控器地址及通讯协议
        JSONObject job_rtu = getRtu(intakeId, null);
        if (job_rtu == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "阀控器不存在(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "阀控器不存在(" + intakeId + ")");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg(WechatResultCode.RTU_NOT_EXIST.getMessage());
        }
        String rtuAddr = job_rtu.getString("rtuAddr");
        String protocol = job_rtu.getString("protocol");
        comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw);
        // 生成订单号
        String orderNo = generateOrderNo();
        if (orderNo == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "未生成订单号(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "未生成订单号(" + intakeId + ")");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg(WechatResultCode.NO_ORDER_NUMBER.getMessage());
        }
        String commandCode = null;
        if (protocol.equals(ProtocolConstantV206V202404.protocolName)) {
            // 获取功能码
            commandCode = CodeV202404.cd_A2;
            // 创建视图
            ComCd92_A2Vo param = new ComCd92_A2Vo();
            param.controllerType = controllerType;
            param.projectNo = projectNo;
            param.icCardNo = vc.getVcNum().toString();
            param.waterRemain = 0.0;
            param.moneyRemain = vc.getMoney();
            param.waterPrice = waterPrice;
            param.elePrice = 0.0;
            param.orderNo = orderNo;
            /**
             * 构造命令、发送命令并处理请求结果及执行结果
             * 1 准备参数
             * 2 调用公共方法
             */
            Param myParam = new Param();
            myParam.setComId(comId);
            myParam.setComType((byte) 1);
            myParam.setCommandCode(commandCode);
            myParam.setCommandName(CodeV202404.getCodeName(commandCode));
            myParam.setIntakeId(intakeId);
            myParam.setRtuAddr(rtuAddr);
            myParam.setProtocol(protocol);
            myParam.setVcId(vcId);
            myParam.setParam(param);
            myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm);
            myParam.setOperator(operator);
            //BaseResponse<Boolean> result = dealWithCommandResult(myParam);
            BaseResponse<Boolean> result = dealWithCommandResult(myParam);
            // 如果命令执行失败,则再次执行命令
            if (!result.isSuccess()) {
                myParam.setComId(new IDLongGenerator().generate());
                result = dealWithCommandResult(myParam);
            }
            Map map = new HashMap<>();
            map.put("success", result.isSuccess());
            map.put("msg", result.getMsg());
            map.put("content", result.getContent());
            return map;
        } else if (protocol.equals(ProtocolConstantV206V1.protocolName) || protocol.equals(ProtocolConstantV206V2.protocolName)) {
            // 获取功能码
            commandCode = CodeV1.cd_97;
            // 创建视图
            Com97Vo param = new Com97Vo();
            param.icCardNo = vc.getVcNum().toString();
            param.moneyRemain = vc.getMoney();
            param.waterPrice = waterPrice;
            param.orderNo = orderNo;
            /**
             * 构造命令、发送命令并处理请求结果及执行结果
             * 1 准备参数
             * 2 调用公共方法
             */
            Param myParam = new Param();
            myParam.setComId(comId);
            myParam.setComType((byte) 1);
            myParam.setCommandCode(commandCode);
            myParam.setCommandName(CodeV1.getCodeName(commandCode));
            myParam.setIntakeId(intakeId);
            myParam.setRtuAddr(rtuAddr);
            myParam.setProtocol(protocol);
            myParam.setVcId(vcId);
            myParam.setParam(param);
            myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm);
            myParam.setOperator(operator);
            //return dealWithCommandResult(myParam);
            BaseResponse<Boolean> result = dealWithCommandResult(myParam);
            Map map = new HashMap<>();
            map.put("success", result.isSuccess());
            map.put("msg", result.getMsg());
            map.put("content", result.getContent());
            return map;
        } else {
            //return BaseResponseUtils.buildErrorMsg("系统暂不支持该协议");
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "系统暂不支持该协议" + "(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "系统暂不支持该协议");
            map.put("content", null);
            return map;
        }
    }
    /**
     * 小程序远程关阀
     *
     * @param valve
     * @param planId
     * @param operatetype
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map closeWx(ValveClose valve, Long planId, Byte operatetype) {
        String rtuAddr = valve.getRtuAddr();
        String vcNum = valve.getVcNum();
        Long vcId = Optional.ofNullable(seVirtualCardMapper.getVcIdByNum(vcNum)).orElse(0L);
        String orderNo = valve.getOrderNo();
        Long operator = valve.getOperator();
        Byte openType = valve.getOpenType();
        Long comId = new IDLongGenerator().generate();
        // 阀控器地址换取水口ID和通讯协议
        JSONObject job_rtu = getRtu(null, rtuAddr);
        if (job_rtu == null) {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "阀控器不存在");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "阀控器不存在");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg(WechatResultCode.RTU_NOT_EXIST.getMessage());
        }
        Long intakeId = job_rtu.getLong("intakeId");
        String protocol = job_rtu.getString("protocol");
        // 如果是轮灌
        if (openType != null && openType == 1) {
            IrIntakeOperate irIntakeOperate = new IrIntakeOperate();
            irIntakeOperate.setPlanId(planId);
            irIntakeOperate.setOperateType(operatetype);
            irIntakeOperate.setCommandId(comId);
            irIntakeOperate.setIntakeId(intakeId);
            irIntakeOperate.setCommandResult((byte) 1);
            Long id = addIntakeOperate(irIntakeOperate);
            if (id == null) {
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "添加取水口操作记录失败");
                map.put("content", null);
                return map;
            }
        }
        comSendUrl = env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw);
        String commandCode = null;
        if (protocol.equals(ProtocolConstantV206V202404.protocolName)) {
            // 获取功能码
            commandCode = CodeV202404.cd_A3;
            // 创建视图
            ComCd93_A3Vo param = new ComCd93_A3Vo();
            param.controllerType = controllerType;
            param.projectNo = projectNo;
            param.icCardNo = vcNum;
            param.orderNo = orderNo;
            /**
             * 构造命令、发送命令并处理请求结果及执行结果
             * 1 准备参数
             * 2 调用公共方法
             */
            Param myParam = new Param();
            myParam.setComId(comId);
            myParam.setComType((byte) 2);
            myParam.setCommandCode(commandCode);
            myParam.setCommandName(CodeV202404.getCodeName(commandCode));
            myParam.setIntakeId(intakeId);
            myParam.setRtuAddr(rtuAddr);
            myParam.setProtocol(protocol);
            myParam.setVcId(vcId);
            myParam.setParam(param);
            myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm);
            myParam.setOperator(operator);
            //return dealWithCommandResult(myParam);
            BaseResponse<Boolean> result = dealWithCommandResult(myParam);
            Map map = new HashMap<>();
            map.put("success", result.isSuccess());
            map.put("msg", result.getMsg());
            map.put("content", result.getContent());
            return map;
        } else if (protocol.equals(ProtocolConstantV206V1.protocolName) || protocol.equals(ProtocolConstantV206V2.protocolName)) {
            // 获取功能码
            commandCode = CodeV1.cd_98;
            // 创建视图
            Com98Vo param = new Com98Vo();
            param.icCardNo = vcNum;
            param.orderNo = orderNo;
            /**
             * 构造命令、发送命令并处理请求结果及执行结果
             * 1 准备参数
             * 2 调用公共方法
             */
            Param myParam = new Param();
            myParam.setComId(comId);
            myParam.setComType((byte) 2);
            myParam.setCommandCode(commandCode);
            myParam.setCommandName(CodeV1.getCodeName(commandCode));
            myParam.setIntakeId(intakeId);
            myParam.setRtuAddr(rtuAddr);
            myParam.setProtocol(protocol);
            myParam.setVcId(vcId);
            myParam.setParam(param);
            myParam.setRtuResultSendWebUrl(rtuCallbackUrl_rm);
            myParam.setOperator(operator);
            //return dealWithCommandResult(myParam);
            BaseResponse<Boolean> result = dealWithCommandResult(myParam);
            // 如果命令执行失败,则再次执行命令
            if (!result.isSuccess()) {
                myParam.setComId(new IDLongGenerator().generate());
                result = dealWithCommandResult(myParam);
            }
            Map map = new HashMap<>();
            map.put("success", result.isSuccess());
            map.put("msg", result.getMsg());
            map.put("content", result.getContent());
            return map;
        } else {
            irIntakeOperateMapper.updateByCommandId(comId, (byte) 2, "系统暂不支持该协议" + "(" + intakeId + ")");
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "系统暂不支持该协议");
            map.put("content", null);
            return map;
            //return BaseResponseUtils.buildErrorMsg("系统暂不支持该协议");
        }
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/AutomaticClose.java
New file
@@ -0,0 +1,46 @@
package com.dy.pipIrrIrrigate.command.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2025-07-02 8:52
 * @LastEditTime 2025-07-02 8:52
 * @Description
 */
@Data
@EqualsAndHashCode(callSuper=false)
public class AutomaticClose extends DtoBase {
    public static final long serialVersionUID = 202507020853001L;
    /**
     * 虚拟卡ID
     */
    private Long vcId;
    /**
     * 计划开阀时间
     */
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date plannedOpenTime;
    /**
     * 用水时长,拥有定时关阀
     */
    private Integer minutes;
    /**
     * 预用水量,拥有定量关阀
     */
    private Integer waterAmount;
    /**
     * 开阀类型;1-轮灌
     */
    private Byte openType;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/DtoBase.java
New file
@@ -0,0 +1,31 @@
package com.dy.pipIrrIrrigate.command.dto;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2025-07-02 8:54
 * @LastEditTime 2025-07-02 8:54
 * @Description 设置命令基类
 */
@Data
public class DtoBase {
    public static final long serialVersionUID = 202507020904001L;
    /**
     * 取水口ID
     */
    private Long intakeId;
    /**
     * 取水口名称
     */
    private String intakeName;
    /**
     * 操作人
     */
    //@NotNull(message = "操作人不能为空")
    private Long operator;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/ValveClose.java
New file
@@ -0,0 +1,53 @@
package com.dy.pipIrrIrrigate.command.dto;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author ZhuBaoMin
 * @date 2025-07-02 9:42
 * @LastEditTime 2025-07-02 9:42
 * @Description 设置命令子类-远程关阀传输对象
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class ValveClose {
    public static final long serialVersionUID = 202507020943001L;
    /**
     * 阀控器地址
     */
    @NotBlank(message = "阀控器地址不能为空")
    private String rtuAddr;
    /**
     * 虚拟卡编号
     */
    @NotBlank(message = "虚拟卡编号不能为空")
    private String vcNum;
    /**
     * 开阀时生成的订单号
     */
    @NotBlank(message = "订单号不能为空")
    private String orderNo;
    /**
     * 用户类型 1-平台,2-APP
     */
    //@NotNull(message = "操作类型不能为空")
    //private Integer userType;
    /**
     * 操作人
     */
    @NotNull(message = "操作人不能为空")
    private Long operator;
    /**
     * 开阀类型;1-轮灌
     */
    private Byte openType;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/command/dto/ValveOpen.java
New file
@@ -0,0 +1,32 @@
package com.dy.pipIrrIrrigate.command.dto;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author ZhuBaoMin
 * @date 2025-07-02 9:23
 * @LastEditTime 2025-07-02 9:23
 * @Description 设置命令子类-远程开阀传输对象
 */
@Data
@EqualsAndHashCode(callSuper = false)
public class ValveOpen extends DtoBase {
    public static final long serialVersionUID = 202507020924001L;
    /**
     * 虚拟卡ID
     */
    private Long vcId;
    /**
     * 强制开阀
     */
    private Boolean forceOpen = false;
    /**
     * 开阀类型;1-轮灌
     */
    private Byte openType;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/IrrigateGroupCtrl.java
@@ -541,6 +541,26 @@
    }
    /**
     * 获取项目下轮灌组
     * @param qo
     * @return
     */
    @GetMapping(path = "/getGroupsByProjectId")
    @SsoAop()
    public BaseResponse<List<VoGroupSimple>> getGroupsByProjectId(QoGroup qo) {
        if(qo.getProjectId() == null) {
            return BaseResponseUtils.buildErrorMsg("请选择项目");
        }
        try {
            return BaseResponseUtils.buildSuccess(irrigateGroupSv.getGroupsByProjectId(qo));
        } catch (Exception e) {
            log.error("获取轮灌组记录异常", e);
            return BaseResponseUtils.buildException(e.getMessage());
        }
    }
    /**
     * 获取全部轮灌组,新代码
     * @return
     */
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/IrrigateGroupSv.java
@@ -347,6 +347,16 @@
    }
    /**
     * 获取项目下轮灌组
     * @param queryVo
     * @return
     */
    public List<VoGroupSimple> getGroupsByProjectId(QoGroup queryVo) {
        Map<String, Object> params = (Map<String, Object>) PojoUtils.generalize(queryVo);
        return irIrrigateGroupMapper.getSimpleGroups(params);
    }
    /**
     * 查询全部轮灌组
     * @return
     */
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigateGroup/qo/QoGroup.java
@@ -18,4 +18,9 @@
     * 轮灌组编码
     */
    private String groupCode;
    /**
     * 项目ID
     */
    private Long projectId;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigatePlanCtrl.java
New file
@@ -0,0 +1,117 @@
package com.dy.pipIrrIrrigate.irrigatePlan;
import com.dy.common.aop.SsoAop;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.pipIrrIrrigate.irrigatePlan.dto.IrrigatePlan;
import com.dy.pipIrrIrrigate.irrigatePlan.dto.PlanSimple;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Objects;
/**
 * @author ZhuBaoMin
 * @date 2025-06-30 14:58
 * @LastEditTime 2025-06-30 14:58
 * @Description
 */
@Slf4j
@RestController
@RequestMapping(path = "plan")
@RequiredArgsConstructor
public class IrrigatePlanCtrl {
    private final IrrigatePlanSv irrigatePlanSv;
    /**
     * 创建灌溉计划
     * @param planAndSchedule
     * @param bindingResult
     * @return
     */
    @PostMapping(path = "createPlan", consumes = MediaType.APPLICATION_JSON_VALUE)
    @SsoAop()
    public BaseResponse<Boolean> createPlan(@RequestBody @Valid IrrigatePlan planAndSchedule, BindingResult bindingResult){
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Map map_result = irrigatePlanSv.createPlan(planAndSchedule);
        if(map_result.get("success").equals(false)) {
            return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString());
        }
        return BaseResponseUtils.buildSuccess() ;
    }
    /**
     * 删除灌溉计划
     * @param planSimple
     * @param bindingResult
     * @return
     */
    @PostMapping(path = "deletePlan")
    @SsoAop()
    public BaseResponse<Boolean> deletePlan(@RequestBody @Valid PlanSimple planSimple, BindingResult bindingResult) {
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Map map_result = irrigatePlanSv.deletePlan(planSimple);
        if(map_result.get("success").equals(false)) {
            return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString());
        }
        return BaseResponseUtils.buildSuccess() ;
    }
    /**
     * 发布灌溉计划
     * 1. 修改灌溉计划状态为发布状态
     * 2. 添加灌溉计划操作记录
     * 3. 生成开阀计划
     * @param planSimple
     * @param bindingResult
     * @return
     */
    @PostMapping(path = "publishPlan", consumes = MediaType.APPLICATION_JSON_VALUE)
    @SsoAop()
    public BaseResponse<Boolean> publishPlan(@RequestBody @Valid PlanSimple planSimple, BindingResult bindingResult){
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Map map_result = irrigatePlanSv.publishPlan(planSimple);
        if(map_result.get("success").equals(false)) {
            return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString());
        }
        return BaseResponseUtils.buildSuccess() ;
    }
    /**
     * 终止灌溉计划
     * @param planSimple
     * @param bindingResult
     * @return
     */
    @PostMapping(path = "terminatePlan", consumes = MediaType.APPLICATION_JSON_VALUE)
    @SsoAop()
    public BaseResponse<Boolean> terminatePlan(@RequestBody @Valid PlanSimple planSimple, BindingResult bindingResult){
        if(bindingResult != null && bindingResult.hasErrors()){
            return BaseResponseUtils.buildErrorMsg(Objects.requireNonNull(bindingResult.getFieldError()).getDefaultMessage());
        }
        Map map_result = irrigatePlanSv.terminatePlan(planSimple);
        if(map_result.get("success").equals(false)) {
            return BaseResponseUtils.buildErrorMsg(map_result.get("msg").toString());
        }
        return BaseResponseUtils.buildSuccess() ;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigatePlanSv.java
New file
@@ -0,0 +1,650 @@
package com.dy.pipIrrIrrigate.irrigatePlan;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.multiDataSource.DataSourceContext;
import com.dy.common.mw.protocol.Command;
import com.dy.common.mw.protocol.CommandType;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.pipIrrGlobal.daoIr.*;
import com.dy.pipIrrGlobal.daoRm.RmCommandHistoryMapper;
import com.dy.pipIrrGlobal.pojoIr.IrIrrigatePlan;
import com.dy.pipIrrGlobal.pojoIr.IrIrrigateSchedule;
import com.dy.pipIrrGlobal.pojoIr.IrPlanOperate;
import com.dy.pipIrrGlobal.pojoIr.IrPlanSchedule;
import com.dy.pipIrrGlobal.rtuMw.CodeLocal;
import com.dy.pipIrrGlobal.voIr.VoIrrigateSchedule;
import com.dy.pipIrrGlobal.voIr.VoPlanSimple;
import com.dy.pipIrrGlobal.voIr.VoToTerminateIntakes;
import com.dy.pipIrrGlobal.voRm.VoIntakeVc;
import com.dy.pipIrrIrrigate.command.CommandSv;
import com.dy.pipIrrIrrigate.command.dto.AutomaticClose;
import com.dy.pipIrrIrrigate.command.dto.ValveClose;
import com.dy.pipIrrIrrigate.irrigatePlan.dto.IrrigatePlan;
import com.dy.pipIrrIrrigate.irrigatePlan.dto.IrrigateSchedule;
import com.dy.pipIrrIrrigate.irrigatePlan.dto.PlanSimple;
import com.dy.pipIrrIrrigate.irrigatePlan.enums.OperateTypeENUM;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * @author ZhuBaoMin
 * @date 2025-06-30 14:58
 * @LastEditTime 2025-06-30 14:58
 * @Description
 */
@Slf4j
@Service
public class IrrigatePlanSv {
    @Autowired
    private IrIrrigatePlanMapper irrigatePlanMapper;
    @Autowired
    private IrIrrigateScheduleMapper irIrrigateScheduleMapper;
    @Autowired
    private IrPlanScheduleMapper irPlanScheduleMapper;
    @Autowired
    private IrPlanOperateMapper irPlanOperateMapper;
    @Autowired
    private IrGroupIntakeMapper irGroupIntakeMapper;
    @Autowired
    private CommandSv commandSv;
    @Autowired
    private IrIntakeOperateMapper irIntakeOperateMapper;
    @Autowired
    private RmCommandHistoryMapper rmdCommandHistoryMapper;
    protected String comSendUrl;
    private String key_mw = "comSendUrl";
    @Value("${irr.plan.delay:5}")
    private Integer irrPlanDelay;//轮灌中计划开阀的延迟时长
    private static final Integer irrPlanDelayDefault = 5;//轮灌中计划开阀的默认延迟时长
    @Value("${irr.plan.preOpeningTime:10}")
    private Integer preOpeningTime;
    @Value("${irr.plan.planTerminateLimitMinutes:5}")
    private Integer planTerminateLimitMinutes; // 计划终止后限制发布新计划的时间间隔(分钟)
    /**
     * 添加灌溉计划
     *
     * @param po
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Long addIrrigatePlan(IrIrrigatePlan po) {
        irrigatePlanMapper.insert(po);
        return po.getId();
    }
    /**
     * 添加灌溉次序记录
     *
     * @param po
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Long addIrrigateSchedule(IrIrrigateSchedule po) {
        irIrrigateScheduleMapper.insert(po);
        return po.getId();
    }
    /**
     * 添加计划次序关联记录
     *
     * @param po
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Long addPlanSchedule(IrPlanSchedule po) {
        irPlanScheduleMapper.insert(po);
        return po.getId();
    }
    /**
     * 添加灌溉计划操作记录
     *
     * @param po
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Long addPlanOperate(IrPlanOperate po) {
        irPlanOperateMapper.insert(po);
        return po.getId();
    }
    /**
     * 创建灌溉计划
     * 1. 添加灌溉计划
     * 2. 添加灌溉次序
     * 3. 添加灌溉计划操作记录
     * @param planAndSchedule
     * @return
     */
    public Map createPlan(IrrigatePlan planAndSchedule) {
        Map map = new HashMap<>();
        map.put("success", false);
        Long projectId = planAndSchedule.getProjectId();
        Long operatorId = planAndSchedule.getOperatorId();
        Byte startupMode = planAndSchedule.getStartupMode();
        Date planStartTime = planAndSchedule.getPlanStartTime();;
        Date planStopTime = null;
        Integer duration = 0;
        for(IrrigateSchedule schedule : planAndSchedule.getSchedules()){
            duration = duration + schedule.getDuration();
        }
        if(startupMode == 2){
            if(planStartTime == null) {
                map.put("msg", "自动启动模式必须指定计划启动时间");
                map.put("content", null);
                return map;
            }
            LocalDateTime startTime = planStartTime.toInstant().atZone(ZoneId.systemDefault()) .toLocalDateTime();
            LocalDateTime currentTime = LocalDateTime.now();
            currentTime = currentTime.plusHours(8);
            if(!startTime.isAfter(currentTime)) {
                map.put("msg", "启动时间不能在8小时之内");
                map.put("content", null);
                return map;
            }
        }
        IrIrrigatePlan plan = new IrIrrigatePlan();
        plan.setProjectId(projectId);
        plan.setPlanName(planAndSchedule.getPlanName());
        plan.setStartupMode(startupMode);
        plan.setPlanStartTime(planStartTime);
        plan.setDuration(duration);
        plan.setPlanState((byte)1);
        plan.setExecutingState((byte)1);
        plan.setDeleted(0L);
        Long planId = addIrrigatePlan(plan);
        if(planId == null) {
            map.put("msg", "创建灌溉计划失败");
            map.put("content", null);
            return map;
        }
        // 添加灌溉次序及计划次序关联表
        Integer sort = 1;
        for(IrrigateSchedule schedule : planAndSchedule.getSchedules()){
            IrIrrigateSchedule po = new IrIrrigateSchedule();
            po.setGroupId(schedule.getGroupId());
            po.setDuration(schedule.getDuration());
            po.setCurrentState((byte)1);
            Long scheduleId = addIrrigateSchedule(po);
            if(scheduleId == null) {
                map.put("msg", "创建灌溉次序失败");
                map.put("content", null);
                return map;
            }
            IrPlanSchedule planSchedule = new IrPlanSchedule();
            planSchedule.setPlanId(planId);
            planSchedule.setScheduleId(scheduleId);
            planSchedule.setSort(sort++);
            Long planScheduleId = addPlanSchedule(planSchedule);
            if(planScheduleId == null) {
                map.put("msg", "创建灌溉次序失败");
                map.put("content", null);
                return map;
            }
        }
        // 添加灌溉计划操作记录
        IrPlanOperate planOperate  = new IrPlanOperate();
        planOperate.setPlanId(planId);
        planOperate.setOperator(operatorId);
        planOperate.setOperateType(OperateTypeENUM.CREATE.getCode());
        planOperate.setOperateTime(new Date());
        if(addPlanOperate(planOperate) == 0){
            map.put("msg", "添加灌溉计划操作记录失败");
            map.put("content", null);
            return map;
        }
        map.put("success", true);
        map.put("msg", "灌溉计划创建成功");
        map.put("content", null);
        return map;
    }
    /**
     * 删除灌溉计划
     *
     * @param planSimple
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map deletePlan(PlanSimple planSimple) {
        Long planId = planSimple.getPlanId();
        Long operatorId = planSimple.getOperatorId();
        Integer planState = irrigatePlanMapper.getPlanState(planId);
        if (planState == null) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "灌溉计划不存在");
            map.put("content", null);
            return map;
        }
        if (planState != 1) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "灌溉计划非草稿状态,不允许删除");
            map.put("content", null);
            return map;
        }
        try {
            irrigatePlanMapper.deleteByPrimaryKey(planId);
            // 添加灌溉计划操作记录
            IrPlanOperate planOperate = new IrPlanOperate();
            planOperate.setPlanId(planId);
            planOperate.setOperator(operatorId);
            planOperate.setOperateType(OperateTypeENUM.DELETE.getCode());
            planOperate.setOperateTime(new Date());
            addPlanOperate(planOperate);
            Map map = new HashMap<>();
            map.put("success", true);
            map.put("msg", "灌溉项目删除成功");
            map.put("content", null);
            return map;
        } catch (Exception e) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "灌溉项目删除失败");
            map.put("content", null);
            return map;
        }
    }
    /**
     * 根据计划ID获取灌溉次序记录
     *
     * @param planId
     * @return
     */
    public List<VoIrrigateSchedule> getSchedulesByPlanId(Long planId) {
        return irIrrigateScheduleMapper.getSchedulesByPlanId(planId);
    }
    /**
     * 根据灌溉次序ID更新次序开始时间
     *
     * @param scheduleId
     * @param startTime
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer updateScheduleStartTime(Long scheduleId, Date startTime) {
        return irIrrigateScheduleMapper.updateScheduleStartTime(scheduleId, startTime);
    }
    @Transactional(rollbackFor = Exception.class)
    Integer updatePlanTimes(Date planStartTime, Date planEndTime, Long planId) {
        return irrigatePlanMapper.updatePlanTimes(planStartTime, planEndTime, planId);
    }
    /**
     * 根据组ID获取取水口ID集合
     *
     * @param groupId
     * @return
     */
    public List<Long> getIntakeIdsByGroupId(Long groupId) {
        return irGroupIntakeMapper.getIntakeIdsByGroupId(groupId);
    }
    /**
     * 发布灌溉计划
     *
     * @param planSimple
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map publishPlan(PlanSimple planSimple) {
        Long planId = planSimple.getPlanId();
        Long operatorId = planSimple.getOperatorId();
        Byte operateType = 1;
        // 检查当前项目是否存在未完成的灌溉计划
        if (irrigatePlanMapper.hasPlan_CurrentProject(planId) > 0) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "当前项目存在未完成的灌溉计划");
            map.put("content", null);
            return map;
        }
        // 检查其他项目中是否存在未完成的灌溉计划
        if (irrigatePlanMapper.hasPlan_OtherProject(planId) > 0) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "待发布计划的轮灌组在其他项目的灌溉计划中,且该计划尚未完成");
            map.put("content", null);
            return map;
        }
        // 检查5分钟内是否有终止的计划,且轮灌组有重叠
        Long lastTerminatedPlanId = irPlanOperateMapper.getLastTerminatedPlanId(planTerminateLimitMinutes);
        if (lastTerminatedPlanId != null) {
            // 检查当前计划与最近终止计划的轮灌组是否有重叠
            if (irrigatePlanMapper.hasPlanOverlapWithTerminated(planId, lastTerminatedPlanId) > 0) {
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "上一个计划终止后" + planTerminateLimitMinutes + "分钟内不能发布新计划");
                map.put("content", null);
                return map;
            }
        }
        /**
         * 获取灌溉计划信息
         * 更新灌溉计划起止时间及计划状态
         */
        VoPlanSimple plan = irrigatePlanMapper.getPlanSimple(planId);
        if (plan == null) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "您要发布的计划不存在,或该计划已发布");
            map.put("content", null);
            return map;
        }
        Byte startupMode = plan.getStartupMode();
        Integer duration = plan.getDuration();
        Date planStartTime = plan.getPlanStartTime();
        Date planStopTime = null;
        if (startupMode == 1) {
            planStartTime = new Date();
        }
        LocalDateTime startTime = planStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
        if (startupMode == 1) {
            if (irrPlanDelay == null || irrPlanDelay <= 0) {
                irrPlanDelay = irrPlanDelayDefault;
            }
            startTime = startTime.plusMinutes(irrPlanDelay);
        }
        planStartTime = Date.from(startTime.atZone(ZoneId.systemDefault()).toInstant());
        // 更新每个灌溉次序的开始时间并计算最后一组关阀时间
        List<VoIrrigateSchedule> schedules = getSchedulesByPlanId(planId);
        Date scheduleStartTime = null;
        LocalDateTime lastScheduleEndTime = null;
        Integer sort = 0;
        // 第一次遍历:更新每个灌溉次序的开始时间
        for (VoIrrigateSchedule schedule : schedules) {
            if (scheduleStartTime == null) {
                scheduleStartTime = planStartTime;
            }
            this.updateScheduleStartTime(schedule.getScheduleId(), scheduleStartTime);
            // 计算当前次序的结束时间
            LocalDateTime currentScheduleStartTime = scheduleStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            LocalDateTime currentScheduleEndTime = currentScheduleStartTime.plusMinutes(schedule.getDuration());
            // 更新最后一组的结束时间
            lastScheduleEndTime = currentScheduleEndTime;
            // 计算下一组的开始时间
            LocalDateTime LocalscheduleStartTime = scheduleStartTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            LocalscheduleStartTime = LocalscheduleStartTime.plusMinutes(schedule.getDuration());
            LocalscheduleStartTime = LocalscheduleStartTime.minusMinutes(preOpeningTime);
            scheduleStartTime = Date.from(LocalscheduleStartTime.atZone(ZoneId.systemDefault()).toInstant());
            // 确保下一组的开始时间不早于计划开始时间
            if (scheduleStartTime.before(planStartTime)) {
                scheduleStartTime = planStartTime;
            }
        }
        // 根据最后一组关阀时间设置计划结束时间
        if (lastScheduleEndTime != null) {
            planStopTime = Date.from(lastScheduleEndTime.atZone(ZoneId.systemDefault()).toInstant());
        } else {
            // 如果没有灌溉次序,则使用原来的计算方式
            LocalDateTime stopTime = startTime.plusMinutes(duration);
            planStopTime = Date.from(stopTime.atZone(ZoneId.systemDefault()).toInstant());
        }
        // 根据计划ID更新计划信息(起止时间、计划状态)
        if (this.updatePlanTimes(planStartTime, planStopTime, planId) == 0) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "计划信息更新失败");
            map.put("content", null);
            return map;
        }
        // 添加灌溉计划操作记录
        IrPlanOperate planOperate = new IrPlanOperate();
        planOperate.setPlanId(planId);
        planOperate.setOperator(operatorId);
        planOperate.setOperateType(OperateTypeENUM.PUBLISH.getCode());
        planOperate.setOperateTime(new Date());
        if (addPlanOperate(planOperate) == 0) {
            Map map = new HashMap<>();
            map.put("success", false);
            map.put("msg", "添加灌溉计划操作记录失败");
            map.put("content", null);
            return map;
        }
        schedules = getSchedulesByPlanId(planId);
        for (VoIrrigateSchedule schedule : schedules) {
            Long groupId = schedule.getGroupId();
            List<Long> intakeIds = getIntakeIdsByGroupId(groupId);
            if(intakeIds == null || intakeIds.size() <= 0) {
                Map map = new HashMap<>();
                map.put("success", false);
                map.put("msg", "当前轮灌组内没有有效的取水口");
                map.put("content", null);
                return map;
            }
            for (Long intakeId : intakeIds) {
                if (schedule.getDuration() > 0) {
                    // 今发布灌溉时长大于0的
                    AutomaticClose automaticClose = new AutomaticClose();
                    automaticClose.setIntakeId(intakeId);
                    automaticClose.setPlannedOpenTime(schedule.getStartTime());
                    automaticClose.setMinutes(schedule.getDuration());
                    automaticClose.setOperator(operatorId);
                    automaticClose.setOpenType(Byte.valueOf("1"));
                    commandSv.planedOpenTimedClose(automaticClose, planId, operateType, schedule.getStartTime(), schedule.getDuration());
                }
            }
        }
        Map map = new HashMap<>();
        map.put("success", true);
        map.put("msg", "灌溉计划发布成功");
        map.put("content", null);
        return map;
    }
    /**
     * 根据计划ID获取待终止计划的结束时间:未删除、未终止、已发布、当前时间小于计划结束时间
     *
     * @param planId
     * @return
     */
    public Date getToTerminatePlan(Long planId) {
        return irrigatePlanMapper.getToTerminatePlan(planId);
    }
    /**
     * 根据计划ID获取待终止的取水口列表(开阀成功的)
     *
     * @param planId
     * @return
     */
    public List<VoToTerminateIntakes> getToTerminateIntakes(Long planId) {
        return irIntakeOperateMapper.getToTerminateIntakes(planId);
    }
    /**
     * 根据命令日志ID获取取水口及虚拟卡信息,终止灌溉计划时使用,用来执行远程关阀
     *
     * @param commandId
     * @return
     */
    public VoIntakeVc getValveOpen(Long commandId) {
        return rmdCommandHistoryMapper.getValveOpen(commandId);
    }
    /**
     * 修改灌溉计划
     *
     * @param po
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer updatePlan(IrIrrigatePlan po) {
        return irrigatePlanMapper.updateByPrimaryKeySelective(po);
    }
    /**
     * 根据计划ID终止灌溉次序,将灌溉次序的当前状态改为已终止
     *
     * @param planId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Integer terminateSchedule(Long planId) {
        return irIrrigateScheduleMapper.terminateSchedule(planId);
    }
    /**
     * 终止灌溉计划
     * @param planSimple
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Map terminatePlan(PlanSimple planSimple) {
        Map map = new HashMap<>();
        map.put("success", false);
        map.put("content", null);
        Long planId = planSimple.getPlanId();
        Long operatorId = planSimple.getOperatorId();
        // 生成终止开始时间、终止灌溉时长
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String terminateStartTimeStr = LocalDate.now().getYear() + "-01-01 00:00:00";
        Date terminateStartTime = Date.from(LocalDateTime.parse(terminateStartTimeStr, formatter).atZone(ZoneId.systemDefault()).toInstant());
        Integer terminateDuration = 0;
        // 判断该计划是否可执行终止操作:未删除、未终止、已发布、当前时间小于计划结束时间
        Date planStartTime = getToTerminatePlan(planId);
        if(planStartTime == null){
            map.put("msg", "该计划不存在或不支持终止操作");
            return map;
        }
        List<VoToTerminateIntakes> toTerminateIntakes = getToTerminateIntakes(planId);
        if(toTerminateIntakes != null && toTerminateIntakes.size() > 0){
            for(VoToTerminateIntakes toTerminateIntake : toTerminateIntakes){
                Long intakeId = toTerminateIntake.getIntakeId();
                Long commandId = toTerminateIntake.getCommandId();
                Date startTime = toTerminateIntake.getStartTime();
                Date currentTime = new Date();
                if(currentTime.before(startTime)){
                    // 取消(覆盖开阀计划)
                    AutomaticClose automaticClose = new AutomaticClose();
                    automaticClose.setIntakeId(intakeId);
                    automaticClose.setPlannedOpenTime(terminateStartTime);
                    automaticClose.setMinutes(terminateDuration);
                    automaticClose.setOperator(operatorId);
                    automaticClose.setOpenType(Byte.valueOf("1"));
                    commandSv.planedOpenTimedClose(automaticClose, planId, (byte)2, terminateStartTime, terminateDuration);
                }else {
                    // 终止(远程关阀)
                    VoIntakeVc intakeVc = getValveOpen(commandId);
                    String rtuAddr = intakeVc.getRtuAddr();
                    String vcNum = intakeVc.getVcNum();
                    String orderNo = intakeVc.getOrderNo();
                    ValveClose valveClose = new ValveClose();
                    valveClose.setRtuAddr(rtuAddr);
                    valveClose.setVcNum(vcNum);
                    valveClose.setOrderNo(orderNo);
                    valveClose.setOperator(operatorId);
                    valveClose.setOpenType(Byte.valueOf("1"));
                    commandSv.closeWx(valveClose, planId, (byte)3);
                }
            }
        }
        // 终止计划
        IrIrrigatePlan iIrrigatePlan = new IrIrrigatePlan();
        iIrrigatePlan.setId(planId);
        iIrrigatePlan.setExecutingState((byte)3);
        if(updatePlan(iIrrigatePlan) == 0){
            map.put("msg", "终止计划失败");
            return map;
        }
        // 终止灌溉次序
        if(terminateSchedule(planId) == 0){
            map.put("msg", "终止灌溉次序失败");
            return map;
        }
        // 添加终止操作记录
        IrPlanOperate planOperate  = new IrPlanOperate();
        planOperate.setPlanId(planId);
        planOperate.setOperator(operatorId);
        planOperate.setOperateType(OperateTypeENUM.TERMINATE.getCode());
        planOperate.setOperateTime(new Date());
        if(addPlanOperate(planOperate) == 0){
            map.put("msg", "添加终止操作记录失败");
            return map;
        }
        map.put("success", true);
        map.put("msg", "终止灌溉计划成功");
        return map;
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/IrrigationSv.java
New file
@@ -0,0 +1,109 @@
package com.dy.pipIrrIrrigate.irrigatePlan;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.dy.common.multiDataSource.DataSourceContext;
import com.dy.common.mw.protocol.Command;
import com.dy.common.mw.protocol.CommandType;
import com.dy.common.webUtil.BaseResponse;
import com.dy.common.webUtil.BaseResponseUtils;
import com.dy.pipIrrGlobal.rtuMw.CodeLocal;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
/**
 * @author ZhuBaoMin
 * @date 2025-07-02 11:08
 * @LastEditTime 2025-07-02 11:08
 * @Description
 */
@Slf4j
@Service
public class IrrigationSv {
    @Autowired
    private Environment env;
    @Autowired
    private RestTemplate restTemplate;
    private static final String pro_mw = "mw";
    private String key_mw = "comSendUrl";
    public Boolean getOnlineStatus(String rtuAdd) {
        Command com = new Command();
        com.id = Command.defaultId;
        com.code = CodeLocal.onLinePart;
        com.type = CommandType.innerCommand;
        com.setRtuAddr(rtuAdd);
        com.setParam(rtuAdd);
        JSONObject response = (JSONObject) JSON.toJSON(sendCom2Mw(com));
        if (response == null || !response.getString("code").equals("0001") || response.getJSONObject("content").getJSONObject("attachment").size() == 0) {
            return false;
        }
        if(response.getJSONObject("content").getJSONObject("attachment").getBoolean(rtuAdd)) {
            return true;
        }
        else {
            return false;
        }
    }
    public Boolean getOpenCloseStatus(String rtuAdd) {
        Command com = new Command();
        com.id = Command.defaultId;
        com.code = CodeLocal.oneRtuStates;
        com.type = CommandType.innerCommand;
        com.setRtuAddr(rtuAdd);
        com.setParam(rtuAdd);
        JSONObject response = (JSONObject) JSON.toJSON(sendCom2Mw(com));
        if (response == null || !response.getString("code").equals("0001") || response.getJSONObject("content").getJSONObject("attachment") == null) {
            return false;
        }
        // if(response.getJSONObject("content").getJSONObject("attachment").get("valveOpenTrueCloseFalse").equals("true")) {
        if(response.getJSONObject("content").getJSONObject("attachment").getBoolean("valveOpenTrueCloseFalse")) {
            return true;
        }
        else {
            return false;
        }
    }
    /**
     * 发送命令
     *
     * @return
     */
    protected BaseResponse sendCom2Mw(Command com) {
        String url = UriComponentsBuilder.fromUriString(env.getProperty(pro_mw + "." + DataSourceContext.get() + "." + key_mw))
                .build()
                .toUriString();
        HttpHeaders headers = new HttpHeaders();
        HttpEntity<Command> httpEntity = new HttpEntity<>(com, headers);
        ResponseEntity<BaseResponse> response = null;
        try {
            // 通过Post方式调用接口
            response = restTemplate.exchange(url, HttpMethod.POST, httpEntity, BaseResponse.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(response == null) {
            return BaseResponseUtils.buildErrorMsg("中间件调用失败");
        }
        return response.getBody();
    }
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/IrrigatePlan.java
New file
@@ -0,0 +1,55 @@
package com.dy.pipIrrIrrigate.irrigatePlan.dto;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
 * @author ZhuBaoMin
 * @date 2025-06-30 15:15
 * @LastEditTime 2025-06-30 15:15
 * @Description 灌溉计划对象
 */
@Data
public class IrrigatePlan {
    public static final long serialVersionUID = 202506301516001L;
    /**
     * 项目ID
     */
    private Long projectId;
    /**
     * 计划名称
     */
    @NotBlank(message = "计划名称不能为空")
    private String planName;
    /**
     * 计划启动模式;1-手工启动,2-自动启动
     */
    @NotNull(message = "计划启动模式不能为空")
    @Min(value = 1, message = "计划启动模式不能小于1")
    @Max(value = 2, message = "计划启动模式不能大于2")
    private Byte startupMode;
    /**
     * 计划启动时间
     */
    private Date planStartTime;
    /**
     * 操作人ID
     */
    @NotNull(message = "操作人ID不能为空")
    private Long operatorId;
    /**
     * 灌溉次序
     */
    @NotEmpty(message = "灌溉次序不能为空")
    private List<IrrigateSchedule> schedules;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/IrrigateSchedule.java
New file
@@ -0,0 +1,32 @@
package com.dy.pipIrrIrrigate.irrigatePlan.dto;
import lombok.Data;
import java.util.Date;
/**
 * @author ZhuBaoMin
 * @date 2025-06-30 15:17
 * @LastEditTime 2025-06-30 15:17
 * @Description 灌溉次序对象表
 */
@Data
public class IrrigateSchedule {
    public static final long serialVersionUID = 202506301518001L;
    /**
     * 轮灌组ID
     */
    private Long groupId;
    /**
     * 灌溉开始时间
     */
    private Date startTime;
    /**
     * 该轮灌组灌溉时长
     */
    private Integer duration;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/dto/PlanSimple.java
New file
@@ -0,0 +1,28 @@
package com.dy.pipIrrIrrigate.irrigatePlan.dto;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * @author ZhuBaoMin
 * @date 2025-07-01 17:23
 * @LastEditTime 2025-07-01 17:23
 * @Description 灌溉计划简单对象
 */
@Data
public class PlanSimple {
    public static final long serialVersionUID = 202507011724001L;
    /**
     * 灌溉计划
     */
    @NotNull(message = "计划ID不能为空")
    private Long planId;
    /**
     * 操作人ID
     */
    @NotNull(message = "操作人ID不能为空")
    private Long operatorId;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/java/com/dy/pipIrrIrrigate/irrigatePlan/enums/OperateTypeENUM.java
New file
@@ -0,0 +1,25 @@
package com.dy.pipIrrIrrigate.irrigatePlan.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * @author ZhuBaoMin
 * @date 2025-06-30 15:31
 * @LastEditTime 2025-06-30 15:31
 * @Description 灌溉计划操作类型枚举
 */
@Getter
@AllArgsConstructor
public enum OperateTypeENUM {
    CREATE((byte)1, "创建"),
    PUBLISH((byte)2, "发布"),
    EXECUTE((byte)3, "执行"),
    SUSPEND((byte)4, "暂停"),
    TERMINATE((byte)5, "终止"),
    DELETE((byte)6, "删除");
    private final Byte code;
    private final String message;
}
pipIrr-platform/pipIrr-web/pipIrr-web-irrigate/src/main/resources/application-self.yml
@@ -16,3 +16,14 @@
    context-parameters:
      #GenerateIdSetSuffixListener中应用,取值范围是0-99
      idSuffix: ${pipIrr.irrigate.idSuffix}
irr:
  plan:
    delay: 5 #轮灌中计划开阀的延迟时长(分钟)
    preOpeningTime: 10 #提前时间(分钟)
    planTerminateLimitMinutes: 5 #计划终止后限制发布新计划的时间间隔(分钟)
logging:
  level:
    com:
      dy:
        pipIrrGlobal:
          daoIr: trace