liusheng
9 天以前 34f78281cf395a24e1f3f1c3b861186ff8a03b7b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
package com.ruoyi.message.config;
 
import com.ruoyi.message.MessageChannel;
import com.ruoyi.message.SendResult;
import com.ruoyi.message.sms.SmsSendRequest;
import com.ruoyi.message.sms.SmsSender;
import com.ruoyi.message.sms.SmsSenderFactory;
import com.ruoyi.message.sms.provider.shiyi.ShiyiSmsUtil;
import com.ruoyi.message.wechat.WechatSendRequest;
import com.ruoyi.message.wechat.WechatSender;
import com.ruoyi.message.wechat.WechatSenderFactory;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertySource;
import org.springframework.core.env.StandardEnvironment;
 
import java.io.IOException;
import java.util.Collections;
import java.util.List;
 
/**
 * 「新增 {@code message.*} 前缀项」的绑定与工厂装配校验(不依赖 JUnit)。
 * <p>
 * 覆盖两件事:
 * <ol>
 *   <li>{@code message.sms.*} / {@code message.wechat.*} 能绑到对应的嵌套字段,未覆盖的项回落默认值;</li>
 *   <li>工厂按 key 路由确实是「调用到对应发送方」——这是四个站点发送链路里本地唯一能验证的一环。</li>
 * </ol>
 */
public final class MessagePrefixBindingCheck {
 
    private static int failed = 0;
 
    private MessagePrefixBindingCheck() {
    }
 
    public static void main(String[] args) throws Exception {
        String[] yaml = {
                "message:",
                "  sms:",
                "    shiyi:",
                "      ws-url: http://his.example.com:7790/MediInfoHis.svc",
                "      default-caozuoydm: OPR001",
                "    messaging-insert:",
                "      ws-url: http://msg.example.com:8081/MessagingInsert.asmx",
                "  wechat:",
                "    shiyi:",
                "      title: 自定义标题",
                "    xinhua:",
                "      service: custom.service",
        };
        ConfigurableEnvironment environment = environment(yaml);
        Binder binder = new Binder(ConfigurationPropertySources.get(environment));
 
        // ---------- 1) 前缀项绑定 ----------
        MessageProperties.Sms sms = binder
                .bind(ConfigurationPropertyName.of("message.sms"), Bindable.of(MessageProperties.Sms.class))
                .orElseGet(MessageProperties.Sms::new);
        MessageProperties.Wechat wechat = binder
                .bind(ConfigurationPropertyName.of("message.wechat"), Bindable.of(MessageProperties.Wechat.class))
                .orElseGet(MessageProperties.Wechat::new);
 
        check("message.sms.shiyi.ws-url", "http://his.example.com:7790/MediInfoHis.svc",
                sms.getShiyi().getWsUrl());
        check("message.sms.shiyi.default-caozuoydm", "OPR001", sms.getShiyi().getDefaultCaozuoydm());
        check("message.sms.messaging-insert.ws-url", "http://msg.example.com:8081/MessagingInsert.asmx",
                sms.getMessagingInsert().getWsUrl());
        check("message.wechat.shiyi.title", "自定义标题", wechat.getShiyi().getTitle());
        check("message.wechat.xinhua.service", "custom.service", wechat.getXinhua().getService());
        // 同组未覆盖项应回落默认值
        check("message.wechat.shiyi.url(回落默认)", "http://192.200.54.14:5003/sfjk/SendMessage",
                wechat.getShiyi().getUrl());
        check("message.sms.shiyi.ws-url 默认值不丢", "http://192.178.0.104:7790/MediInfoHis.svc",
                new MessageProperties.Sms().getShiyi().getWsUrl());
 
        // ---------- 2) 工厂路由确实调到对应发送方 ----------
        SmsSenderFactory smsFactory = new SmsSenderFactory(Collections.<SmsSender>singletonList(new CountingSmsSender()));
        WechatSenderFactory wechatFactory = new WechatSenderFactory(Collections.<WechatSender>singletonList(new CountingWechatSender()));
 
        SendResult smsResult = smsFactory.send("probe-channel", SmsSendRequest.of("13500000001", "内容"));
        check("短信工厂按 key 路由到对应发送方", "true", String.valueOf(smsResult.isOk()));
 
        SendResult wechatResult = wechatFactory.send("probe-channel", new WechatSendRequest());
        check("公众号工厂按 key 路由到对应发送方", "true", String.valueOf(wechatResult.isOk()));
 
        try {
            smsFactory.send("不存在的渠道", SmsSendRequest.of("13500000001", "内容"));
            check("未知渠道应抛 SendException", "抛异常", "没抛");
        } catch (com.ruoyi.message.SendException e) {
            check("未知渠道应抛 SendException", "抛异常", "抛异常");
        }
 
        // ---------- 3) ShiyiSmsUtil 仍能正常构造(保底回调/参数校验行为不丢) ----------
        ShiyiSmsUtil shiyiSmsUtil = new ShiyiSmsUtil(new MessageProperties(new RootMessageProperties()));
        boolean paramChecked = false;
        try {
            shiyiSmsUtil.sendSms(null);
        } catch (IllegalArgumentException e) {
            paramChecked = "短信请求不能为空".equals(e.getMessage());
        }
        check("ShiyiSmsUtil 入参校验仍生效", "true", String.valueOf(paramChecked));
 
        System.out.println();
        if (failed == 0) {
            System.out.println("===== message.* 绑定与工厂路由校验全部通过 =====");
        } else {
            System.out.println("===== message.* 绑定与工厂路由校验失败 " + failed + " 项 =====");
            System.exit(1);
        }
    }
 
    private static ConfigurableEnvironment environment(String[] inlineYaml) throws IOException {
        ConfigurableEnvironment environment = new StandardEnvironment();
        environment.getPropertySources()
                .remove(StandardEnvironment.SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME);
        environment.getPropertySources()
                .remove(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME);
        org.springframework.boot.env.YamlPropertySourceLoader loader =
                new org.springframework.boot.env.YamlPropertySourceLoader();
        List<PropertySource<?>> sources = loader.load("inline.yml",
                new org.springframework.core.io.ByteArrayResource(
                        String.join("\n", inlineYaml).getBytes(java.nio.charset.StandardCharsets.UTF_8)));
        for (PropertySource<?> source : sources) {
            environment.getPropertySources().addLast(source);
        }
        return environment;
    }
 
    private static void check(String key, String expected, String actual) {
        boolean ok = expected.equals(actual);
        if (!ok) {
            failed++;
        }
        System.out.println((ok ? "[OK]   " : "[FAIL] ") + key + "  期望=[" + expected + "]  实际=[" + actual + "]");
    }
 
    /** 探针短信发送方 */
    static class CountingSmsSender implements SmsSender {
        @Override
        public String key() {
            return "probe-channel";
        }
 
        @Override
        public SendResult send(SmsSendRequest request) {
            return SendResult.success(key(), MessageChannel.SMS, "called");
        }
    }
 
    /** 探针公众号发送方 */
    static class CountingWechatSender implements WechatSender {
        @Override
        public String key() {
            return "probe-channel";
        }
 
        @Override
        public SendResult send(WechatSendRequest request) {
            return SendResult.success(key(), MessageChannel.WECHAT, "called");
        }
    }
}