NoopDfGroundLockMsgPushService.java 6.96 KB
package com.zteits.glockcollect.biz;

import com.zteits.clouds.api.apibase.bean.BizResult;
import com.zteits.clouds.api.apibase.constants.ErrorType;
import com.zteits.clouds.api.dto.datacollect.param.df.DfGroundLockConfigPushRequest;
import com.zteits.clouds.api.dto.datacollect.param.df.DfGroundLockControlPushRequest;
import com.zteits.clouds.api.dto.datacollect.param.df.DfGroundLockOpenByPayPushRequest;
import com.zteits.clouds.api.service.datacollect.DfGroundLockMsgPushService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * {@link DfGroundLockMsgPushService}(给德飞地锁推送下发任务)的**空实现** —— 临时桥接,不是业务代码。
 *
 * <h2>为什么需要它</h2>
 *
 * 老工程里这个接口由**独立 JVM** {@code zteits-glock-collection} 通过 Dubbo 提供。
 * 单 Jar 之后没有 Dubbo 了,但调用方还在:
 * <ul>
 *   <li>{@code com.zteits.ipay.service.apppubpay.busigroup.impl.park.notify.impl.DeFeiNotifyServiceImpl}
 *       (德飞支付成功通知 → 开锁)</li>
 *   <li>{@code com.zteits.ipay.biz.AliWxPayServiceImpl#openTheDfLockForPaid}(0 元支付/时域卡 → 降锁)</li>
 * </ul>
 * 两处都是**裸 {@code @Autowired}(required=true)**,缺 Bean 则整个单 Jar 启动失败
 * (实测报错:{@code No qualifying bean of type
 * 'com.zteits.clouds.api.service.datacollect.DfGroundLockMsgPushService'},
 * 装配链 {@code dealGeoInOutParkDataSchedule → ... → DeFeiNotifyServiceImpl})。
 *
 * <h2>为什么不用真实实现</h2>
 *
 * 真实实现是 {@code zteits-glock-collection/src/main/java/com/zteits/glockcollect/biz/
 * DfGroundLockMsgPushServiceImpl.java},它依赖 {@code DFClientMsgPushService} —— 一条
 * **netty 长连接 TCP 客户端**(持有设备 Channel、按设备号找 ChannelHandlerContext)。
 * 该工程用的是 {@code netty 5.0.0.Alpha2},而本工程 Redisson 依赖 netty 4.1.x,
 * 两者不能同进程共存(同名 {@code io.netty.*} 类会互相覆盖)。
 * 德飞地锁属于**设备网关**范畴,随迁它 = 同时解决 netty 版本冲突 + TCP 客户端生命周期,
 * 故整体推迟到设备对接阶段(方案 P5),本轮只保证单 Jar 能起来。
 *
 * <h2>为什么是「返回失败」而不是「抛异常」</h2>
 *
 * 三个调用点对异常的容忍度不同,**抛异常会改变既有行为**:
 * <ul>
 *   <li>{@code DeFeiNotifyServiceImpl.notify(...)} 外面有 {@code catch (Exception)},
 *       抛出只被记成 error 日志,不改流程 —— 抛是安全的。</li>
 *   <li>{@code AliWxPayServiceImpl#openTheDfLockForPaid} 的 3 个调用点**只 catch
 *       {@code InterruptedException}**,且紧跟其后就要抛
 *       {@code BizException(NOT_FOUND_PAY_ORDER, "您已经支付,请及时离场")}。
 *       这里若抛 RuntimeException/BizException,会把那句**面向用户的业务提示**顶掉,
 *       变成一个 500 级错误 —— 属于改动支付链路行为,不可接受。</li>
 * </ul>
 * 折中:返回 {@code success=false} 的 {@link BizResult} 并**打 error 日志**。
 * 三个调用点都只看日志、不判 {@code isSuccess()},因此**行为与接入真实实现时完全一致**
 * (真实实现在消息写成功后同样只返回结果、不做后续判断),同时丢失的链路在日志里一眼可见。
 * <p>
 * 也**刻意不返回 {@code new BizResult<>(true)}**:那等于谎报「地锁已开」,
 * 将来若有调用方开始判返回值,会被静默骗过。
 *
 * <h2>⚠️ P5 交接(务必执行)</h2>
 *
 * 搬入 {@code zteits-glock-collection} 的真实实现后,**必须删除本类**:
 * 本类的 Bean 名刻意取了真实实现的 {@code @Service} 值
 * ({@code com.zteits.glockcollect.biz.DfGroundLockMsgPushServiceImpl}),
 * 两者同时存在会以 {@code ConflictingBeanDefinitionException} 启动失败 ——
 * 这是刻意留的「别忘了删我」的探针,而不是「自动兜底」。
 * 本类所在的 {@code com.zteits.glockcollect} 包已在
 * {@code ParkingServerApplication} 的 {@code @ComponentScan} 里,搬入真实实现无需再改扫描配置。
 *
 * @see com.zteits.clouds.api.service.datacollect.DfGroundLockMsgPushService
 */
@Slf4j
@Service("com.zteits.glockcollect.biz.DfGroundLockMsgPushServiceImpl")
public class NoopDfGroundLockMsgPushService implements DfGroundLockMsgPushService {

    /** 错误信息前缀:在日志/返回体里全局可检索,用于区分「地锁未接入」与「地锁真的发失败了」。 */
    public static final String NOT_INTEGRATED_PREFIX = "[德飞地锁未接入]";

    private static final String NOT_INTEGRATED =
            NOT_INTEGRATED_PREFIX + " 单 Jar 未随迁 zteits-glock-collection(netty 5.0.0.Alpha2 与 "
                    + "Redisson 的 netty 4.1.x 冲突,见类注释),本次下发已丢弃。";

    @Override
    public BizResult<Boolean> pushGroundLockConfig(DfGroundLockConfigPushRequest request) throws InterruptedException {
        warn("pushGroundLockConfig", "配置下发", request == null ? null : request.getPlNo(),
                request == null ? null : request.getEqpNo());
        return failure();
    }

    @Override
    public BizResult<Boolean> pushGroundLockOpenByPay(DfGroundLockOpenByPayPushRequest request)
            throws InterruptedException {
        warn("pushGroundLockOpenByPay", "支付开锁", request == null ? null : request.getPlNo(),
                request == null ? null : request.getEqpNo());
        return failure();
    }

    @Override
    public BizResult<Boolean> pushGroundLockControl(DfGroundLockControlPushRequest request)
            throws InterruptedException {
        warn("pushGroundLockControl", "控制指令", request == null ? null : request.getPlNo(),
                request == null ? null : request.getEqpNo());
        return failure();
    }

    /**
     * 每次下发都按 error 级别记录:这是「地锁不会真的动作」的唯一可见信号,
     * 降级成 warn/info 会让线上问题排查时漏掉。
     * 用 plNo/eqpNo 定位到具体车场与设备(eqpNo 在存量代码里取的是 {@code orderPark.getCarNumber()})。
     */
    private void warn(String method, String action, String plNo, String eqpNo) {
        log.error("{} {}({}) 未真实下发 —— 消息已丢弃。plNo={}, eqpNo={}。"
                        + "依赖地锁的链路(德飞支付成功开锁 / 0 元支付降锁 / 地锁配置与控制)都不会生效;"
                        + "恢复方式见 NoopDfGroundLockMsgPushService 类注释的 P5 交接。",
                NOT_INTEGRATED_PREFIX, method, action, plNo, eqpNo);
    }

    private BizResult<Boolean> failure() {
        // 注意:BizResult(ErrorType,String) 会把 success 置为 false(ResultBean 构造函数),
        // 且不设置 data —— 不能改写成 new BizResult<>(Boolean.FALSE),那会把 success 置为 true。
        return new BizResult<>(ErrorType.PARK_SEND_FAIL, NOT_INTEGRATED);
    }
}