package com.ruoyi.publicity.api; import com.ruoyi.common.core.service.PublicityApi; import com.ruoyi.publicity.config.PublicityProperties; import com.ruoyi.publicity.config.PublicitySwitch; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * 宣教模块对外门面(常驻 Bean),实现 ruoyi-common 里的 {@link PublicityApi}。 * *
* ⚠️ 必须显式标注 {@code @Autowired}:本类有两个公开构造器,
* 不标注会因"构造器不唯一"退回无参构造器 → 启动报 {@code No default constructor found}。
* —— 满意度模块实机踩到的坑,此处刻意规避。
*/
@Autowired
public PublicityFacade(PublicityProperties properties,
PublicitySwitch publicitySwitch,
ObjectProvider
* ⚠️ 必须排除"自己":本类自身也实现了 {@link PublicityApi},
* Spring 按类型查找时若没有别的实现,会把自引用当兜底候选返回,
* 于是 {@code delegate.dealInHospPublicity(...)} 变成无限递归 → {@code StackOverflowError}。
*
* 用 {@code stream()} 而不是 {@code getIfAvailable()}:后者候选 >1 时抛
* {@code NoUniqueBeanDefinitionException},会被吞成 null,掩盖配置错误。
*/
private PublicityApi delegateOrNull() {
if (apiProvider == null) {
return null;
}
try {
return apiProvider.stream()
.filter(api -> api != this)
.findFirst()
.orElse(null);
} catch (Exception e) {
log.warn("【宣教】获取内部实现失败,按未启用处理:{}", e.toString());
return null;
}
}
/**
* 宣教模块是否可用(开关打开 且 实现存在)
*/
public boolean isEnabled() {
return publicitySwitch.isEnabled() && delegateOrNull() != null;
}
@Override
public int dealInHospPublicity(String config) {
if (!isEnabled()) {
log.warn("【宣教】模块未启用(参数配置 publicity.enabled=false),入院宣教跳过。config={}", config);
return 0;
}
return delegateOrNull().dealInHospPublicity(config);
}
@Override
public int dealMedicalPublicity(String config) {
if (!isEnabled()) {
log.warn("【宣教】模块未启用(参数配置 publicity.enabled=false),药品宣教跳过。config={}", config);
return 0;
}
return delegateOrNull().dealMedicalPublicity(config);
}
/**
* 固定值的 {@code ObjectProvider} 实现:仅用于非 Spring 环境/测试
*/
private static final class FixedProvider implements ObjectProvider