|
@@ -23,9 +23,9 @@ import java.util.Map;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 哈哈平台回调通知接口
|
|
* 哈哈平台回调通知接口
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 接收哈哈平台推送的各类通知,分为两类回调地址:
|
|
* 接收哈哈平台推送的各类通知,分为两类回调地址:
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 1. 消息回调地址 (/callback/haha/message)
|
|
* 1. 消息回调地址 (/callback/haha/message)
|
|
|
* - DEVICE_STATUS: 开关门状态通知
|
|
* - DEVICE_STATUS: 开关门状态通知
|
|
|
* - ONLINE_STATUS: 设备在线状态通知
|
|
* - ONLINE_STATUS: 设备在线状态通知
|
|
@@ -33,11 +33,11 @@ import java.util.Map;
|
|
|
* - CLIENT_NEW_PRODUCT: 新品审核结果回调
|
|
* - CLIENT_NEW_PRODUCT: 新品审核结果回调
|
|
|
* - MERGE_PRODUCT: 商品合并结果通知
|
|
* - MERGE_PRODUCT: 商品合并结果通知
|
|
|
* - ORC_RESULT: AI识别结果通知(最重要)
|
|
* - ORC_RESULT: AI识别结果通知(最重要)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 2. 订单回调地址 (/callback/haha/order)
|
|
* 2. 订单回调地址 (/callback/haha/order)
|
|
|
* - 哈哈平台生成订单后推送的订单信息
|
|
* - 哈哈平台生成订单后推送的订单信息
|
|
|
* - 包含订单号、设备号、用户ID、订单明细、金额等
|
|
* - 包含订单号、设备号、用户ID、订单明细、金额等
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 注意:
|
|
* 注意:
|
|
|
* - 所有通知都包含 sign 字段用于验证
|
|
* - 所有通知都包含 sign 字段用于验证
|
|
|
* - 必须返回 "success" 字符串表示接收成功
|
|
* - 必须返回 "success" 字符串表示接收成功
|
|
@@ -51,16 +51,16 @@ public class CallbackController {
|
|
|
|
|
|
|
|
@Autowired
|
|
@Autowired
|
|
|
private OrderService orderService;
|
|
private OrderService orderService;
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
@Value("${haha.api.app-secret}")
|
|
@Value("${haha.api.app-secret}")
|
|
|
private String appSecret;
|
|
private String appSecret;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 消息回调通知统一入口
|
|
* 消息回调通知统一入口
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 接收哈哈平台推送的所有类型通知,通过 notify_type 区分具体类型
|
|
* 接收哈哈平台推送的所有类型通知,通过 notify_type 区分具体类型
|
|
|
* 支持 application/json 和 application/x-www-form-urlencoded 两种格式
|
|
* 支持 application/json 和 application/x-www-form-urlencoded 两种格式
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param request HTTP请求对象
|
|
* @param request HTTP请求对象
|
|
|
* @param requestParams form表单参数(用于application/x-www-form-urlencoded)
|
|
* @param requestParams form表单参数(用于application/x-www-form-urlencoded)
|
|
|
* @return 响应结果,必须返回 "success" 字符串
|
|
* @return 响应结果,必须返回 "success" 字符串
|
|
@@ -72,21 +72,21 @@ public class CallbackController {
|
|
|
try {
|
|
try {
|
|
|
// 解析请求参数(兼容JSON和表单两种格式)
|
|
// 解析请求参数(兼容JSON和表单两种格式)
|
|
|
Map<String, Object> params = parseRequestParams(request, requestParams);
|
|
Map<String, Object> params = parseRequestParams(request, requestParams);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 记录回调日志
|
|
// 记录回调日志
|
|
|
log.info("收到消息回调通知: {}", JSON.toJSONString(params));
|
|
log.info("收到消息回调通知: {}", JSON.toJSONString(params));
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 1. 验证签名(如果需要)
|
|
// 1. 验证签名(如果需要)
|
|
|
// validateSign(params);
|
|
// validateSign(params);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 2. 获取通知类型
|
|
// 2. 获取通知类型
|
|
|
String notifyType = (String) params.get("notify_type");
|
|
String notifyType = (String) params.get("notify_type");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (notifyType == null || notifyType.isEmpty()) {
|
|
if (notifyType == null || notifyType.isEmpty()) {
|
|
|
log.warn("消息回调缺少 notify_type 参数");
|
|
log.warn("消息回调缺少 notify_type 参数");
|
|
|
return "success";
|
|
return "success";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 3. 根据通知类型分发处理
|
|
// 3. 根据通知类型分发处理
|
|
|
switch (notifyType) {
|
|
switch (notifyType) {
|
|
|
case "DEVICE_STATUS":
|
|
case "DEVICE_STATUS":
|
|
@@ -110,10 +110,10 @@ public class CallbackController {
|
|
|
default:
|
|
default:
|
|
|
log.warn("未知的消息通知类型: {}", notifyType);
|
|
log.warn("未知的消息通知类型: {}", notifyType);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 4. 返回成功响应(必须返回 "success" 字符串)
|
|
// 4. 返回成功响应(必须返回 "success" 字符串)
|
|
|
return "success";
|
|
return "success";
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理消息回调通知失败", e);
|
|
log.error("处理消息回调通知失败", e);
|
|
|
// 即使处理失败,也返回成功,避免重复推送
|
|
// 即使处理失败,也返回成功,避免重复推送
|
|
@@ -123,13 +123,13 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理开关门状态通知 (DEVICE_STATUS)
|
|
* 处理开关门状态通知 (DEVICE_STATUS)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* status取值:
|
|
* status取值:
|
|
|
* - 1=ERROR(开门失败)
|
|
* - 1=ERROR(开门失败)
|
|
|
* - 2=OPENED(门已开)
|
|
* - 2=OPENED(门已开)
|
|
|
* - 3=CLOSED(门已关)
|
|
* - 3=CLOSED(门已关)
|
|
|
* - 4=ANOTHER(设备繁忙)
|
|
* - 4=ANOTHER(设备繁忙)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 通知参数
|
|
* @param params 通知参数
|
|
|
*/
|
|
*/
|
|
|
private void handleDeviceStatus(Map<String, Object> params) {
|
|
private void handleDeviceStatus(Map<String, Object> params) {
|
|
@@ -139,10 +139,10 @@ public class CallbackController {
|
|
|
String status = (String) params.get("status");
|
|
String status = (String) params.get("status");
|
|
|
String openType = (String) params.get("open_type");
|
|
String openType = (String) params.get("open_type");
|
|
|
String outUserId = (String) params.get("out_user_id");
|
|
String outUserId = (String) params.get("out_user_id");
|
|
|
-
|
|
|
|
|
- log.info("开关门状态通知 - 设备: {}, 状态: {}, 类型: {}, 用户: {}",
|
|
|
|
|
|
|
+
|
|
|
|
|
+ log.info("开关门状态通知 - 设备: {}, 状态: {}, 类型: {}, 用户: {}",
|
|
|
deviceId, status, openType, outUserId);
|
|
deviceId, status, openType, outUserId);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 可以根据业务需要处理不同状态
|
|
// 可以根据业务需要处理不同状态
|
|
|
switch (status) {
|
|
switch (status) {
|
|
|
case "2": // OPENED
|
|
case "2": // OPENED
|
|
@@ -158,7 +158,7 @@ public class CallbackController {
|
|
|
log.warn("设备 {} 繁忙", deviceId);
|
|
log.warn("设备 {} 繁忙", deviceId);
|
|
|
break;
|
|
break;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理开关门状态通知失败", e);
|
|
log.error("处理开关门状态通知失败", e);
|
|
|
}
|
|
}
|
|
@@ -166,19 +166,19 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理设备在线状态通知 (ONLINE_STATUS)
|
|
* 处理设备在线状态通知 (ONLINE_STATUS)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 通知参数
|
|
* @param params 通知参数
|
|
|
*/
|
|
*/
|
|
|
private void handleOnlineStatus(Map<String, Object> params) {
|
|
private void handleOnlineStatus(Map<String, Object> params) {
|
|
|
try {
|
|
try {
|
|
|
String deviceId = (String) params.get("device_id");
|
|
String deviceId = (String) params.get("device_id");
|
|
|
Integer isOnline = parseInteger(params.get("is_online"));
|
|
Integer isOnline = parseInteger(params.get("is_online"));
|
|
|
-
|
|
|
|
|
- log.info("设备在线状态通知 - 设备: {}, 在线状态: {}",
|
|
|
|
|
|
|
+
|
|
|
|
|
+ log.info("设备在线状态通知 - 设备: {}, 在线状态: {}",
|
|
|
deviceId, isOnline != null && isOnline == 1 ? "在线" : "离线");
|
|
deviceId, isOnline != null && isOnline == 1 ? "在线" : "离线");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 可以在这里更新设备在线状态到数据库
|
|
// 可以在这里更新设备在线状态到数据库
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理设备在线状态通知失败", e);
|
|
log.error("处理设备在线状态通知失败", e);
|
|
|
}
|
|
}
|
|
@@ -186,16 +186,16 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理音量调节结果通知 (VOICE_RESULT)
|
|
* 处理音量调节结果通知 (VOICE_RESULT)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 通知参数
|
|
* @param params 通知参数
|
|
|
*/
|
|
*/
|
|
|
private void handleVoiceResult(Map<String, Object> params) {
|
|
private void handleVoiceResult(Map<String, Object> params) {
|
|
|
try {
|
|
try {
|
|
|
String deviceId = (String) params.get("device_id");
|
|
String deviceId = (String) params.get("device_id");
|
|
|
Integer voice = parseInteger(params.get("voice"));
|
|
Integer voice = parseInteger(params.get("voice"));
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log.info("音量调节结果通知 - 设备: {}, 音量值: {}", deviceId, voice);
|
|
log.info("音量调节结果通知 - 设备: {}, 音量值: {}", deviceId, voice);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理音量调节结果通知失败", e);
|
|
log.error("处理音量调节结果通知失败", e);
|
|
|
}
|
|
}
|
|
@@ -203,12 +203,12 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理新品审核结果回调 (CLIENT_NEW_PRODUCT)
|
|
* 处理新品审核结果回调 (CLIENT_NEW_PRODUCT)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* status取值:
|
|
* status取值:
|
|
|
* - 1=已通过
|
|
* - 1=已通过
|
|
|
* - 2=已拒绝
|
|
* - 2=已拒绝
|
|
|
* - 3=已上架
|
|
* - 3=已上架
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 通知参数
|
|
* @param params 通知参数
|
|
|
*/
|
|
*/
|
|
|
private void handleNewProductAudit(Map<String, Object> params) {
|
|
private void handleNewProductAudit(Map<String, Object> params) {
|
|
@@ -218,13 +218,13 @@ public class CallbackController {
|
|
|
String name = (String) params.get("name");
|
|
String name = (String) params.get("name");
|
|
|
String code = (String) params.get("code");
|
|
String code = (String) params.get("code");
|
|
|
String rejectReason = (String) params.get("reject_reason");
|
|
String rejectReason = (String) params.get("reject_reason");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log.info("新品审核结果 - ID: {}, 商品名: {}, 状态: {}, code: {}", id, name, status, code);
|
|
log.info("新品审核结果 - ID: {}, 商品名: {}, 状态: {}, code: {}", id, name, status, code);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (status != null && status == 2) {
|
|
if (status != null && status == 2) {
|
|
|
log.warn("新品 {} 被拒绝,原因: {}", name, rejectReason);
|
|
log.warn("新品 {} 被拒绝,原因: {}", name, rejectReason);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理新品审核结果通知失败", e);
|
|
log.error("处理新品审核结果通知失败", e);
|
|
|
}
|
|
}
|
|
@@ -232,17 +232,17 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理商品合并结果通知 (MERGE_PRODUCT)
|
|
* 处理商品合并结果通知 (MERGE_PRODUCT)
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 通知参数
|
|
* @param params 通知参数
|
|
|
*/
|
|
*/
|
|
|
private void handleMergeProduct(Map<String, Object> params) {
|
|
private void handleMergeProduct(Map<String, Object> params) {
|
|
|
try {
|
|
try {
|
|
|
Object listObj = params.get("list");
|
|
Object listObj = params.get("list");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (listObj instanceof JSONArray) {
|
|
if (listObj instanceof JSONArray) {
|
|
|
JSONArray list = (JSONArray) listObj;
|
|
JSONArray list = (JSONArray) listObj;
|
|
|
log.info("商品合并结果通知 - 合并数量: {}", list.size());
|
|
log.info("商品合并结果通知 - 合并数量: {}", list.size());
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
for (Object item : list) {
|
|
for (Object item : list) {
|
|
|
if (item instanceof JSONObject) {
|
|
if (item instanceof JSONObject) {
|
|
|
JSONObject merge = (JSONObject) item;
|
|
JSONObject merge = (JSONObject) item;
|
|
@@ -252,7 +252,7 @@ public class CallbackController {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理商品合并结果通知失败", e);
|
|
log.error("处理商品合并结果通知失败", e);
|
|
|
}
|
|
}
|
|
@@ -260,66 +260,66 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理AI识别结果通知 (ORC_RESULT) - 最重要的回调
|
|
* 处理AI识别结果通知 (ORC_RESULT) - 最重要的回调
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 用户关门后,AI识别完成,哈哈平台推送识别结果
|
|
* 用户关门后,AI识别完成,哈哈平台推送识别结果
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 通知参数
|
|
* @param params 通知参数
|
|
|
*/
|
|
*/
|
|
|
private void handleOrcResult(Map<String, Object> params) {
|
|
private void handleOrcResult(Map<String, Object> params) {
|
|
|
try {
|
|
try {
|
|
|
String activityId = (String) params.get("activity_id");
|
|
String activityId = (String) params.get("activity_id");
|
|
|
String deviceId = (String) params.get("device_id");
|
|
String deviceId = (String) params.get("device_id");
|
|
|
- String outUserId = (String) params.get("out_user_id");
|
|
|
|
|
-
|
|
|
|
|
|
|
+ String userId = (String) params.get("out_user_id");
|
|
|
|
|
+
|
|
|
// 安全转换 nobuy 字段(兼容字符串和整型)
|
|
// 安全转换 nobuy 字段(兼容字符串和整型)
|
|
|
Integer nobuy = parseInteger(params.get("nobuy"));
|
|
Integer nobuy = parseInteger(params.get("nobuy"));
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 解析识别结果
|
|
// 解析识别结果
|
|
|
String resultStr = (String) params.get("result");
|
|
String resultStr = (String) params.get("result");
|
|
|
String skuListStr = (String) params.get("sku_list");
|
|
String skuListStr = (String) params.get("sku_list");
|
|
|
String resourceInfoStr = (String) params.get("resource_info");
|
|
String resourceInfoStr = (String) params.get("resource_info");
|
|
|
-
|
|
|
|
|
- log.info("AI识别结果通知 - 设备: {}, 活动: {}, 是否消费: {}",
|
|
|
|
|
|
|
+
|
|
|
|
|
+ log.info("AI识别结果通知 - 设备: {}, 活动: {}, 是否消费: {}",
|
|
|
deviceId, activityId, nobuy != null && nobuy == 1 ? "无消费" : "有消费");
|
|
deviceId, activityId, nobuy != null && nobuy == 1 ? "无消费" : "有消费");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (nobuy != null && nobuy == 1) {
|
|
if (nobuy != null && nobuy == 1) {
|
|
|
log.info("用户打开柜门但未消费,无需处理");
|
|
log.info("用户打开柜门但未消费,无需处理");
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 解析result字段
|
|
// 解析result字段
|
|
|
if (resultStr != null && !resultStr.isEmpty()) {
|
|
if (resultStr != null && !resultStr.isEmpty()) {
|
|
|
JSONObject result = JSON.parseObject(resultStr);
|
|
JSONObject result = JSON.parseObject(resultStr);
|
|
|
String type = result.getString("type"); // IN或OUT
|
|
String type = result.getString("type"); // IN或OUT
|
|
|
JSONArray data = result.getJSONArray("data");
|
|
JSONArray data = result.getJSONArray("data");
|
|
|
JSONArray excepts = result.getJSONArray("excepts");
|
|
JSONArray excepts = result.getJSONArray("excepts");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log.info("识别类型: {}, 数据: {}", type, data != null ? data.size() : 0);
|
|
log.info("识别类型: {}, 数据: {}", type, data != null ? data.size() : 0);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 检查是否有异常
|
|
// 检查是否有异常
|
|
|
if (excepts != null && !excepts.isEmpty()) {
|
|
if (excepts != null && !excepts.isEmpty()) {
|
|
|
log.warn("识别结果包含异常: {}", excepts.toJSONString());
|
|
log.warn("识别结果包含异常: {}", excepts.toJSONString());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 解析sku_list
|
|
// 解析sku_list
|
|
|
if (skuListStr != null && !skuListStr.isEmpty()) {
|
|
if (skuListStr != null && !skuListStr.isEmpty()) {
|
|
|
JSONArray skuList = JSON.parseArray(skuListStr);
|
|
JSONArray skuList = JSON.parseArray(skuListStr);
|
|
|
log.info("商品数量: {}", skuList != null ? skuList.size() : 0);
|
|
log.info("商品数量: {}", skuList != null ? skuList.size() : 0);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 解析resource_info获取视频URL
|
|
// 解析resource_info获取视频URL
|
|
|
if (resourceInfoStr != null && !resourceInfoStr.isEmpty()) {
|
|
if (resourceInfoStr != null && !resourceInfoStr.isEmpty()) {
|
|
|
JSONObject resourceInfo = JSON.parseObject(resourceInfoStr);
|
|
JSONObject resourceInfo = JSON.parseObject(resourceInfoStr);
|
|
|
String videoUrl = resourceInfo.getString("video_url");
|
|
String videoUrl = resourceInfo.getString("video_url");
|
|
|
log.info("视频URL: {}", videoUrl);
|
|
log.info("视频URL: {}", videoUrl);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// TODO: 根据识别结果进行后续处理
|
|
// TODO: 根据识别结果进行后续处理
|
|
|
// 1. 计算订单金额
|
|
// 1. 计算订单金额
|
|
|
// 2. 生成订单记录
|
|
// 2. 生成订单记录
|
|
|
// 3. 触发支付流程
|
|
// 3. 触发支付流程
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理AI识别结果通知失败", e);
|
|
log.error("处理AI识别结果通知失败", e);
|
|
|
}
|
|
}
|
|
@@ -327,11 +327,11 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 订单回调通知接口
|
|
* 订单回调通知接口
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* 当AI识别完成并生成订单后,哈哈平台会推送订单信息到此接口
|
|
* 当AI识别完成并生成订单后,哈哈平台会推送订单信息到此接口
|
|
|
* 这是独立于消息回调的订单回调接口
|
|
* 这是独立于消息回调的订单回调接口
|
|
|
* 支持 application/json 和 application/x-www-form-urlencoded 两种格式
|
|
* 支持 application/json 和 application/x-www-form-urlencoded 两种格式
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param request HTTP请求对象
|
|
* @param request HTTP请求对象
|
|
|
* @param requestParams form表单参数(用于application/x-www-form-urlencoded)
|
|
* @param requestParams form表单参数(用于application/x-www-form-urlencoded)
|
|
|
* @return 响应结果,必须返回 "success" 字符串
|
|
* @return 响应结果,必须返回 "success" 字符串
|
|
@@ -343,13 +343,13 @@ public class CallbackController {
|
|
|
try {
|
|
try {
|
|
|
// 解析请求参数(兼容JSON和表单两种格式)
|
|
// 解析请求参数(兼容JSON和表单两种格式)
|
|
|
Map<String, Object> params = parseRequestParams(request, requestParams);
|
|
Map<String, Object> params = parseRequestParams(request, requestParams);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 记录回调日志
|
|
// 记录回调日志
|
|
|
log.info("收到订单回调通知: {}", JSON.toJSONString(params));
|
|
log.info("收到订单回调通知: {}", JSON.toJSONString(params));
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 1. 验证签名(如果需要)
|
|
// 1. 验证签名(如果需要)
|
|
|
// validateSign(params);
|
|
// validateSign(params);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 2. 提取订单信息
|
|
// 2. 提取订单信息
|
|
|
String orderId = (String) params.get("order_id");
|
|
String orderId = (String) params.get("order_id");
|
|
|
String deviceId = (String) params.get("device_id");
|
|
String deviceId = (String) params.get("device_id");
|
|
@@ -360,10 +360,10 @@ public class CallbackController {
|
|
|
String from = (String) params.get("from");
|
|
String from = (String) params.get("from");
|
|
|
Object orderMoney = params.get("order_money");
|
|
Object orderMoney = params.get("order_money");
|
|
|
Object createTime = params.get("create_time");
|
|
Object createTime = params.get("create_time");
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
log.info("订单信息 - 订单号: {}, 设备: {}, 活动: {}, 用户: {}, 金额: {}",
|
|
log.info("订单信息 - 订单号: {}, 设备: {}, 活动: {}, 用户: {}, 金额: {}",
|
|
|
orderId, deviceId, activityId, userId, orderMoney);
|
|
orderId, deviceId, activityId, userId, orderMoney);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 3. 查找本地订单记录
|
|
// 3. 查找本地订单记录
|
|
|
Order localOrder = orderService.lambdaQuery()
|
|
Order localOrder = orderService.lambdaQuery()
|
|
|
.eq(Order::getUserId, Long.parseLong(userId))
|
|
.eq(Order::getUserId, Long.parseLong(userId))
|
|
@@ -372,41 +372,41 @@ public class CallbackController {
|
|
|
.orderByDesc(Order::getCreateTime)
|
|
.orderByDesc(Order::getCreateTime)
|
|
|
.last("LIMIT 1")
|
|
.last("LIMIT 1")
|
|
|
.one();
|
|
.one();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
if (localOrder == null) {
|
|
if (localOrder == null) {
|
|
|
log.warn("未找到对应的本地订单记录,用户: {}, 设备: {}", userId, deviceId);
|
|
log.warn("未找到对应的本地订单记录,用户: {}, 设备: {}", userId, deviceId);
|
|
|
return "success";
|
|
return "success";
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 4. 更新本地订单信息
|
|
// 4. 更新本地订单信息
|
|
|
localOrder.setOrderNo(orderId);
|
|
localOrder.setOrderNo(orderId);
|
|
|
localOrder.setActivityId(activityId);
|
|
localOrder.setActivityId(activityId);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 将订单明细存储到 items 字段(JSON格式)
|
|
// 将订单明细存储到 items 字段(JSON格式)
|
|
|
if (orderDetail != null) {
|
|
if (orderDetail != null) {
|
|
|
localOrder.setItems(orderDetail);
|
|
localOrder.setItems(orderDetail);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 设置订单金额
|
|
// 设置订单金额
|
|
|
if (orderMoney != null) {
|
|
if (orderMoney != null) {
|
|
|
localOrder.setTotalAmount(Double.parseDouble(orderMoney.toString()));
|
|
localOrder.setTotalAmount(Double.parseDouble(orderMoney.toString()));
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
boolean updated = orderService.updateById(localOrder);
|
|
boolean updated = orderService.updateById(localOrder);
|
|
|
if (updated) {
|
|
if (updated) {
|
|
|
log.info("订单信息更新成功: {}", orderId);
|
|
log.info("订单信息更新成功: {}", orderId);
|
|
|
} else {
|
|
} else {
|
|
|
log.error("订单信息更新失败: {}", orderId);
|
|
log.error("订单信息更新失败: {}", orderId);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 5. TODO: 根据业务需要进行后续处理
|
|
// 5. TODO: 根据业务需要进行后续处理
|
|
|
// - 触发支付流程
|
|
// - 触发支付流程
|
|
|
// - 发送通知给用户
|
|
// - 发送通知给用户
|
|
|
// - 更新库存等
|
|
// - 更新库存等
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// 6. 返回成功响应
|
|
// 6. 返回成功响应
|
|
|
return "success";
|
|
return "success";
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("处理订单回调通知失败", e);
|
|
log.error("处理订单回调通知失败", e);
|
|
|
// 即使处理失败,也返回成功,避免重复推送
|
|
// 即使处理失败,也返回成功,避免重复推送
|
|
@@ -416,17 +416,17 @@ public class CallbackController {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 解析请求参数,支持 JSON 和 form-urlencoded 两种格式
|
|
* 解析请求参数,支持 JSON 和 form-urlencoded 两种格式
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param request HTTP请求对象
|
|
* @param request HTTP请求对象
|
|
|
* @param requestParams form表单参数
|
|
* @param requestParams form表单参数
|
|
|
* @return 参数Map
|
|
* @return 参数Map
|
|
|
*/
|
|
*/
|
|
|
private Map<String, Object> parseRequestParams(HttpServletRequest request, Map<String, String> requestParams) {
|
|
private Map<String, Object> parseRequestParams(HttpServletRequest request, Map<String, String> requestParams) {
|
|
|
Map<String, Object> params = new HashMap<>();
|
|
Map<String, Object> params = new HashMap<>();
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
String contentType = request.getContentType();
|
|
String contentType = request.getContentType();
|
|
|
log.debug("请求Content-Type: {}", contentType);
|
|
log.debug("请求Content-Type: {}", contentType);
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
// 判断 Content-Type
|
|
// 判断 Content-Type
|
|
|
if (contentType != null && contentType.toLowerCase().contains("application/json")) {
|
|
if (contentType != null && contentType.toLowerCase().contains("application/json")) {
|
|
@@ -453,13 +453,13 @@ public class CallbackController {
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.error("解析请求参数失败", e);
|
|
log.error("解析请求参数失败", e);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return params;
|
|
return params;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 安全转换为整型,兼容字符串和整型
|
|
* 安全转换为整型,兼容字符串和整型
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param value 原始值
|
|
* @param value 原始值
|
|
|
* @return 整型值,如果转换失败返回null
|
|
* @return 整型值,如果转换失败返回null
|
|
|
*/
|
|
*/
|
|
@@ -467,7 +467,7 @@ public class CallbackController {
|
|
|
if (value == null) {
|
|
if (value == null) {
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
try {
|
|
try {
|
|
|
if (value instanceof Integer) {
|
|
if (value instanceof Integer) {
|
|
|
return (Integer) value;
|
|
return (Integer) value;
|
|
@@ -483,13 +483,13 @@ public class CallbackController {
|
|
|
} catch (NumberFormatException e) {
|
|
} catch (NumberFormatException e) {
|
|
|
log.warn("无法转换为整型: {}", value);
|
|
log.warn("无法转换为整型: {}", value);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
return null;
|
|
return null;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 验证签名
|
|
* 验证签名
|
|
|
- *
|
|
|
|
|
|
|
+ *
|
|
|
* @param params 回调参数
|
|
* @param params 回调参数
|
|
|
* @throws Exception 签名验证失败
|
|
* @throws Exception 签名验证失败
|
|
|
*/
|
|
*/
|
|
@@ -498,7 +498,7 @@ public class CallbackController {
|
|
|
if (receivedSign == null || receivedSign.isEmpty()) {
|
|
if (receivedSign == null || receivedSign.isEmpty()) {
|
|
|
throw new Exception("签名参数为空");
|
|
throw new Exception("签名参数为空");
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
// TODO: 实现签名验证逻辑
|
|
// TODO: 实现签名验证逻辑
|
|
|
// 1. 提取所有参数(除sign外)
|
|
// 1. 提取所有参数(除sign外)
|
|
|
// 2. 按字典序排序
|
|
// 2. 按字典序排序
|