|
|
@@ -0,0 +1,301 @@
|
|
|
+package com.etotem.cfc.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.etotem.cfc.dto.ProcurementTaskDTO;
|
|
|
+import com.etotem.cfc.entity.ProcurementTask;
|
|
|
+import com.etotem.cfc.entity.User;
|
|
|
+import com.etotem.cfc.mapper.ProcurementTaskMapper;
|
|
|
+import com.etotem.cfc.mapper.UserMapper;
|
|
|
+import com.etotem.cfc.service.NotificationDispatchService;
|
|
|
+import com.etotem.cfc.service.ProductService;
|
|
|
+import com.etotem.cfc.service.ProductSkuService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.*;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 采购任务服务
|
|
|
+ * 管理商品组合推荐、用户定制、采购任务全生命周期
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+@Service
|
|
|
+public class ProcurementTaskService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProcurementTaskMapper procurementTaskMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private UserMapper userMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProductService productService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProductSkuService productSkuService;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private NotificationDispatchService notificationDispatchService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建采购任务
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public ProcurementTaskDTO createTask(Long userId, Long familyMemberId, String selectedTier,
|
|
|
+ List<Long> baseSkuIds, List<Long> addedSkuIds,
|
|
|
+ List<Long> removedSkuIds) {
|
|
|
+ List<Long> resolvedBase = resolveSkuIds(baseSkuIds);
|
|
|
+ List<Long> resolvedAdded = resolveSkuIds(addedSkuIds);
|
|
|
+ List<Long> resolvedRemoved = resolveSkuIds(removedSkuIds);
|
|
|
+ List<Long> finalSkuIds = mergeSkus(resolvedBase, resolvedAdded, resolvedRemoved);
|
|
|
+
|
|
|
+ ProcurementTaskDTO task = new ProcurementTaskDTO();
|
|
|
+ task.setTaskId(UUID.randomUUID().toString());
|
|
|
+ task.setUserId(userId);
|
|
|
+ task.setFamilyMemberId(familyMemberId);
|
|
|
+ task.setComboTier(selectedTier);
|
|
|
+ task.setOriginalComboTier(selectedTier);
|
|
|
+ task.setSelectedProductSkuIds(finalSkuIds);
|
|
|
+ task.setCustomAdditionSkuIds(resolvedAdded);
|
|
|
+ task.setCustomRemovalSkuIds(resolvedRemoved);
|
|
|
+ task.setStatus("SELECTED");
|
|
|
+ task.setCreateTime(new Date());
|
|
|
+ task.setMessageReminderSent(false);
|
|
|
+
|
|
|
+ saveTask(task);
|
|
|
+ log.info("创建采购任务: taskId={}, userId={}, tier={}, skus={}",
|
|
|
+ task.getTaskId(), userId, selectedTier, finalSkuIds);
|
|
|
+ return task;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 启动采购流程 - SELECTED → PROCURING → PROCURED → EXECUTING
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public ProcurementTaskDTO startProcurement(String taskId) {
|
|
|
+ ProcurementTaskDTO task = getTaskById(taskId);
|
|
|
+ if (task == null) {
|
|
|
+ throw new RuntimeException("任务不存在");
|
|
|
+ }
|
|
|
+ if (!"SELECTED".equals(task.getStatus())) {
|
|
|
+ throw new RuntimeException("仅 SELECTED 状态的任务可进行采购流程");
|
|
|
+ }
|
|
|
+
|
|
|
+ task.setStatus("PROCURING");
|
|
|
+ updateTask(task);
|
|
|
+
|
|
|
+ // 模拟采购执行(实际应调用供应商API/库存服务)
|
|
|
+ boolean success = executeProcurement(task);
|
|
|
+
|
|
|
+ if (success) {
|
|
|
+ task.setStatus("PROCURED");
|
|
|
+ task.setExecutionTaskId(UUID.randomUUID().toString());
|
|
|
+ task.setStatus("EXECUTING");
|
|
|
+ log.info("采购成功,进入执行阶段: taskId={}, executionTaskId={}", taskId, task.getExecutionTaskId());
|
|
|
+ } else {
|
|
|
+ task.setStatus("CANCELLED");
|
|
|
+ log.warn("采购失败: taskId={}", taskId);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateTask(task);
|
|
|
+ return task;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 完成任务执行 - EXECUTING → COMPLETED,发送消息提醒
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public ProcurementTaskDTO completeExecution(String taskId) {
|
|
|
+ ProcurementTaskDTO task = getTaskById(taskId);
|
|
|
+ if (task == null) {
|
|
|
+ throw new RuntimeException("任务不存在");
|
|
|
+ }
|
|
|
+ if (!"EXECUTING".equals(task.getStatus())) {
|
|
|
+ throw new RuntimeException("仅 EXECUTING 状态的任务可标记为完成");
|
|
|
+ }
|
|
|
+
|
|
|
+ task.setStatus("COMPLETED");
|
|
|
+ task.setCompleteTime(new Date());
|
|
|
+
|
|
|
+ if (task.getMessageReminderSent() == null || !task.getMessageReminderSent()) {
|
|
|
+ sendCompletionReminder(task);
|
|
|
+ task.setMessageReminderSent(true);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateTask(task);
|
|
|
+ log.info("执行任务完成: taskId={}, userId={}", taskId, task.getUserId());
|
|
|
+ return task;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 取消采购任务
|
|
|
+ */
|
|
|
+ @Transactional
|
|
|
+ public ProcurementTaskDTO cancelTask(String taskId) {
|
|
|
+ ProcurementTaskDTO task = getTaskById(taskId);
|
|
|
+ if (task == null) {
|
|
|
+ throw new RuntimeException("任务不存在");
|
|
|
+ }
|
|
|
+ if (!"SELECTED".equals(task.getStatus()) && !"PROCURING".equals(task.getStatus())) {
|
|
|
+ throw new RuntimeException("仅 SELECTED 或 PROCURING 状态的任务可取消");
|
|
|
+ }
|
|
|
+
|
|
|
+ task.setStatus("CANCELLED");
|
|
|
+ task.setCompleteTime(new Date());
|
|
|
+ updateTask(task);
|
|
|
+ log.info("取消采购任务: taskId={}, userId={}", taskId, task.getUserId());
|
|
|
+ return task;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取任务详情
|
|
|
+ */
|
|
|
+ public ProcurementTaskDTO getTaskById(String taskId) {
|
|
|
+ return convertToDTO(procurementTaskMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ProcurementTask>()
|
|
|
+ .eq(ProcurementTask::getTaskId, taskId)));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取用户的采购任务列表
|
|
|
+ */
|
|
|
+ public List<ProcurementTaskDTO> getUserTasks(Long userId) {
|
|
|
+ List<ProcurementTask> tasks = procurementTaskMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<ProcurementTask>()
|
|
|
+ .eq(ProcurementTask::getUserId, userId)
|
|
|
+ .orderByDesc(ProcurementTask::getCreateTime));
|
|
|
+ return tasks.stream().map(this::convertToDTO).collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ /** ---------------------------------------------------------
|
|
|
+ * 内部辅助方法
|
|
|
+ * --------------------------------------------------------- */
|
|
|
+
|
|
|
+ private List<Long> resolveSkuIds(List<Long> skuIds) {
|
|
|
+ if (skuIds == null || skuIds.isEmpty()) return new ArrayList<>();
|
|
|
+ // 过滤无效 ID(<=0)
|
|
|
+ return skuIds.stream()
|
|
|
+ .filter(id -> id != null && id > 0)
|
|
|
+ .distinct()
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Long> mergeSkus(List<Long> base, List<Long> added, List<Long> removed) {
|
|
|
+ Set<Long> result = new LinkedHashSet<>(base);
|
|
|
+ result.addAll(added);
|
|
|
+ result.removeAll(removed);
|
|
|
+ return new ArrayList<>(result);
|
|
|
+ }
|
|
|
+
|
|
|
+ private boolean executeProcurement(ProcurementTaskDTO task) {
|
|
|
+ // 实际应:检查库存、调用供应商API、扣减库存、生成订单等
|
|
|
+ // 简化实现:假设采购成功
|
|
|
+ log.info("执行采购: taskId={}, skus={}", task.getTaskId(), task.getSelectedProductSkuIds());
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void sendCompletionReminder(ProcurementTaskDTO task) {
|
|
|
+ try {
|
|
|
+ Map<String, Object> vars = new HashMap<>();
|
|
|
+ vars.put("comboTier", formatTierLabel(task.getComboTier()));
|
|
|
+ vars.put("productCount", task.getSelectedProductSkuIds() != null ? task.getSelectedProductSkuIds().size() : 0);
|
|
|
+ vars.put("createTime", task.getCreateTime() != null ?
|
|
|
+ new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(task.getCreateTime()) : "");
|
|
|
+ notificationDispatchService.sendByTemplate(
|
|
|
+ "procurement_completed",
|
|
|
+ task.getUserId(),
|
|
|
+ vars,
|
|
|
+ "procurement_task",
|
|
|
+ null,
|
|
|
+ "采购任务完成",
|
|
|
+ "您的采购任务已完成,产品即将送达。请及时查收!"
|
|
|
+ );
|
|
|
+ log.info("发送采购完成提醒: taskId={}, userId={}", task.getTaskId(), task.getUserId());
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.warn("发送采购完成提醒失败: taskId={}, error={}", task.getTaskId(), e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private String formatTierLabel(String tier) {
|
|
|
+ if (tier == null) return "未知";
|
|
|
+ switch (tier) {
|
|
|
+ case "high": return "尊享";
|
|
|
+ case "medium": return "平衡";
|
|
|
+ case "low": return "入门";
|
|
|
+ default: return tier;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private ProcurementTaskDTO convertToDTO(ProcurementTask task) {
|
|
|
+ if (task == null) return null;
|
|
|
+ ProcurementTaskDTO dto = new ProcurementTaskDTO();
|
|
|
+ dto.setTaskId(task.getTaskId());
|
|
|
+ dto.setUserId(task.getUserId());
|
|
|
+ dto.setFamilyMemberId(task.getFamilyMemberId());
|
|
|
+ dto.setComboTier(task.getComboTier());
|
|
|
+ dto.setOriginalComboTier(task.getOriginalComboTier());
|
|
|
+ dto.setSelectedProductSkuIds(fromJson(task.getSelectedProductSkuIds()));
|
|
|
+ dto.setCustomAdditionSkuIds(fromJson(task.getCustomAdditionSkuIds()));
|
|
|
+ dto.setCustomRemovalSkuIds(fromJson(task.getCustomRemovalSkuIds()));
|
|
|
+ dto.setStatus(task.getStatus());
|
|
|
+ dto.setCreateTime(task.getCreateTime());
|
|
|
+ dto.setCompleteTime(task.getCompleteTime());
|
|
|
+ dto.setMessageReminderSent(task.getMessageReminderSent() != null && task.getMessageReminderSent() == 1);
|
|
|
+ dto.setExecutionTaskId(task.getExecutionTaskId());
|
|
|
+ return dto;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void saveTask(ProcurementTaskDTO dto) {
|
|
|
+ ProcurementTask task = new ProcurementTask();
|
|
|
+ task.setTaskId(dto.getTaskId());
|
|
|
+ task.setUserId(dto.getUserId());
|
|
|
+ task.setFamilyMemberId(dto.getFamilyMemberId());
|
|
|
+ task.setComboTier(dto.getComboTier());
|
|
|
+ task.setOriginalComboTier(dto.getOriginalComboTier());
|
|
|
+ task.setSelectedProductSkuIds(toJson(dto.getSelectedProductSkuIds()));
|
|
|
+ task.setCustomAdditionSkuIds(toJson(dto.getCustomAdditionSkuIds()));
|
|
|
+ task.setCustomRemovalSkuIds(toJson(dto.getCustomRemovalSkuIds()));
|
|
|
+ task.setStatus(dto.getStatus());
|
|
|
+ task.setCreateTime(dto.getCreateTime());
|
|
|
+ task.setCompleteTime(dto.getCompleteTime());
|
|
|
+ task.setMessageReminderSent(dto.getMessageReminderSent() != null && dto.getMessageReminderSent() ? 1 : 0);
|
|
|
+ task.setExecutionTaskId(dto.getExecutionTaskId());
|
|
|
+ task.setUpdateTime(new Date());
|
|
|
+ procurementTaskMapper.insert(task);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void updateTask(ProcurementTaskDTO dto) {
|
|
|
+ ProcurementTask task = procurementTaskMapper.selectOne(
|
|
|
+ new LambdaQueryWrapper<ProcurementTask>()
|
|
|
+ .eq(ProcurementTask::getTaskId, dto.getTaskId()));
|
|
|
+ if (task == null) return;
|
|
|
+ task.setComboTier(dto.getComboTier());
|
|
|
+ task.setOriginalComboTier(dto.getOriginalComboTier());
|
|
|
+ task.setSelectedProductSkuIds(toJson(dto.getSelectedProductSkuIds()));
|
|
|
+ task.setCustomAdditionSkuIds(toJson(dto.getCustomAdditionSkuIds()));
|
|
|
+ task.setCustomRemovalSkuIds(toJson(dto.getCustomRemovalSkuIds()));
|
|
|
+ task.setStatus(dto.getStatus());
|
|
|
+ task.setCompleteTime(dto.getCompleteTime());
|
|
|
+ task.setMessageReminderSent(dto.getMessageReminderSent() != null && dto.getMessageReminderSent() ? 1 : 0);
|
|
|
+ task.setExecutionTaskId(dto.getExecutionTaskId());
|
|
|
+ task.setUpdateTime(new Date());
|
|
|
+ procurementTaskMapper.updateById(task);
|
|
|
+ }
|
|
|
+
|
|
|
+ private String toJson(List<Long> list) {
|
|
|
+ if (list == null) return null;
|
|
|
+ return com.alibaba.fastjson.JSON.toJSONString(list);
|
|
|
+ }
|
|
|
+
|
|
|
+ private List<Long> fromJson(String json) {
|
|
|
+ if (json == null || json.isEmpty()) return new ArrayList<>();
|
|
|
+ try {
|
|
|
+ return com.alibaba.fastjson.JSON.parseArray(json, Long.class);
|
|
|
+ } catch (Exception e) {
|
|
|
+ return new ArrayList<>();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|