liujie
2026-06-09 c96fbe6b0aeb413a17c429133ee6d81d138144b9
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
package com.xiaopeng.account.util;
 
 
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.xiaopeng.account.constant.I18nKeyConstant;
import com.xiaopeng.account.service.IXpAppUserCouponService;
import com.xiaopeng.account.service.impl.XpAppUserCouponServiceImpl;
import com.xiaopeng.api.dto.account.UcUserInfo;
import com.xiaopeng.api.dto.operation.SmsDto;
import com.xiaopeng.api.feignClient.account.UserResourcesClient;
import com.xiaopeng.api.feignClient.account.XpAppUserCouponClient;
import com.xiaopeng.api.feignClient.operation.SmsClient;
import com.xiaopeng.api.feignClient.operation.XpActicityClient;
import com.xiaopeng.api.feignClient.operation.XpCouponClient;
import com.xiaopeng.api.feignClient.order.XpOrderClient;
import com.xiaopeng.api.feignClient.pushMessage.SubscribeMessageClient;
import com.xiaopeng.api.feignClient.pushMessage.WebSocketMessageClient;
import com.xiaopeng.api.model.account.XpAppUserCoupon;
import com.xiaopeng.api.model.operation.XpActicityStrategy;
import com.xiaopeng.api.model.operation.XpActivity;
import com.xiaopeng.api.model.operation.XpCoupon;
import com.xiaopeng.api.vo.operation.SmsVo;
import com.xiaopeng.api.vo.pushMessage.SendCouponMessageVo;
import com.xiaopeng.api.vo.pushMessage.SendSubscribeMessageVo;
import com.xiaopeng.common.core.constant.Constants;
import com.xiaopeng.common.core.domain.R;
import com.xiaopeng.common.core.enums.MessageTemplateEnum;
import com.xiaopeng.common.core.exception.ServiceException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
 
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
 
@Slf4j
@Component
public class UserCouponTask {
 
    @Resource
    private IXpAppUserCouponService xpAppUserCouponService;
 
    @Resource
    private XpActicityClient acticityClient;
 
    @Resource
    private SubscribeMessageClient subscribeMessageClient;
 
    @Resource
    private XpCouponClient xpCouponClient;
 
    @Resource
    private SmsClient smsClient;
 
    @Value("${sms.appid}")
    private String sms_appid;
 
    @Resource
    private UserResourcesClient userResourcesClient;
 
    @Resource
    private WebSocketMessageClient webSocketMessageClient;
 
