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}(给德飞地锁推送下发任务)的**空实现** —— 临时桥接,不是业务代码。
*
*
为什么需要它
*
* 老工程里这个接口由**独立 JVM** {@code zteits-glock-collection} 通过 Dubbo 提供。
* 单 Jar 之后没有 Dubbo 了,但调用方还在:
*
* - {@code com.zteits.ipay.service.apppubpay.busigroup.impl.park.notify.impl.DeFeiNotifyServiceImpl}
* (德飞支付成功通知 → 开锁)
* - {@code com.zteits.ipay.biz.AliWxPayServiceImpl#openTheDfLockForPaid}(0 元支付/时域卡 → 降锁)
*
* 两处都是**裸 {@code @Autowired}(required=true)**,缺 Bean 则整个单 Jar 启动失败
* (实测报错:{@code No qualifying bean of type
* 'com.zteits.clouds.api.service.datacollect.DfGroundLockMsgPushService'},
* 装配链 {@code dealGeoInOutParkDataSchedule → ... → DeFeiNotifyServiceImpl})。
*
* 为什么不用真实实现
*
* 真实实现是 {@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 能起来。
*
* 为什么是「返回失败」而不是「抛异常」
*
* 三个调用点对异常的容忍度不同,**抛异常会改变既有行为**:
*
* - {@code DeFeiNotifyServiceImpl.notify(...)} 外面有 {@code catch (Exception)},
* 抛出只被记成 error 日志,不改流程 —— 抛是安全的。
* - {@code AliWxPayServiceImpl#openTheDfLockForPaid} 的 3 个调用点**只 catch
* {@code InterruptedException}**,且紧跟其后就要抛
* {@code BizException(NOT_FOUND_PAY_ORDER, "您已经支付,请及时离场")}。
* 这里若抛 RuntimeException/BizException,会把那句**面向用户的业务提示**顶掉,
* 变成一个 500 级错误 —— 属于改动支付链路行为,不可接受。
*
* 折中:返回 {@code success=false} 的 {@link BizResult} 并**打 error 日志**。
* 三个调用点都只看日志、不判 {@code isSuccess()},因此**行为与接入真实实现时完全一致**
* (真实实现在消息写成功后同样只返回结果、不做后续判断),同时丢失的链路在日志里一眼可见。
*
* 也**刻意不返回 {@code new BizResult<>(true)}**:那等于谎报「地锁已开」,
* 将来若有调用方开始判返回值,会被静默骗过。
*
*
⚠️ P5 交接(务必执行)
*
* 搬入 {@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 pushGroundLockConfig(DfGroundLockConfigPushRequest request) throws InterruptedException {
warn("pushGroundLockConfig", "配置下发", request == null ? null : request.getPlNo(),
request == null ? null : request.getEqpNo());
return failure();
}
@Override
public BizResult pushGroundLockOpenByPay(DfGroundLockOpenByPayPushRequest request)
throws InterruptedException {
warn("pushGroundLockOpenByPay", "支付开锁", request == null ? null : request.getPlNo(),
request == null ? null : request.getEqpNo());
return failure();
}
@Override
public BizResult 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 failure() {
// 注意:BizResult(ErrorType,String) 会把 success 置为 false(ResultBean 构造函数),
// 且不设置 data —— 不能改写成 new BizResult<>(Boolean.FALSE),那会把 success 置为 true。
return new BizResult<>(ErrorType.PARK_SEND_FAIL, NOT_INTEGRATED);
}
}