|
@@ -0,0 +1,176 @@
|
|
|
|
|
+package com.etotem.cfc.service;
|
|
|
|
|
+
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.etotem.cfc.entity.Product;
|
|
|
|
|
+import com.etotem.cfc.entity.ProductOrder;
|
|
|
|
|
+import com.etotem.cfc.entity.RepurchaseReminderConfig;
|
|
|
|
|
+import com.etotem.cfc.entity.RepurchaseReminderRecord;
|
|
|
|
|
+import com.etotem.cfc.mapper.ProductMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.ProductOrderMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.RepurchaseReminderConfigMapper;
|
|
|
|
|
+import com.etotem.cfc.mapper.RepurchaseReminderRecordMapper;
|
|
|
|
|
+import org.slf4j.Logger;
|
|
|
|
|
+import org.slf4j.LoggerFactory;
|
|
|
|
|
+import org.springframework.scheduling.annotation.Scheduled;
|
|
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
+
|
|
|
|
|
+import javax.annotation.Resource;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+
|
|
|
|
|
+@Service
|
|
|
|
|
+public class RepurchaseReminderService {
|
|
|
|
|
+
|
|
|
|
|
+ private static final Logger log = LoggerFactory.getLogger(RepurchaseReminderService.class);
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ProductOrderMapper productOrderMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ProductMapper productMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private RepurchaseReminderConfigMapper reminderConfigMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private RepurchaseReminderRecordMapper reminderRecordMapper;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 每日 09:00 扫描到期复购提醒
|
|
|
|
|
+ */
|
|
|
|
|
+ @Scheduled(cron = "0 0 9 * * ?")
|
|
|
|
|
+ public void scanAndCreateReminders() {
|
|
|
|
|
+ log.info("开始扫描复购提醒...");
|
|
|
|
|
+
|
|
|
|
|
+ // 1. 查询 30~60 天前已完成的订单
|
|
|
|
|
+ Calendar cal = Calendar.getInstance();
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_YEAR, -60);
|
|
|
|
|
+ Date fromDate = cal.getTime();
|
|
|
|
|
+ cal.add(Calendar.DAY_OF_YEAR, 30);
|
|
|
|
|
+ Date toDate = cal.getTime();
|
|
|
|
|
+
|
|
|
|
|
+ List<ProductOrder> orders = productOrderMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<ProductOrder>()
|
|
|
|
|
+ .eq(ProductOrder::getStatus, "completed")
|
|
|
|
|
+ .ge(ProductOrder::getCreatedAt, fromDate)
|
|
|
|
|
+ .lt(ProductOrder::getCreatedAt, toDate));
|
|
|
|
|
+
|
|
|
|
|
+ if (orders.isEmpty()) {
|
|
|
|
|
+ log.info("无到期复购订单");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 加载所有启用的 config
|
|
|
|
|
+ List<RepurchaseReminderConfig> configs = reminderConfigMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<RepurchaseReminderConfig>()
|
|
|
|
|
+ .eq(RepurchaseReminderConfig::getEnabled, 1));
|
|
|
|
|
+
|
|
|
|
|
+ if (configs.isEmpty()) {
|
|
|
|
|
+ log.info("无复购提醒配置");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 3. 为每个订单匹配 config 并创建提醒
|
|
|
|
|
+ int created = 0;
|
|
|
|
|
+ for (ProductOrder order : orders) {
|
|
|
|
|
+ for (RepurchaseReminderConfig config : configs) {
|
|
|
|
|
+ if (!matchesConfig(order, config)) continue;
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否已发送过 reminder 且未超出最大次数
|
|
|
|
|
+ Long count = reminderRecordMapper.selectCount(
|
|
|
|
|
+ new LambdaQueryWrapper<RepurchaseReminderRecord>()
|
|
|
|
|
+ .eq(RepurchaseReminderRecord::getUserId, order.getBuyerId())
|
|
|
|
|
+ .eq(RepurchaseReminderRecord::getProductId, order.getProductId()));
|
|
|
|
|
+
|
|
|
|
|
+ int maxReminders = config.getMaxReminders() != null ? config.getMaxReminders() : 3;
|
|
|
|
|
+ if (count != null && count >= maxReminders) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ RepurchaseReminderRecord record = new RepurchaseReminderRecord();
|
|
|
|
|
+ record.setUserId(order.getBuyerId());
|
|
|
|
|
+ record.setProductId(order.getProductId());
|
|
|
|
|
+ record.setOrderId(order.getId());
|
|
|
|
|
+ record.setReminderDays(config.getReminderDays() != null ? config.getReminderDays() : 30);
|
|
|
|
|
+ record.setSentAt(new Date());
|
|
|
|
|
+ record.setClicked(0);
|
|
|
|
|
+ record.setPurchased(0);
|
|
|
|
|
+ reminderRecordMapper.insert(record);
|
|
|
|
|
+ created++;
|
|
|
|
|
+ log.info("已创建复购提醒: userId={}, productId={}", order.getBuyerId(), order.getProductId());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ log.info("复购提醒扫描完成,共创建 {} 条", created);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 检查订单商品是否匹配提醒配置
|
|
|
|
|
+ */
|
|
|
|
|
+ private boolean matchesConfig(ProductOrder order, RepurchaseReminderConfig config) {
|
|
|
|
|
+ if (config.getProductId() != null) {
|
|
|
|
|
+ return config.getProductId().equals(order.getProductId());
|
|
|
|
|
+ }
|
|
|
|
|
+ if (config.getProductCategory() != null) {
|
|
|
|
|
+ Product product = productMapper.selectById(order.getProductId());
|
|
|
|
|
+ return product != null && config.getProductCategory().equals(product.getDomain());
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取用户待处理的复购提醒
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<Map<String, Object>> getPendingReminders(Long userId) {
|
|
|
|
|
+ List<RepurchaseReminderRecord> records = reminderRecordMapper.selectList(
|
|
|
|
|
+ new LambdaQueryWrapper<RepurchaseReminderRecord>()
|
|
|
|
|
+ .eq(RepurchaseReminderRecord::getUserId, userId)
|
|
|
|
|
+ .eq(RepurchaseReminderRecord::getPurchased, 0)
|
|
|
|
|
+ .orderByDesc(RepurchaseReminderRecord::getSentAt));
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> result = new ArrayList<>();
|
|
|
|
|
+ for (RepurchaseReminderRecord record : records) {
|
|
|
|
|
+ Map<String, Object> item = new LinkedHashMap<>();
|
|
|
|
|
+ item.put("id", record.getId());
|
|
|
|
|
+ item.put("productId", record.getProductId());
|
|
|
|
|
+ item.put("reminderDays", record.getReminderDays());
|
|
|
|
|
+ item.put("sentAt", record.getSentAt());
|
|
|
|
|
+ item.put("clicked", record.getClicked());
|
|
|
|
|
+
|
|
|
|
|
+ Product product = productMapper.selectById(record.getProductId());
|
|
|
|
|
+ if (product != null) {
|
|
|
|
|
+ item.put("productName", product.getName());
|
|
|
|
|
+ item.put("coverImage", product.getCoverImage());
|
|
|
|
|
+ item.put("price", product.getPrice());
|
|
|
|
|
+ }
|
|
|
|
|
+ result.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 标记提醒为已点击
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public void onReminderClicked(Long reminderId) {
|
|
|
|
|
+ RepurchaseReminderRecord record = reminderRecordMapper.selectById(reminderId);
|
|
|
|
|
+ if (record != null) {
|
|
|
|
|
+ record.setClicked(1);
|
|
|
|
|
+ reminderRecordMapper.updateById(record);
|
|
|
|
|
+ log.info("复购提醒已点击: id={}", reminderId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 标记提醒为已购买
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional
|
|
|
|
|
+ public void onReminderPurchased(Long reminderId, Long orderId) {
|
|
|
|
|
+ RepurchaseReminderRecord record = reminderRecordMapper.selectById(reminderId);
|
|
|
|
|
+ if (record != null) {
|
|
|
|
|
+ record.setPurchased(1);
|
|
|
|
|
+ reminderRecordMapper.updateById(record);
|
|
|
|
|
+ log.info("复购提醒已购买: id={}, orderId={}", reminderId, orderId);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|