    private static final ThreadPoolExecutor RETRY_EXECUTOR = new ThreadPoolExecutor(
            4,
            16,
            60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(512),
            new ThreadFactory() {
                private final AtomicInteger counter = new AtomicInteger(0);
                @Override
                public Thread newThread(Runnable r) {
                    Thread t = new Thread(r, "route-push-retry-" + counter.incrementAndGet());
                    t.setDaemon(true);
                    return t;
                }
            },
            new ThreadPoolExecutor.CallerRunsPolicy()
    );
 
 
    /**
     * 定时任务发送优惠券通知
     */
    @Scheduled(fixedDelay = 30000)
    public void taskOrder() {
        List<XpActivity> activityList = acticityClient.getAllEffectiveActivity().getData();
        List<XpActivity> collect1 = activityList.stream().filter(xpActivity -> null != xpActivity.getWechatNotification() && xpActivity.getWechatNotification() == 1).collect(Collectors.toList());
        pushWxCouponMessage(collect1);
        List<XpActivity> collect2 = activityList.stream().filter(xpActivity -> null != xpActivity.getSmsNotification() && xpActivity.getSmsNotification() == 1).collect(Collectors.toList());
        pushSmsCouponMessage(collect2);
        List<XpActivity> collect3 = activityList.stream().filter(xpActivity -> null != xpActivity.getMiniProgramNotification() && xpActivity.getMiniProgramNotification() == 1).collect(Collectors.toList());
        pushMiniProgramCouponMessage(collect3);
        editUserCouponStatus();
    }
 
 
    /**
     * 微信通知
     */
    private void pushWxCouponMessage(List<XpActivity> activityList) {
        log.info("开始执行优惠券微信提醒消息发送 {}", activityList.size());
        for (XpActivity xpActivity : activityList) {
            //微信通知活动开始提醒(0=否,1=是)
            Integer wechatActicityStart = xpActivity.getWechatActicityStart();
            if(null != wechatActicityStart && 1 == wechatActicityStart){
                RETRY_EXECUTOR.execute(() -> {
                    //即将到期提前通知
                    Integer wechatActicityStartMinutes = xpActivity.getWechatActicityStartMinutes();
                    //获取满足微信通知的即将生效的优惠券
                    long time = LocalDateTime.now().plusMinutes(wechatActicityStartMinutes).toEpochSecond(ZoneOffset.of("+8"));
                    List<XpAppUserCoupon> list = xpAppUserCouponService.list(new LambdaUpdateWrapper<XpAppUserCoupon>()
                            .eq(XpAppUserCoupon::getActivityId, xpActivity.getId()).eq(XpAppUserCoupon::getStatus, 1)
                            .last(" and UNIX_TIMESTAMP(effective_time) >= " + time + " and " + time + "> UNIX_TIMESTAMP(effective_time) - 100"));
                    if(list.isEmpty()){
                        return;
                    }
                    log.info("开始执行生效前优惠券微信提醒消息发送");
                    for (XpAppUserCoupon xpAppUserCoupon : list) {
                        XpCoupon coupon = xpCouponClient.getCouponById(xpAppUserCoupon.getCouponId()).getData();
                        SendSubscribeMessageVo vo = new SendSubscribeMessageVo();
                        vo.setUserId(xpAppUserCoupon.getAppUserId());
                        vo.setTemplateId(MessageTemplateEnum.C.getTemplateId());
                        String json = MessageTemplateEnum.C.getRemarks();
                        json = json.replace("活动名称", xpAppUserCoupon.getCouponName());
                        json = json.replace("温馨提示", coupon.getCouponDescription());
                        vo.setJson(json);
                        R<?> r = subscribeMessageClient.sendSubscribeMessage(vo);
                    }
                });
            }
 
            //微信通知即将过期提醒(0=否,1=是)
            Integer wechatAboutExpireReminder = xpActivity.getWechatAboutExpireReminder();
            if(null != wechatAboutExpireReminder && 1 == wechatAboutExpireReminder){
                RETRY_EXECUTOR.execute(() -> {
                    //即将到期提前通知
                    Integer wechatAboutExpireDays = xpActivity.getWechatAboutExpireDays();
                    //微信通知即将过期天数
                    long time = LocalDateTime.now().plusDays(wechatAboutExpireDays).toEpochSecond(ZoneOffset.of("+8"));
                    List<XpAppUserCoupon> list = xpAppUserCouponService.list(new LambdaUpdateWrapper<XpAppUserCoupon>()
                            .eq(XpAppUserCoupon::getActivityId, xpActivity.getId()).eq(XpAppUserCoupon::getStatus, 1)
                            .last(" and UNIX_TIMESTAMP(expiration_time) >= " + time + " and " + time + "> UNIX_TIMESTAMP(expiration_time) - 100"));
                    if(list.isEmpty()){
                        return;
                    }
                    log.info("开始执行优惠券即将过期微信提醒消息发送");
                    for (XpAppUserCoupon xpAppUserCoupon : list) {
                        XpCoupon coupon = xpCouponClient.getCouponById(xpAppUserCoupon.getCouponId()).getData();
                        SendSubscribeMessageVo vo = new SendSubscribeMessageVo();
                        vo.setUserId(xpAppUserCoupon.getAppUserId());
                        vo.setTemplateId(MessageTemplateEnum.F.getTemplateId());
                        String json = MessageTemplateEnum.F.getRemarks();
                        json = json.replace("优惠券名称", xpAppUserCoupon.getCouponName());
                        json = json.replace("券过期时间", xpAppUserCoupon.getExpirationTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")));
                        json = json.replace("使用规则", coupon.getCouponDescription());
                        vo.setJson(json);
                        R<?> r = subscribeMessageClient.sendSubscribeMessage(vo);
                    }
                });
            }
        }
    }
 
 
    /**
     * 短信通知
     * @param activityList
     */
    private void pushSmsCouponMessage(List<XpActivity> activityList) {
        log.info("开始执行优惠券短信提醒消息发送 {}", activityList.size());
        aa: for (XpActivity xpActivity : activityList) {
            Integer now = Integer.valueOf(LocalDateTime.now().format(DateTimeFormatter.ofPattern("HHmm")));
            Integer smsAboutExpireReminder = xpActivity.getSmsAboutExpireReminder();
            if(StringUtils.hasLength(xpActivity.getSmsStartBlockingTime())){
                String[] smsStartBlockingTime = xpActivity.getSmsStartBlockingTime().split(",");
                String[] smsEndBlockingTime = xpActivity.getSmsEndBlockingTime().split(",");
                for (int i = 0; i < smsStartBlockingTime.length; i++) {
                    Integer start = Integer.valueOf(smsStartBlockingTime[i].replace(":", ""));
                    Integer end = Integer.valueOf(smsEndBlockingTime[i].replace(":", ""));
                    if(start <= now && now <= end){
                        log.info("当前时间在短信发送时间区间内,不发送短信");
                        continue aa;
                    }
                }
            }
 
 
            //微信通知活动开始提醒(0=否,1=是)
            Integer smsActicityStart = xpActivity.getSmsActicityStart();
            if(null != smsActicityStart && 1 == smsActicityStart){
                RETRY_EXECUTOR.execute(() -> {
                    //短信通知临近券生效前X分钟通知
                    Integer smsActicityStartMinutes = xpActivity.getSmsActicityStartMinutes();
                    //获取满足微信通知的即将生效的优惠券
                    long time = LocalDateTime.now().plusMinutes(smsActicityStartMinutes).toEpochSecond(ZoneOffset.of("+8"));
                    List<XpAppUserCoupon> list = xpAppUserCouponService.list(new LambdaUpdateWrapper<XpAppUserCoupon>()
                            .eq(XpAppUserCoupon::getActivityId, xpActivity.getId()).eq(XpAppUserCoupon::getStatus, 1)
                            .last(" and UNIX_TIMESTAMP(effective_time) >= " + time + " and " + time + "> UNIX_TIMESTAMP(effective_time) - 100"));
                    if(list.isEmpty()){
                        return;
                    }
                    log.info("开始执行生效前优惠券短信提醒消息发送");
                    for (XpAppUserCoupon xpAppUserCoupon : list) {
                        UcUserInfo ucUserInfo = userResourcesClient.getBaseUserByUid(xpAppUserCoupon.getAppUserId().toString());
                        if(null == ucUserInfo || !StringUtils.hasLength(ucUserInfo.getMobile())){
                            log.info("用户信息不存在,跳过");
                            continue;
                        }
                        XpCoupon coupon = xpCouponClient.getCouponById(xpAppUserCoupon.getCouponId()).getData();
                        SmsVo vo = new SmsVo();
                        vo.setAppId(sms_appid);
                        vo.setPushType(1);
                        vo.setReceivers(ucUserInfo.getMobile());
                        vo.setTemplateCode("RTActivityStartTpl");
                        vo.setAppPushConfigCode("RTActivityStartSms");
                        vo.setParams(new HashMap<>());
                        vo.getParams().put("name", xpAppUserCoupon.getCouponName());
                        SmsDto smsDto = smsClient.pushSms(vo);
                        if(null == smsDto || smsDto.getCode() != 200){
                            log.info("短信发送失败");
                        }
                    }
                });
            }
 
            //短信通知即将过期提醒(0=否,1=是)
            if(null != smsAboutExpireReminder && 1 == smsAboutExpireReminder){
                RETRY_EXECUTOR.execute(() -> {
                    //即将到期提前通知
                    Integer smsAboutExpireDays = xpActivity.getSmsAboutExpireDays();
                    //微信通知即将过期天数
                    long time = LocalDateTime.now().plusDays(smsAboutExpireDays).toEpochSecond(ZoneOffset.of("+8"));
                    List<XpAppUserCoupon> list = xpAppUserCouponService.list(new LambdaUpdateWrapper<XpAppUserCoupon>()
                            .eq(XpAppUserCoupon::getActivityId, xpActivity.getId()).eq(XpAppUserCoupon::getStatus, 1)
                            .last(" and UNIX_TIMESTAMP(expiration_time) >= " + time + " and " + time + "> UNIX_TIMESTAMP(expiration_time) - 100"));
                    if(list.isEmpty()){
                        return;
                    }
                    log.info("开始执行优惠券即将过期短信提醒消息发送");
                    for (XpAppUserCoupon xpAppUserCoupon : list) {
                        UcUserInfo ucUserInfo = userResourcesClient.getBaseUserByUid(xpAppUserCoupon.getAppUserId().toString());
                        if(null == ucUserInfo || !StringUtils.hasLength(ucUserInfo.getMobile())){
                            log.info("用户信息不存在,跳过");
                            continue;
                        }
                        SmsVo vo = new SmsVo();
                        vo.setAppId(sms_appid);
                        vo.setPushType(1);
                        vo.setReceivers(ucUserInfo.getMobile());
                        vo.setTemplateCode("RTCouponExpireSoonTpl");
                        vo.setAppPushConfigCode("RTCouponExpireSoonSms");
                        vo.setParams(new HashMap<>());
                        vo.getParams().put("name", xpAppUserCoupon.getCouponName());
                        SmsDto smsDto = smsClient.pushSms(vo);
                        if(null == smsDto || smsDto.getCode() != 200){
                            log.info("短信发送失败");
                        }
                    }
                });
            }
        }
 
 
    }
 
 
    /**
     * 小程序通知
     * @param activityList
     */
    private void pushMiniProgramCouponMessage(List<XpActivity> activityList) {
        log.info("开始执行优惠券小程序提醒消息发送 {}", activityList.size());
        for (XpActivity xpActivity : activityList) {
            //小程序弹窗券到账提醒(0=否,1=是)
            Integer miniProgramReceivedReminder = xpActivity.getMiniProgramReceivedReminder();
            String expireMinutesNotificationContent = xpActivity.getExpireMinutesNotificationContent();
            if(null != miniProgramReceivedReminder && 1 == miniProgramReceivedReminder){
                RETRY_EXECUTOR.execute(() -> {
                    //即将到期提前通知
                    Integer miniProgramAboutExpireMinutes = xpActivity.getMiniProgramAboutExpireMinutes();
                    //获取满足微信通知的即将生效的优惠券
                    long time = LocalDateTime.now().plusMinutes(miniProgramAboutExpireMinutes).toEpochSecond(ZoneOffset.of("+8"));
                    List<XpAppUserCoupon> list = xpAppUserCouponService.list(new LambdaUpdateWrapper<XpAppUserCoupon>()
                            .eq(XpAppUserCoupon::getActivityId, xpActivity.getId()).eq(XpAppUserCoupon::getStatus, 1)
                            .last(" and UNIX_TIMESTAMP(effective_time) >= " + time + " and " + time + "> UNIX_TIMESTAMP(effective_time) - 100"));
                    if(list.isEmpty()){
                        return;
                    }
                    log.info("开始执行生效前优惠券小程序提醒消息发送");
                    for (XpAppUserCoupon xpAppUserCoupon : list) {
                        SendCouponMessageVo vo = new SendCouponMessageVo();
                        vo.setAppUserId(xpAppUserCoupon.getAppUserId());
                        JSONObject message = new JSONObject();
                        message.put("type", "COUPON_MESSAGE");
                        message.put("content", expireMinutesNotificationContent);
                        message.put("timestamp", System.currentTimeMillis());
                        vo.setMessage(message.toString());
                        webSocketMessageClient.sendCouponMessage(vo);
                    }
                });
            }
 
            //小程序弹窗即将过期提醒(0=否,1=是)
            Integer miniProgramAboutExpireReminder = xpActivity.getMiniProgramAboutExpireReminder();
            String expireDaysNotificationContent = xpActivity.getExpireDaysNotificationContent();
            if(null != miniProgramAboutExpireReminder && 1 == miniProgramAboutExpireReminder){
                RETRY_EXECUTOR.execute(() -> {
                    //即将到期提前通知
                    Integer aboutExpireDays = xpActivity.getAboutExpireDays();
                    //微信通知即将过期天数
                    long time = LocalDateTime.now().plusDays(aboutExpireDays).toEpochSecond(ZoneOffset.of("+8"));
                    List<XpAppUserCoupon> list = xpAppUserCouponService.list(new LambdaUpdateWrapper<XpAppUserCoupon>()
                            .eq(XpAppUserCoupon::getActivityId, xpActivity.getId()).eq(XpAppUserCoupon::getStatus, 1)
                            .last(" and UNIX_TIMESTAMP(expiration_time) >= " + time + " and " + time + "> UNIX_TIMESTAMP(expiration_time) - 100"));
                    if(list.isEmpty()){
                        return;
                    }
                    log.info("开始执行优惠券即将过期小程序提醒消息发送");
                    for (XpAppUserCoupon xpAppUserCoupon : list) {
                        SendCouponMessageVo vo = new SendCouponMessageVo();
                        vo.setAppUserId(xpAppUserCoupon.getAppUserId());
                        JSONObject message = new JSONObject();
                        message.put("type", "COUPON_MESSAGE");
                        message.put("content", expireDaysNotificationContent);
                        message.put("timestamp", System.currentTimeMillis());
                        vo.setMessage(message.toString());
                        webSocketMessageClient.sendCouponMessage(vo);
                    }
                });
            }
        }
    }
 
 
 
    private void editUserCouponStatus(){
        log.info("开始执行编辑用户优惠券状态");
        RETRY_EXECUTOR.execute(() -> {
            xpAppUserCouponService.update(new LambdaUpdateWrapper<XpAppUserCoupon>()
                    .set(XpAppUserCoupon::getStatus, 3)
                    .eq(XpAppUserCoupon::getStatus, 1)
                    .le(XpAppUserCoupon::getExpirationTime, LocalDateTime.now()));
        });
    }
}