|
|
@@ -24,6 +24,7 @@ import com.fs.common.utils.StringUtils;
|
|
|
import com.fs.common.utils.spring.SpringUtils;
|
|
|
import com.fs.live.domain.*;
|
|
|
import com.fs.live.service.*;
|
|
|
+import com.fs.live.vo.LiveConsoleOpLogVo;
|
|
|
import com.fs.live.vo.LiveGoodsVo;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.commons.lang3.time.DateUtils;
|
|
|
@@ -102,6 +103,7 @@ public class WebSocketServer {
|
|
|
private final ILiveUserFirstEntryService liveUserFirstEntryService = SpringUtils.getBean(ILiveUserFirstEntryService.class);
|
|
|
private final ILiveCouponIssueService liveCouponIssueService = SpringUtils.getBean(ILiveCouponIssueService.class);
|
|
|
private final LiveCouponMapper liveCouponMapper = SpringUtils.getBean(LiveCouponMapper.class);
|
|
|
+ private final ILiveConsoleOpLogService liveConsoleOpLogService = SpringUtils.getBean(ILiveConsoleOpLogService.class);
|
|
|
private final ILiveWatchLogService liveWatchLogService = SpringUtils.getBean(ILiveWatchLogService.class);
|
|
|
private final ILiveVideoService liveVideoService = SpringUtils.getBean(ILiveVideoService.class);
|
|
|
private final ILiveCompletionPointsRecordService completionPointsRecordService = SpringUtils.getBean(ILiveCompletionPointsRecordService.class);
|
|
|
@@ -485,6 +487,9 @@ public class WebSocketServer {
|
|
|
sendMessage(session, JSONObject.toJSONString(R.error("你已被禁言")));
|
|
|
return;
|
|
|
}
|
|
|
+ if (!liveWatchUser.isEmpty() && liveWatchUser.get(0).getSingleVisible() != null) {
|
|
|
+ liveMsg.setSingleVisible(liveWatchUser.get(0).getSingleVisible());
|
|
|
+ }
|
|
|
} catch (Exception e) {
|
|
|
log.error("[WebSocket-sendMsg] 检查禁言状态失败, liveId={}, userId={}, error={}",
|
|
|
liveId, msg.getUserId(), e.getMessage(), e);
|
|
|
@@ -594,10 +599,16 @@ public class WebSocketServer {
|
|
|
break;
|
|
|
case "showCart":
|
|
|
msg.setOn(true);
|
|
|
+ if (msg.getStatus() != null) {
|
|
|
+ liveService.updateShowCartWithoutBroadcast(liveId, msg.getStatus());
|
|
|
+ }
|
|
|
enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
|
|
|
break;
|
|
|
case "singleVisible":
|
|
|
- liveWatchUserService.updateSingleVisible(liveId, msg.getStatus(),msg.getUserId());
|
|
|
+ msg.setOn(true);
|
|
|
+ if (msg.getStatus() != null && msg.getUserId() != null) {
|
|
|
+ liveWatchUserService.updateSingleVisible(liveId, msg.getStatus(), msg.getUserId());
|
|
|
+ }
|
|
|
// 管理员消息插队
|
|
|
enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
|
|
|
break;
|
|
|
@@ -659,6 +670,8 @@ public class WebSocketServer {
|
|
|
} else {
|
|
|
redisCache.deleteObject(String.format(LiveKeysConstant.LIVE_COUPON_NUM , couponIssueId));
|
|
|
}
|
|
|
+ LiveConsoleOpLog opLog = saveConsoleCouponOpLog(liveId, couponIssueId, status);
|
|
|
+ attachOpLog(msg, opLog);
|
|
|
// 管理员消息插队
|
|
|
enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
|
|
|
}
|
|
|
@@ -690,6 +703,8 @@ public class WebSocketServer {
|
|
|
if (Objects.nonNull(liveRedConf)) {
|
|
|
liveService.asyncToCacheLiveConfig(liveId);
|
|
|
msg.setData(JSONObject.toJSONString(liveRedConf));
|
|
|
+ LiveConsoleOpLog opLog = saveConsoleRedOpLog(liveId, liveRedConf, status);
|
|
|
+ attachOpLog(msg, opLog);
|
|
|
// 管理员消息插队
|
|
|
enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
|
|
|
}
|
|
|
@@ -706,11 +721,138 @@ public class WebSocketServer {
|
|
|
if (Objects.nonNull(liveLotteryConf)) {
|
|
|
liveService.asyncToCacheLiveConfig(liveId);
|
|
|
msg.setData(JSONObject.toJSONString(liveLotteryConf));
|
|
|
+ LiveConsoleOpLog opLog = saveConsoleLotteryOpLog(liveId, liveLotteryConf, status);
|
|
|
+ attachOpLog(msg, opLog);
|
|
|
// 管理员消息插队
|
|
|
enqueueMessage(liveId, JSONObject.toJSONString(R.ok().put("data", msg)), true);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 中控台红包:仅结算时挂载 REST/定时任务已写入的留存,不在 WebSocket 侧新建记录
|
|
|
+ */
|
|
|
+ private LiveConsoleOpLog saveConsoleRedOpLog(Long liveId, LiveRedConf liveRedConf, Integer status) {
|
|
|
+ if (liveRedConf == null || status == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (status == 2 || status == -1) {
|
|
|
+ return findRecentRedSettleOpLog(liveId, liveRedConf.getRedId());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询 REST 结算接口刚写入的红包结算留存,供 WebSocket 挂载 opLog
|
|
|
+ */
|
|
|
+ private LiveConsoleOpLog findRecentRedSettleOpLog(Long liveId, Long redId) {
|
|
|
+ LiveConsoleOpLog query = new LiveConsoleOpLog();
|
|
|
+ query.setLiveId(liveId);
|
|
|
+ query.setBizId(redId);
|
|
|
+ List<LiveConsoleOpLog> list = liveConsoleOpLogService.selectLiveConsoleOpLogList(query);
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ long now = System.currentTimeMillis();
|
|
|
+ for (LiveConsoleOpLog item : list) {
|
|
|
+ if (item.getCreateTime() == null
|
|
|
+ || !Objects.equals(item.getOpType(), LiveConsoleOpLog.OP_RED_SETTLE)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (now - item.getCreateTime().getTime() <= 30000) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 中控台抽奖:仅结算时挂载 REST 已写入的留存,不在 WebSocket 侧新建记录
|
|
|
+ */
|
|
|
+ private LiveConsoleOpLog saveConsoleLotteryOpLog(Long liveId, LiveLotteryConf liveLotteryConf, Integer status) {
|
|
|
+ if (liveLotteryConf == null || status == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ if (status == 2 || status == -1) {
|
|
|
+ return findRecentLotterySettleOpLog(liveId, liveLotteryConf.getLotteryId());
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询 REST 结算接口刚写入的抽奖结算留存,供 WebSocket 挂载 opLog
|
|
|
+ */
|
|
|
+ private LiveConsoleOpLog findRecentLotterySettleOpLog(Long liveId, Long lotteryId) {
|
|
|
+ LiveConsoleOpLog query = new LiveConsoleOpLog();
|
|
|
+ query.setLiveId(liveId);
|
|
|
+ query.setBizId(lotteryId);
|
|
|
+ List<LiveConsoleOpLog> list = liveConsoleOpLogService.selectLiveConsoleOpLogList(query);
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ long now = System.currentTimeMillis();
|
|
|
+ for (LiveConsoleOpLog item : list) {
|
|
|
+ if (item.getCreateTime() == null
|
|
|
+ || !Objects.equals(item.getOpType(), LiveConsoleOpLog.OP_LOTTERY_SETTLE)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ if (now - item.getCreateTime().getTime() <= 30000) {
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 中控台优惠券展示留存
|
|
|
+ */
|
|
|
+ private LiveConsoleOpLog saveConsoleCouponOpLog(Long liveId, Long couponIssueId, Integer status) {
|
|
|
+ if (status == null || status != 1 || couponIssueId == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ LiveConsoleOpLog recent = findRecentCouponShowOpLog(liveId, couponIssueId);
|
|
|
+ if (recent != null) {
|
|
|
+ return recent;
|
|
|
+ }
|
|
|
+ LiveCouponIssue liveCouponIssue = liveCouponIssueService.selectLiveCouponIssueById(couponIssueId);
|
|
|
+ if (liveCouponIssue == null || liveCouponIssue.getCouponId() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ LiveCoupon liveCoupon = liveCouponMapper.selectLiveCouponById(liveCouponIssue.getCouponId());
|
|
|
+ return liveConsoleOpLogService.saveCouponShowLog(
|
|
|
+ liveId,
|
|
|
+ couponIssueId,
|
|
|
+ liveCoupon,
|
|
|
+ LiveConsoleOpLog.HANDLE_CONSOLE
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 中控台展示券时 REST 已写入留存,WebSocket 复用近期记录避免重复插入
|
|
|
+ */
|
|
|
+ private LiveConsoleOpLog findRecentCouponShowOpLog(Long liveId, Long couponIssueId) {
|
|
|
+ LiveConsoleOpLog query = new LiveConsoleOpLog();
|
|
|
+ query.setLiveId(liveId);
|
|
|
+ query.setBizId(couponIssueId);
|
|
|
+ List<LiveConsoleOpLog> list = liveConsoleOpLogService.selectLiveConsoleOpLogList(query);
|
|
|
+ if (list == null || list.isEmpty()) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ LiveConsoleOpLog latest = list.get(0);
|
|
|
+ if (latest.getCreateTime() == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ long ageMs = System.currentTimeMillis() - latest.getCreateTime().getTime();
|
|
|
+ if (ageMs > 5000) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ Integer opType = latest.getOpType();
|
|
|
+ if (opType != null
|
|
|
+ && (opType == LiveConsoleOpLog.OP_COUPON_SHOW || opType == LiveConsoleOpLog.OP_VERIFY_COUPON_SHOW)) {
|
|
|
+ return latest;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
//错误时调用
|
|
|
@OnError
|
|
|
public void onError(Session session, Throwable throwable) {
|
|
|
@@ -906,7 +1048,20 @@ public class WebSocketServer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void sendIntegralMessage(Long liveId, Long userId,Long scoreAmount) {
|
|
|
+ /**
|
|
|
+ * 将中控台操作留存挂载到 WebSocket 消息体,供 App 端展示
|
|
|
+ */
|
|
|
+ public static void attachOpLog(SendMsgVo msg, LiveConsoleOpLog opLog) {
|
|
|
+ if (msg != null && opLog != null) {
|
|
|
+ msg.setOpLog(LiveConsoleOpLogVo.from(opLog));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public void sendIntegralMessage(Long liveId, Long userId, Long scoreAmount) {
|
|
|
+ sendIntegralMessage(liveId, userId, scoreAmount, null);
|
|
|
+ }
|
|
|
+
|
|
|
+ public void sendIntegralMessage(Long liveId, Long userId, Long scoreAmount, LiveConsoleOpLog opLog) {
|
|
|
ConcurrentHashMap<Long, Session> room = getRoom(liveId);
|
|
|
Session session = room.get(userId);
|
|
|
if (session == null || !session.isOpen()) {
|
|
|
@@ -919,6 +1074,7 @@ public class WebSocketServer {
|
|
|
sendMsgVo.setCmd("Integral");
|
|
|
sendMsgVo.setMsg("恭喜你成功获得观看奖励:" + scoreAmount + "积分");
|
|
|
sendMsgVo.setData(String.valueOf(scoreAmount));
|
|
|
+ attachOpLog(sendMsgVo, opLog);
|
|
|
|
|
|
if(Objects.isNull(session)) return;
|
|
|
sendMessage(session, JSONObject.toJSONString(R.ok().put("data", sendMsgVo)));
|
|
|
@@ -1002,6 +1158,17 @@ public class WebSocketServer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 广播中控台指令到 App 端(管理员消息插队)
|
|
|
+ */
|
|
|
+ public void broadcastLiveCmd(SendMsgVo msg) {
|
|
|
+ if (msg == null || msg.getLiveId() == null || StringUtils.isEmpty(msg.getCmd())) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ msg.setOn(true);
|
|
|
+ enqueueMessage(msg.getLiveId(), JSONObject.toJSONString(R.ok().put("data", msg)), true);
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* 广播消息
|
|
|
* @param liveId 直播间ID
|
|
|
@@ -1329,6 +1496,12 @@ public class WebSocketServer {
|
|
|
data.put("couponTime", liveCoupon.getCouponTime());
|
|
|
msg.setData(JSON.toJSONString(data));
|
|
|
liveCouponMapper.updateChangeShow(task.getLiveId(), liveCouponIssue.getId());
|
|
|
+ attachOpLog(msg, liveConsoleOpLogService.saveCouponShowLog(
|
|
|
+ task.getLiveId(),
|
|
|
+ liveCouponIssue.getId(),
|
|
|
+ liveCoupon,
|
|
|
+ LiveConsoleOpLog.HANDLE_AUTO
|
|
|
+ ));
|
|
|
} else if (task.getTaskType() == 6L) {
|
|
|
// 上架/下架商品
|
|
|
msg.setCmd("goods");
|
|
|
@@ -1622,7 +1795,8 @@ public class WebSocketServer {
|
|
|
log.debug("[实时完课检查] liveId={}, userId={}, duration={}秒", liveId, userId, duration);
|
|
|
|
|
|
// 1. 调用完课记录服务检查并创建完课记录
|
|
|
- completionPointsRecordService.checkAndCreateCompletionRecord(liveId, userId, duration);
|
|
|
+ LiveCompletionPointsRecord createdRecord =
|
|
|
+ completionPointsRecordService.checkAndCreateCompletionRecord(liveId, userId, duration);
|
|
|
|
|
|
// 2. 查询是否有新的未领取完课记录
|
|
|
List<LiveCompletionPointsRecord> unreceivedRecords =
|
|
|
@@ -1633,19 +1807,32 @@ public class WebSocketServer {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ LiveCompletionPointsRecord notifyRecord = unreceivedRecords.get(0);
|
|
|
+
|
|
|
// 3. 构建推送消息
|
|
|
SendMsgVo sendMsgVo = new SendMsgVo();
|
|
|
sendMsgVo.setLiveId(liveId);
|
|
|
sendMsgVo.setUserId(userId);
|
|
|
sendMsgVo.setCmd("completionPoints");
|
|
|
sendMsgVo.setMsg("完成任务!");
|
|
|
- sendMsgVo.setData(JSONObject.toJSONString(unreceivedRecords.get(0)));
|
|
|
+ sendMsgVo.setData(JSONObject.toJSONString(notifyRecord));
|
|
|
+ if (createdRecord != null) {
|
|
|
+ LiveConsoleOpLog opLog = liveConsoleOpLogService.saveLog(
|
|
|
+ liveId,
|
|
|
+ LiveConsoleOpLog.OP_COMPLETION_POINTS,
|
|
|
+ LiveConsoleOpLog.HANDLE_AUTO,
|
|
|
+ createdRecord.getId(),
|
|
|
+ "完课积分" + createdRecord.getPointsAwarded() + "分"
|
|
|
+ );
|
|
|
+ liveConsoleOpLogService.bindOpLogUser(opLog.getId(), liveId, userId);
|
|
|
+ attachOpLog(sendMsgVo, opLog);
|
|
|
+ }
|
|
|
|
|
|
// 4. 实时推送完课积分弹窗
|
|
|
sendCompletionPointsMessage(liveId, userId, sendMsgVo);
|
|
|
|
|
|
log.info("[实时完课推送] 发送完课积分弹窗通知, liveId={}, userId={}, points={}, duration={}秒",
|
|
|
- liveId, userId, unreceivedRecords.get(0).getPointsAwarded(), duration);
|
|
|
+ liveId, userId, notifyRecord.getPointsAwarded(), duration);
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
log.error("[实时完课推送] 实时检查完课积分失败, liveId={}, userId={}, duration={}",
|