|
|
@@ -16,6 +16,7 @@ import com.haha.common.enums.NotifyType;
|
|
|
import com.haha.common.enums.RecognizeActionType;
|
|
|
import com.haha.common.enums.RecognizeConsumeType;
|
|
|
import com.haha.entity.Order;
|
|
|
+import com.haha.sdk.HahaClient;
|
|
|
import com.haha.service.DeviceService;
|
|
|
import com.haha.service.OrderService;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
@@ -71,6 +72,9 @@ public class CallbackController {
|
|
|
@Autowired
|
|
|
private StringRedisTemplate redisTemplate;
|
|
|
|
|
|
+ @Autowired
|
|
|
+ private HahaClient hahaClient;
|
|
|
+
|
|
|
@Value("${haha.api.app-secret}")
|
|
|
private String appSecret;
|
|
|
|
|
|
@@ -310,6 +314,7 @@ public class CallbackController {
|
|
|
* 处理AI识别结果通知 (ORC_RESULT) - 最重要的回调
|
|
|
*
|
|
|
* 用户关门后,AI识别完成,哈哈平台推送识别结果
|
|
|
+ * 包含重复回调检测和并发安全性处理
|
|
|
*
|
|
|
* @param params 通知参数
|
|
|
*/
|
|
|
@@ -318,11 +323,7 @@ public class CallbackController {
|
|
|
String activityId = (String) params.get("activity_id");
|
|
|
String deviceId = (String) params.get("device_id");
|
|
|
String userId = (String) params.get("out_user_id");
|
|
|
-
|
|
|
- // 安全转换 nobuy 字段(兼容字符串和整型)
|
|
|
Integer nobuy = parseInteger(params.get("nobuy"));
|
|
|
-
|
|
|
- // 解析识别结果
|
|
|
String resultStr = (String) params.get("result");
|
|
|
String skuListStr = (String) params.get("sku_list");
|
|
|
String resourceInfoStr = (String) params.get("resource_info");
|
|
|
@@ -330,7 +331,7 @@ public class CallbackController {
|
|
|
log.info("AI识别结果通知 - 设备: {}, 活动: {}, 是否消费: {}",
|
|
|
deviceId, activityId, RecognizeConsumeType.isNoConsume(nobuy) ? "无消费" : "有消费");
|
|
|
|
|
|
- // 保存识别结果到Redis,供小程序轮询查询
|
|
|
+ // 保存识别结果到Redis(始终更新缓存)
|
|
|
String resultKey = RECOGNIZE_RESULT_KEY + activityId;
|
|
|
Map<String, String> recognizeData = new HashMap<>();
|
|
|
recognizeData.put("activityId", activityId != null ? activityId : "");
|
|
|
@@ -350,6 +351,33 @@ public class CallbackController {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ // 解析 confidence
|
|
|
+ BigDecimal confidence = null;
|
|
|
+ if (params.get("confidence") != null) {
|
|
|
+ try {
|
|
|
+ confidence = new BigDecimal(params.get("confidence").toString());
|
|
|
+ } catch (NumberFormatException ignored) {}
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重复回调检测:检查是否已存在该activityId的订单
|
|
|
+ Order existingOrder = orderService.getOrderByActivityId(activityId);
|
|
|
+ if (existingOrder != null) {
|
|
|
+ log.info("识别结果回调重复,订单已存在 - activityId: {}, orderId: {}", activityId, existingOrder.getId());
|
|
|
+ // 更新Redis中的订单信息
|
|
|
+ String orderKey = ORDER_INFO_KEY + activityId;
|
|
|
+ Map<String, String> orderData = new HashMap<>();
|
|
|
+ orderData.put("orderId", existingOrder.getOrderNo() != null ? existingOrder.getOrderNo() : "");
|
|
|
+ orderData.put("activityId", activityId != null ? activityId : "");
|
|
|
+ orderData.put("deviceId", existingOrder.getDeviceId() != null ? existingOrder.getDeviceId() : "");
|
|
|
+ orderData.put("userId", existingOrder.getUserId() != null ? existingOrder.getUserId().toString() : "");
|
|
|
+ orderData.put("totalAmount", existingOrder.getTotalAmount() != null ? existingOrder.getTotalAmount().toString() : "0");
|
|
|
+ orderData.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
|
|
+ redisTemplate.opsForHash().putAll(orderKey, orderData);
|
|
|
+ redisTemplate.expire(orderKey, 30, TimeUnit.MINUTES);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析识别结果详情(保持原有逻辑)
|
|
|
if (resultStr != null && !resultStr.isEmpty()) {
|
|
|
JSONObject result = JSON.parseObject(resultStr);
|
|
|
String type = result.getString("type");
|
|
|
@@ -379,15 +407,7 @@ public class CallbackController {
|
|
|
log.info("视频URL: {}", videoUrl);
|
|
|
}
|
|
|
|
|
|
- // 解析 confidence
|
|
|
- BigDecimal confidence = null;
|
|
|
- if (params.get("confidence") != null) {
|
|
|
- try {
|
|
|
- confidence = new BigDecimal(params.get("confidence").toString());
|
|
|
- } catch (NumberFormatException ignored) {}
|
|
|
- }
|
|
|
-
|
|
|
- // 委托 Service 层创建订单
|
|
|
+ // 委托 Service 层创建订单(Service层已有幂等性检查)
|
|
|
Order order = orderService.createOrderFromRecognition(
|
|
|
activityId, deviceId, userId, skuListStr, resourceInfoStr, confidence);
|
|
|
|
|
|
@@ -395,12 +415,6 @@ public class CallbackController {
|
|
|
log.info("AI识别订单创建成功 - 订单ID: {}", order.getId());
|
|
|
}
|
|
|
|
|
|
- // 3. 优惠信息处理
|
|
|
- // TODO: 根据业务需求处理优惠券、折扣等
|
|
|
-
|
|
|
- // 4. 触发支付流程
|
|
|
- // TODO: 根据业务需求触发支付
|
|
|
-
|
|
|
} catch (Exception e) {
|
|
|
log.error("处理AI识别结果通知失败", e);
|
|
|
}
|
|
|
@@ -445,17 +459,47 @@ public class CallbackController {
|
|
|
log.info("订单信息 - 订单号: {}, 设备: {}, 活动: {}, 用户: {}, 金额: {}",
|
|
|
orderId, deviceId, activityId, userId, orderMoney);
|
|
|
|
|
|
- // 3. 查找本地订单记录
|
|
|
- Order localOrder = orderService.lambdaQuery()
|
|
|
- .eq(Order::getUserId, Long.parseLong(userId))
|
|
|
- .eq(Order::getDeviceId, deviceId)
|
|
|
- .eq(Order::getStatus, OrderConstants.STATUS_PENDING_PAYMENT)
|
|
|
- .orderByDesc(Order::getCreateTime)
|
|
|
- .last("LIMIT 1")
|
|
|
- .one();
|
|
|
+ // 3. 查找本地订单记录(优先使用activityId查询)
|
|
|
+ Order localOrder = null;
|
|
|
+ if (activityId != null && !activityId.isEmpty()) {
|
|
|
+ localOrder = orderService.getOrderByActivityId(activityId);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (localOrder == null && userId != null && deviceId != null) {
|
|
|
+ // 订单不存在,可能是订单回调先于识别回调到达
|
|
|
+ // 主动调用SDK获取识别结果
|
|
|
+ log.info("订单不存在,主动查询识别结果 - activityId: {}", activityId);
|
|
|
+ try {
|
|
|
+ Map<String, Object> recognizeResult = hahaClient.getOrderApi().getRecognizeResult(null, activityId);
|
|
|
+ if (recognizeResult != null) {
|
|
|
+ // 解析识别结果并创建订单
|
|
|
+ String skuListStr = (String) recognizeResult.get("sku_list");
|
|
|
+ String resourceInfoStr = (String) recognizeResult.get("resource_info");
|
|
|
+ Object confidenceObj = recognizeResult.get("confidence");
|
|
|
+ BigDecimal confidence = confidenceObj != null ? new BigDecimal(confidenceObj.toString()) : null;
|
|
|
+
|
|
|
+ localOrder = orderService.createOrderFromRecognition(
|
|
|
+ activityId, deviceId, userId, skuListStr, resourceInfoStr, confidence);
|
|
|
+
|
|
|
+ if (localOrder != null) {
|
|
|
+ log.info("主动创建订单成功 - orderId: {}, activityId: {}", localOrder.getId(), activityId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("主动查询识别结果失败 - activityId: {}", activityId, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
if (localOrder == null) {
|
|
|
- log.warn("未找到对应的本地订单记录,用户: {}, 设备: {}", userId, deviceId);
|
|
|
+ log.warn("无法创建订单 - 缺少必要信息: userId={}, deviceId={}, activityId={}", userId, deviceId, activityId);
|
|
|
+ return "success";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否已经处理过该订单(幂等性)
|
|
|
+ if (orderId != null && orderId.equals(localOrder.getOrderNo())) {
|
|
|
+ log.info("订单已处理过,跳过更新 - orderId: {}", orderId);
|
|
|
+ // 仍然更新Redis缓存
|
|
|
+ saveOrderInfoToRedis(localOrder, activityId, orderId, orderMoney, orderDetail);
|
|
|
return "success";
|
|
|
}
|
|
|
|
|
|
@@ -481,19 +525,7 @@ public class CallbackController {
|
|
|
}
|
|
|
|
|
|
// 保存订单信息到Redis,供小程序轮询查询
|
|
|
- String orderKey = ORDER_INFO_KEY + activityId;
|
|
|
- Map<String, String> orderData = new HashMap<>();
|
|
|
- orderData.put("orderId", orderId != null ? orderId : "");
|
|
|
- orderData.put("activityId", activityId != null ? activityId : "");
|
|
|
- orderData.put("deviceId", deviceId != null ? deviceId : "");
|
|
|
- orderData.put("userId", userId != null ? userId : "");
|
|
|
- orderData.put("orderName", orderName != null ? orderName : "");
|
|
|
- orderData.put("totalAmount", orderMoney != null ? orderMoney.toString() : "0");
|
|
|
- orderData.put("products", orderDetail != null ? orderDetail : "");
|
|
|
- orderData.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
|
|
-
|
|
|
- redisTemplate.opsForHash().putAll(orderKey, orderData);
|
|
|
- redisTemplate.expire(orderKey, 30, TimeUnit.MINUTES);
|
|
|
+ saveOrderInfoToRedis(localOrder, activityId, orderId, orderMoney, orderDetail);
|
|
|
|
|
|
// 5. TODO: 根据业务需要进行后续处理
|
|
|
// - 触发支付流程
|
|
|
@@ -602,4 +634,28 @@ public class CallbackController {
|
|
|
// 4. MD5加密
|
|
|
// 5. 比对签名
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 保存订单信息到Redis
|
|
|
+ *
|
|
|
+ * @param order 订单对象
|
|
|
+ * @param activityId 活动ID
|
|
|
+ * @param orderId 订单号
|
|
|
+ * @param orderMoney 订单金额
|
|
|
+ * @param orderDetail 订单明细
|
|
|
+ */
|
|
|
+ private void saveOrderInfoToRedis(Order order, String activityId, String orderId, Object orderMoney, String orderDetail) {
|
|
|
+ String orderKey = ORDER_INFO_KEY + activityId;
|
|
|
+ Map<String, String> orderData = new HashMap<>();
|
|
|
+ orderData.put("orderId", orderId != null ? orderId : "");
|
|
|
+ orderData.put("activityId", activityId != null ? activityId : "");
|
|
|
+ orderData.put("deviceId", order.getDeviceId() != null ? order.getDeviceId() : "");
|
|
|
+ orderData.put("userId", order.getUserId() != null ? order.getUserId().toString() : "");
|
|
|
+ orderData.put("totalAmount", orderMoney != null ? orderMoney.toString() : "0");
|
|
|
+ orderData.put("products", orderDetail != null ? orderDetail : "");
|
|
|
+ orderData.put("timestamp", String.valueOf(System.currentTimeMillis()));
|
|
|
+
|
|
|
+ redisTemplate.opsForHash().putAll(orderKey, orderData);
|
|
|
+ redisTemplate.expire(orderKey, 30, TimeUnit.MINUTES);
|
|
|
+ }
|
|
|
}
|