From 35638b8664c6afe40429600149f39320a4023bb4 Mon Sep 17 00:00:00 2001 From: dute7liang <383200134@qq.com> Date: Fri, 26 Jan 2024 22:44:14 +0800 Subject: [PATCH] init --- .../controller/admin/AnchorController.java | 11 ++- .../controller/admin/DynamicController.java | 34 ++++++---- .../cai/controller/admin/UserController.java | 8 +++ .../cai/controller/app/AuthAppController.java | 2 +- .../commom/consumer/RechargeConsumerResp.java | 2 + .../com/ruoyi/cai/enums/SystemConfigEnum.java | 2 + .../ruoyi/cai/manager/ConsumerManager.java | 48 ++++++++++++- .../ruoyi/cai/manager/LoginAfterManager.java | 28 +++++++- .../java/com/ruoyi/cai/mq/AmqpProducer.java | 6 ++ .../ruoyi/cai/mq/consumer/WindowConsumer.java | 44 ++++++++++++ .../ruoyi/cai/mq/dto/CommonConsumerEnum.java | 2 +- .../{RankIHandle.java => RankHandle.java} | 2 +- .../ruoyi/cai/mq/handle/WindowGiftHandle.java | 62 +++++++++++++++++ .../cai/mq/handle/WindowRechargeHandle.java | 39 +++++++++++ .../cai/mq/handle/dto/WindowGiftDTO.java | 22 ++++++ .../cai/mq/handle/dto/WindowRechargeDTO.java | 21 ++++++ .../ruoyi/cai/notice/YunxinHttpService.java | 68 +++++++++---------- .../SendGiftWindowsAmountNoticeData.java | 4 +- .../com/ruoyi/cai/service/AnchorService.java | 2 +- .../com/ruoyi/cai/service/DynamicService.java | 5 ++ .../com/ruoyi/cai/service/UserService.java | 2 + .../cai/service/impl/AnchorServiceImpl.java | 3 +- .../cai/service/impl/DynamicServiceImpl.java | 27 ++++++++ .../impl/RechargeOrderServiceImpl.java | 1 + .../cai/service/impl/UserServiceImpl.java | 25 +++++++ .../ruoyi/common/constant/CacheConstants.java | 5 ++ .../com/ruoyi/framework/OnlineTodayCache.java | 36 ++++++++++ .../listener/UserActionListener.java | 3 + .../ruoyi/yunxin/service/YunxinWsService.java | 2 +- 29 files changed, 457 insertions(+), 59 deletions(-) create mode 100644 ruoyi-cai/src/main/java/com/ruoyi/cai/mq/consumer/WindowConsumer.java rename ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/{RankIHandle.java => RankHandle.java} (93%) create mode 100644 ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowGiftHandle.java create mode 100644 ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowRechargeHandle.java create mode 100644 ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowGiftDTO.java create mode 100644 ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowRechargeDTO.java create mode 100644 ruoyi-framework/src/main/java/com/ruoyi/framework/OnlineTodayCache.java diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/AnchorController.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/AnchorController.java index 4b12f0b0..ca39dadf 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/AnchorController.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/AnchorController.java @@ -7,6 +7,7 @@ import com.ruoyi.cai.domain.User; import com.ruoyi.cai.domain.UserOnline; import com.ruoyi.cai.dto.admin.vo.AnchorAdminVo; import com.ruoyi.cai.dto.admin.vo.AnchorFullAdminVo; +import com.ruoyi.cai.notice.YunxinHttpService; import com.ruoyi.cai.service.AnchorService; import com.ruoyi.cai.service.UserInfoService; import com.ruoyi.cai.service.UserOnlineService; @@ -20,6 +21,7 @@ import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.validate.EditGroup; import com.ruoyi.common.enums.BusinessType; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -97,13 +99,18 @@ public class AnchorController extends BaseController { return toAjax(anchorService.updateById(bo)); } + @Autowired + private YunxinHttpService yunxinHttpService; + @SaCheckPermission("cai:anchor:remove") @Log(title = "女神列表", businessType = BusinessType.DELETE) @DeleteMapping("/{userId}") public R remove(@NotNull(message = "主键不能为空") @PathVariable Long userId) { - - anchorService.closeAnchor(userId); + boolean boo = anchorService.closeAnchor(userId); + if(boo){ + yunxinHttpService.cancelAnchorMessage(userId); + } return R.ok(); } } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/DynamicController.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/DynamicController.java index cb02fc64..70f336ce 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/DynamicController.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/DynamicController.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.cai.domain.Dynamic; import com.ruoyi.cai.dto.admin.query.BatchAuditReq; import com.ruoyi.cai.dto.admin.vo.DynamicAdminVo; +import com.ruoyi.cai.dto.app.query.IdRes; import com.ruoyi.cai.enums.AuditStatusEnum; import com.ruoyi.cai.service.DynamicService; import com.ruoyi.common.annotation.Log; @@ -24,6 +25,7 @@ import org.springframework.web.bind.annotation.*; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -74,26 +76,32 @@ public class DynamicController extends BaseController { return toAjax(dynamicService.save(bo)); } - /** - * 修改主播动态 - */ @SaCheckPermission("cai:dynamic:edit") - @Log(title = "主播动态", businessType = BusinessType.UPDATE) + @Log(title = "主播动态修改成功", businessType = BusinessType.UPDATE) @RepeatSubmit() - @PutMapping() - public R edit(@Validated(EditGroup.class) @RequestBody Dynamic bo) { - return toAjax(dynamicService.updateById(bo)); + @PutMapping("/audit/success") + public R auditSuccess(@RequestBody IdRes bo) { + boolean success = dynamicService.auditSuccess(Collections.singletonList(bo.getId())); + return R.ok(); + } + + + @SaCheckPermission("cai:dynamic:edit") + @Log(title = "主播动态修改失败", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/audit/fail") + public R auditFail(@RequestBody IdRes bo) { + dynamicService.auditFail(bo.getId()); + return R.ok(); } @SaCheckPermission("cai:dynamic:edit") @RepeatSubmit() @PostMapping("/batch/audit") - public R edit(@RequestBody BatchAuditReq bo) { - dynamicService.update(Wrappers.lambdaUpdate(Dynamic.class) - .in(Dynamic::getId,bo.getIds()) - .set(Dynamic::getAuditStatus,bo.getAuditStatus()) - .eq(Dynamic::getAuditStatus,1)); - return R.ok(); + @Log(title = "批量审核通过动态", businessType = BusinessType.UPDATE) + public R edit(@RequestBody BatchAuditReq bo) { + boolean success = dynamicService.auditSuccess(bo.getIds()); + return R.ok(success); } @SaCheckPermission("cai:dynamic:edit") diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/UserController.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/UserController.java index d65870bd..dc356217 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/UserController.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/admin/UserController.java @@ -86,6 +86,14 @@ public class UserController extends BaseController { return R.ok(); } + @PostMapping("/resetNickname") + @SaCheckPermission("cai:user:resetNickname") + @Log(title = "重置用户昵称", businessType = BusinessType.UPDATE) + public R resetNickname(@RequestBody IdRes res){ + userService.resetNickname(res.getId()); + return R.ok(); + } + @PostMapping("/userForbid") @SaCheckPermission("cai:user:lock") @Log(title = "封禁用户", businessType = BusinessType.UPDATE) diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/app/AuthAppController.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/app/AuthAppController.java index 8349ee36..1954a9b7 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/app/AuthAppController.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/controller/app/AuthAppController.java @@ -96,7 +96,7 @@ public class AuthAppController { vo.setToken(token); vo.setUserInfo(currentUserManager.currentInfo()); // 异步调用通知 - loginAfterManager.loginAfter(LoginHelper.getUserId()); +// loginAfterManager.loginAfter(LoginHelper.getUserId()); return R.ok(vo); } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/commom/consumer/RechargeConsumerResp.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/commom/consumer/RechargeConsumerResp.java index b0b690db..a69df517 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/commom/consumer/RechargeConsumerResp.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/dto/commom/consumer/RechargeConsumerResp.java @@ -1,11 +1,13 @@ package com.ruoyi.cai.dto.commom.consumer; +import com.ruoyi.cai.domain.User; import lombok.Data; import java.math.BigDecimal; @Data public class RechargeConsumerResp { + private User user; /** * 充值金额 */ diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java index 477c23bd..cfc1fe2c 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/enums/SystemConfigEnum.java @@ -19,6 +19,8 @@ public enum SystemConfigEnum { REGISTER_AWARD("88", "注册奖励",SystemConfigGroupEnum.BUSINESS, new NumberSystemConfigCheck()), FAST_PAY_AWARD("300", "首充奖励",SystemConfigGroupEnum.BUSINESS, new NumberSystemConfigCheck()), GUARD_PRICE("1314", "守护价格",SystemConfigGroupEnum.BUSINESS, new NumberSystemConfigCheck()), + WINDOW_GIFT_THRESHOLD("10", "礼物飘窗阈值(彩贝)",SystemConfigGroupEnum.BUSINESS, new NumberSystemConfigCheck()), + WINDOW_RECHARGE_THRESHOLD("10", "充值飘窗阈值(充值金额)",SystemConfigGroupEnum.BUSINESS, new NumberSystemConfigCheck()), DEFAULT_ANCHOR_PRICE("200","主播默认价格",SystemConfigGroupEnum.BUSINESS, new NumberSystemConfigCheck()), DEFAULT_ANCHOR_GUARD_PRICE("0.5","主播默认守护提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()), DEFAULT_ANCHOR_GIFT_PRICE("0.5","主播默认礼物提成",SystemConfigGroupEnum.BUSINESS,new RateSystemConfigCheck()), diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/ConsumerManager.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/ConsumerManager.java index 5859f0d4..a395f3d1 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/ConsumerManager.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/ConsumerManager.java @@ -2,6 +2,8 @@ package com.ruoyi.cai.manager; import com.ruoyi.cai.domain.Account; import com.ruoyi.cai.domain.ConsumeLog; +import com.ruoyi.cai.domain.Gift; +import com.ruoyi.cai.domain.User; import com.ruoyi.cai.dto.ConsumeResp; import com.ruoyi.cai.dto.app.query.GiveGiftReq; import com.ruoyi.cai.dto.app.query.GiveGuardReq; @@ -9,7 +11,10 @@ import com.ruoyi.cai.dto.commom.consumer.GiftConsumerResp; import com.ruoyi.cai.dto.commom.consumer.GuardConsumerResp; import com.ruoyi.cai.dto.commom.consumer.RechargeConsumerResp; import com.ruoyi.cai.dto.video.VideoSettleResp; +import com.ruoyi.cai.enums.SystemConfigEnum; import com.ruoyi.cai.mq.AmqpProducer; +import com.ruoyi.cai.mq.handle.dto.WindowGiftDTO; +import com.ruoyi.cai.mq.handle.dto.WindowRechargeDTO; import com.ruoyi.cai.notice.YunxinHttpService; import com.ruoyi.cai.pay.PayTypeEnum; import com.ruoyi.cai.rank.RankManager; @@ -19,6 +24,8 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.math.BigDecimal; +import java.time.LocalDateTime; import java.util.Map; @Component @@ -38,6 +45,10 @@ public class ConsumerManager { private RankManager rankManager; @Autowired private YunxinHttpService yunxinHttpService; + @Autowired + private SystemConfigManager systemConfigManager; + @Autowired + private UserService userService; public GuardConsumerResp sendGuard(GiveGuardReq query){ GuardConsumerResp resp = guardTotalService.giveGuard(query); @@ -77,10 +88,28 @@ public class ConsumerManager { yunxinHttpService.getGiftSendMessage(resp.getToUid(), resp.getGift(), resp.getAnchorIncomeCoin(), totalCoin); } // 发送方给接收方的消息 - yunxinHttpService.sendGiftMessage(resp.getFromUid(),resp.getToUid(),resp.getGift(),query.getGiftCount()); + yunxinHttpService.sendGiftMessage(resp.getFromUid(),resp.getToUid(),resp.getGift(),query.getGiftCount().intValue()); }catch (Exception e){ log.error("礼物赠送成功通知失败!",e); } + try { + Long minPrice = systemConfigManager.getSystemConfigOfLong(SystemConfigEnum.WINDOW_GIFT_THRESHOLD); + Gift gift = resp.getGift(); + if(gift.getPrice() > minPrice){ + User fromUser = userService.getById(resp.getFromUid()); + Integer rankHide = fromUser.getRankHide(); + if(rankHide == 0){ + WindowGiftDTO windowGift = new WindowGiftDTO(); + windowGift.setFromId(resp.getFromUid()); + windowGift.setToId(resp.getToUid()); + windowGift.setGift(resp.getGift()); + windowGift.setGiftNum(query.getGiftCount()); + amqpProducer.sendWindowMq(windowGift); + } + } + }catch (Exception e){ + log.error("发送充值飘窗!",e); + } } return resp; } @@ -114,6 +143,23 @@ public class ConsumerManager { }catch (Exception e){ log.error("充值成功通知失败!",e); } + try { + BigDecimal decimal = systemConfigManager.getSystemConfigOfBigDecimal(SystemConfigEnum.WINDOW_RECHARGE_THRESHOLD); + if(decimal.compareTo(resp.getPrice()) < 0){ + Integer rankHide = resp.getUser().getRankHide(); + if(rankHide == 0){ + WindowRechargeDTO windowRecharge = new WindowRechargeDTO(); + windowRecharge.setId(resp.getUserId()); + windowRecharge.setNickname(resp.getUser().getNickname()); + windowRecharge.setAvatar(resp.getUser().getAvatar()); + windowRecharge.setAmount(resp.getPrice()); + windowRecharge.setTime(LocalDateTime.now()); + amqpProducer.sendWindowMq(windowRecharge); + } + } + }catch (Exception e){ + log.error("发送充值飘窗!",e); + } } return resp; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java index a11e73da..4dba9f75 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/manager/LoginAfterManager.java @@ -1,22 +1,48 @@ package com.ruoyi.cai.manager; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.ruoyi.cai.domain.User; import com.ruoyi.cai.domain.UserFollow; import com.ruoyi.cai.notice.YunxinHttpService; import com.ruoyi.cai.service.UserFollowService; +import com.ruoyi.cai.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import java.util.List; +import java.util.stream.Collectors; + @Component public class LoginAfterManager { @Autowired private YunxinHttpService yunxinHttpService; @Autowired private UserFollowService userFollowService; + @Autowired + private UserService userService; public void loginAfter(Long userId){ + // 给我的粉丝推送上线消息 + User user = userService.getById(userId); + if(user == null){ + return; + } Page page = new Page<>(); -// page.setSize() + page.setSize(500); + long current = 0L; + while (true){ + current++; + page.setCurrent(current); + Page pageRes = userFollowService.page(page, Wrappers.lambdaQuery(UserFollow.class) + .eq(UserFollow::getFollowUser, userId)); + List records = pageRes.getRecords(); + if(records.isEmpty()){ + break; + } + List userIds = records.stream().map(UserFollow::getUserId).collect(Collectors.toList()); + yunxinHttpService.sendOnlineAttentionNotice(userIds,user); + } } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/AmqpProducer.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/AmqpProducer.java index 2c93942d..2dfd4d54 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/AmqpProducer.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/AmqpProducer.java @@ -6,6 +6,7 @@ import com.ruoyi.cai.mq.config.RoomSettleDelayMqConfig; import com.ruoyi.cai.mq.config.CommonDelayMqConfig; import com.ruoyi.cai.mq.consumer.CalculateSalesQueueConsumer; import com.ruoyi.cai.mq.consumer.CommonConsumer; +import com.ruoyi.cai.mq.consumer.WindowConsumer; import com.ruoyi.cai.mq.dto.CommonDTO; import com.ruoyi.cai.mq.dto.CommonDelayDto; import org.springframework.amqp.rabbit.core.RabbitTemplate; @@ -27,6 +28,11 @@ public class AmqpProducer { CommonConsumer.COMMON_KEY, JSON.toJSONString(dto)); } + public void sendWindowMq(T dto){ + rabbitTemplate.convertAndSend(WindowConsumer.WINDOW_EXCHANGE, + WindowConsumer.WINDOW_KEY, JSON.toJSONString(dto)); + } + public void sendRoomCheckDelay(String message, Integer timeout){ rabbitTemplate.convertAndSend(RoomCheckDelayMqConfig.EXCHANGE_NAME, RoomCheckDelayMqConfig.ROUTING_KEY, diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/consumer/WindowConsumer.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/consumer/WindowConsumer.java new file mode 100644 index 00000000..e6100fd0 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/consumer/WindowConsumer.java @@ -0,0 +1,44 @@ +package com.ruoyi.cai.mq.consumer; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.ruoyi.cai.mq.handle.HandleConfig; +import com.ruoyi.cai.mq.handle.IHandle; +import lombok.extern.slf4j.Slf4j; +import org.springframework.amqp.rabbit.annotation.Exchange; +import org.springframework.amqp.rabbit.annotation.Queue; +import org.springframework.amqp.rabbit.annotation.QueueBinding; +import org.springframework.amqp.rabbit.annotation.RabbitListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +@Slf4j +public class WindowConsumer { + + public final static String WINDOW_QUEUE = "caiWindowQueue"; + public final static String WINDOW_EXCHANGE = "caiWindowExchange"; + public final static String WINDOW_KEY = "caiWindowKey"; + + @Autowired + private HandleConfig handleConfig; + + + // ,containerFactory = "customContainerFactory" + @RabbitListener(bindings = @QueueBinding( + value = @Queue(value = WINDOW_QUEUE, durable = "false", autoDelete = "false"), + exchange = @Exchange(value = WINDOW_EXCHANGE), + key = WINDOW_KEY)) + public void calculateSalesQueue(String message) { + log.info("飘窗检测-开始: message=" + message); + try { + JSONObject object = JSON.parseObject(message); + String type = object.getString("type"); + IHandle handle = handleConfig.getHandle(type); + handle.run(message); + }catch (Exception e){ + log.error("飘窗检测-失败: message=" + message,e); + } + log.info("飘窗检测-结束: message=" + message); + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/dto/CommonConsumerEnum.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/dto/CommonConsumerEnum.java index 10cad223..dbcd42a9 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/dto/CommonConsumerEnum.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/dto/CommonConsumerEnum.java @@ -1,5 +1,5 @@ package com.ruoyi.cai.mq.dto; public enum CommonConsumerEnum { - RANK + RANK,WINDOW_GIFT,WINDOW_RECHARGE } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/RankIHandle.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/RankHandle.java similarity index 93% rename from ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/RankIHandle.java rename to ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/RankHandle.java index 8045d4e1..4a8c1ba4 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/RankIHandle.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/RankHandle.java @@ -10,7 +10,7 @@ import org.springframework.stereotype.Component; @Component @Slf4j -public class RankIHandle implements IHandle { +public class RankHandle implements IHandle { @Autowired private RankManager rankManager; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowGiftHandle.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowGiftHandle.java new file mode 100644 index 00000000..ac5054c4 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowGiftHandle.java @@ -0,0 +1,62 @@ +package com.ruoyi.cai.mq.handle; + +import com.alibaba.fastjson2.JSON; +import com.google.common.collect.Lists; +import com.ruoyi.cai.domain.Gift; +import com.ruoyi.cai.domain.User; +import com.ruoyi.cai.mq.dto.CommonConsumerEnum; +import com.ruoyi.cai.mq.handle.dto.WindowGiftDTO; +import com.ruoyi.cai.notice.YunxinHttpService; +import com.ruoyi.cai.notice.data.child.SendGiftWindowsAmountNoticeData; +import com.ruoyi.cai.service.UserService; +import com.ruoyi.cai.util.CaiDateUtil; +import com.ruoyi.framework.OnlineTodayCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Component +public class WindowGiftHandle implements IHandle{ + + @Autowired + private YunxinHttpService yunxinHttpService; + @Autowired + private OnlineTodayCache onlineTodayCache; + @Autowired + private UserService userService; + + @Override + public void run(String message) { + WindowGiftDTO windowGift = JSON.parseObject(message, WindowGiftDTO.class); + User fromUser = userService.getById(windowGift.getFromId()); + User toUser = userService.getById(windowGift.getToId()); + Gift gift = windowGift.getGift(); + Set userIds = onlineTodayCache.getAllOnlineToday(); + List userIdList = new ArrayList<>(userIds); + List> lists = Lists.partition(userIdList, 300); + for (List list : lists) { + SendGiftWindowsAmountNoticeData data = new SendGiftWindowsAmountNoticeData(); + data.setId(fromUser.getId()); + data.setNickname(fromUser.getNickname()); + data.setAvatar(fromUser.getAvatar()); + data.setToid(toUser.getId()); + data.setTonickname(toUser.getNickname()); + data.setToavatar(toUser.getAvatar()); + data.setAmount(gift.getPrice()); + data.setGiftname(gift.getName()); + data.setGiftimg(gift.getImg()); + data.setGifttotal(windowGift.getGiftNum()); + data.setTime(CaiDateUtil.getCurrentTimeStr()); + yunxinHttpService.sendGiftWindowsAmount(list,data); + } + + } + + @Override + public CommonConsumerEnum getType() { + return CommonConsumerEnum.WINDOW_GIFT; + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowRechargeHandle.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowRechargeHandle.java new file mode 100644 index 00000000..e198ef5d --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/WindowRechargeHandle.java @@ -0,0 +1,39 @@ +package com.ruoyi.cai.mq.handle; + +import com.alibaba.fastjson2.JSON; +import com.google.common.collect.Lists; +import com.ruoyi.cai.mq.dto.CommonConsumerEnum; +import com.ruoyi.cai.mq.handle.dto.WindowRechargeDTO; +import com.ruoyi.cai.notice.YunxinHttpService; +import com.ruoyi.framework.OnlineTodayCache; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +@Component +public class WindowRechargeHandle implements IHandle{ + + @Autowired + private YunxinHttpService yunxinHttpService; + @Autowired + private OnlineTodayCache onlineTodayCache; + + @Override + public void run(String message) { + WindowRechargeDTO windowRecharge = JSON.parseObject(message, WindowRechargeDTO.class); + Set userIds = onlineTodayCache.getAllOnlineToday(); + List userIdList = new ArrayList<>(userIds); + List> lists = Lists.partition(userIdList, 300); + for (List list : lists) { + yunxinHttpService.sendRechargeWindowsAmount(list,windowRecharge); + } + } + + @Override + public CommonConsumerEnum getType() { + return CommonConsumerEnum.WINDOW_RECHARGE; + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowGiftDTO.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowGiftDTO.java new file mode 100644 index 00000000..df87cd55 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowGiftDTO.java @@ -0,0 +1,22 @@ +package com.ruoyi.cai.mq.handle.dto; + +import com.ruoyi.cai.domain.Gift; +import com.ruoyi.cai.mq.dto.CommonConsumerEnum; +import com.ruoyi.cai.mq.dto.CommonDTO; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class WindowGiftDTO extends CommonDTO { + private Long fromId; + private Long toId; + private Gift gift; + private Long giftNum; + private LocalDateTime time; + + public WindowGiftDTO() { + this.setType(CommonConsumerEnum.WINDOW_GIFT); + this.time = LocalDateTime.now(); + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowRechargeDTO.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowRechargeDTO.java new file mode 100644 index 00000000..4c7acbb9 --- /dev/null +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/mq/handle/dto/WindowRechargeDTO.java @@ -0,0 +1,21 @@ +package com.ruoyi.cai.mq.handle.dto; + +import com.ruoyi.cai.mq.dto.CommonConsumerEnum; +import com.ruoyi.cai.mq.dto.CommonDTO; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDateTime; + +@Data +public class WindowRechargeDTO extends CommonDTO { + private Long id; + private String nickname; + private String avatar; + private BigDecimal amount; + private LocalDateTime time; + public WindowRechargeDTO() { + this.setType(CommonConsumerEnum.WINDOW_RECHARGE); + this.time = LocalDateTime.now(); + } +} diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/YunxinHttpService.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/YunxinHttpService.java index 0ee32e14..cac05e26 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/YunxinHttpService.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/YunxinHttpService.java @@ -3,6 +3,7 @@ package com.ruoyi.cai.notice; import com.alibaba.fastjson.JSON; import com.ruoyi.cai.domain.Gift; import com.ruoyi.cai.domain.User; +import com.ruoyi.cai.mq.handle.dto.WindowRechargeDTO; import com.ruoyi.cai.notice.data.NoticeR; import com.ruoyi.cai.notice.data.child.*; import com.ruoyi.cai.notice.dto.CashSuccessSendMesDTO; @@ -354,21 +355,19 @@ public class YunxinHttpService { * 发送上线了批量自定义消息 */ public void sendOnlineAttentionNotice(List toUid,User loginUser){ - YunExecutor.YUN_EXECUTOR.execute(() -> { - SendOnlineAttentionNoticeData data = new SendOnlineAttentionNoticeData(); - data.setUserid(loginUser.getId()); - data.setNickname(loginUser.getNickname()); - data.setAvatar(loginUser.getAvatar()); - data.setAge(loginUser.getAge()); - data.setCity(loginUser.getCity()); - data.setTime(CaiDateUtil.getCurrentTimeStr()); - data.setSex(loginUser.getGender()); - NoticeR notice = NoticeR.ok(MessageBaseTypeEnum.SEND_ONLINE_ATTENTION, data); - YxDataR r = yunxin.batchSendToNotice(toUid, notice); - if(r == null || !r.isSuccess()){ - log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); - } - }); + SendOnlineAttentionNoticeData data = new SendOnlineAttentionNoticeData(); + data.setUserid(loginUser.getId()); + data.setNickname(loginUser.getNickname()); + data.setAvatar(loginUser.getAvatar()); + data.setAge(loginUser.getAge()); + data.setCity(loginUser.getCity()); + data.setTime(CaiDateUtil.getCurrentTimeStr()); + data.setSex(loginUser.getGender()); + NoticeR notice = NoticeR.ok(MessageBaseTypeEnum.SEND_ONLINE_ATTENTION, data); + YxDataR r = yunxin.batchSendToNotice(toUid, notice); + if(r == null || !r.isSuccess()){ + log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); + } } /** @@ -410,7 +409,7 @@ public class YunxinHttpService { /** - * 相册审核通知 + * 相册审核通知 TODO */ public void albumAuditMessage(Long toUid){ YunExecutor.YUN_EXECUTOR.execute(() -> { @@ -452,36 +451,37 @@ public class YunxinHttpService { /** * 发送充值飘窗提醒 */ - public void sendRechargeWindowsAmount(Long toUid){ - YunExecutor.YUN_EXECUTOR.execute(() -> { - SendRechargeWindowsAmountNoticeData data = new SendRechargeWindowsAmountNoticeData(); - NoticeR notice = NoticeR.ok(MessageBaseTypeEnum.RECHARGE_WINDOWS_AMOUNT, data); - YxDataR r = yunxin.sendToNotice(toUid, notice); - if(r == null || !r.isSuccess()){ - log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); - } - }); + public void sendRechargeWindowsAmount(List toIds, WindowRechargeDTO windowRecharge){ + SendRechargeWindowsAmountNoticeData data = new SendRechargeWindowsAmountNoticeData(); + data.setId(windowRecharge.getId()); + data.setNickname(windowRecharge.getNickname()); + data.setAvatar(windowRecharge.getAvatar()); + data.setAmount(windowRecharge.getAmount().toString()); + data.setTime(CaiDateUtil.getCurrentTimeStr()); + NoticeR notice = NoticeR.ok(MessageBaseTypeEnum.RECHARGE_WINDOWS_AMOUNT, data); + YxDataR r = yunxin.batchSendToNotice(toIds, notice); + if(r == null || !r.isSuccess()){ + log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); + } } /** * 发送礼物飘窗提醒 */ - public void sendGiftWindowsAmount(Long toUid){ - YunExecutor.YUN_EXECUTOR.execute(() -> { - SendGiftWindowsAmountNoticeData data = new SendGiftWindowsAmountNoticeData(); - NoticeR notice = NoticeR.ok(MessageBaseTypeEnum.SEND_GIFT_WINDOWS_AMOUNT, data); - YxDataR r = yunxin.sendToNotice(toUid, notice); - if(r == null || !r.isSuccess()){ - log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); - } - }); + public void sendGiftWindowsAmount(List toUid, SendGiftWindowsAmountNoticeData data){ + NoticeR notice = NoticeR.ok(MessageBaseTypeEnum.SEND_GIFT_WINDOWS_AMOUNT, data); + YxDataR r = yunxin.batchSendToNotice(toUid, notice); + if(r == null || !r.isSuccess()){ + log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); + } } /** * 动态通知 */ + @Deprecated public void sendFollowDynamicBath(Long toUid, User user,String image,String content){ YunExecutor.YUN_EXECUTOR.execute(() -> { SendFollowDynamicNoticeData data = new SendFollowDynamicNoticeData(); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/data/child/SendGiftWindowsAmountNoticeData.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/data/child/SendGiftWindowsAmountNoticeData.java index cb2497af..b636c23d 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/data/child/SendGiftWindowsAmountNoticeData.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/notice/data/child/SendGiftWindowsAmountNoticeData.java @@ -10,10 +10,10 @@ public class SendGiftWindowsAmountNoticeData { private Long toid; private String tonickname; private String toavatar; - private String amount; + private Long amount; private String giftname; private String giftimg; - private String gifttotal; + private Long gifttotal; private String time; diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/AnchorService.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/AnchorService.java index d268679e..f8022f6b 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/AnchorService.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/AnchorService.java @@ -18,7 +18,7 @@ public interface AnchorService extends IService { void joinAnchor(Long userId); - void closeAnchor(Long userId); + boolean closeAnchor(Long userId); Page pageAdmin(PageQuery pageQuery, AnchorAdminVo bo); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/DynamicService.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/DynamicService.java index 2c9fc253..b58ed447 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/DynamicService.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/DynamicService.java @@ -6,6 +6,7 @@ import com.ruoyi.cai.domain.Dynamic; import com.ruoyi.cai.dto.admin.vo.DynamicAdminVo; import com.ruoyi.cai.dto.app.query.DynamicAddReq; import com.ruoyi.cai.dto.app.query.DynamicQuery; +import com.ruoyi.cai.dto.app.query.IdRes; import com.ruoyi.cai.dto.app.vo.DynamicListVo; import com.ruoyi.common.core.domain.PageQuery; @@ -34,4 +35,8 @@ public interface DynamicService extends IService { void deleteDynamic(Long id); void clearDynamic(Integer hours); + + boolean auditSuccess(List ids); + + boolean auditFail(Long id); } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/UserService.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/UserService.java index 99f7488b..38fe7397 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/UserService.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/UserService.java @@ -39,6 +39,8 @@ public interface UserService extends IService { boolean removeUser(Long id); + void resetNickname(Long id); + void resetAvatar(Long id); void refreshByAge(); diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/AnchorServiceImpl.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/AnchorServiceImpl.java index 76fda89b..ff7adc22 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/AnchorServiceImpl.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/AnchorServiceImpl.java @@ -75,7 +75,7 @@ public class AnchorServiceImpl extends ServiceImpl impleme @Override @Transactional(rollbackFor = Exception.class) - public void closeAnchor(Long userId){ + public boolean closeAnchor(Long userId){ User user = userService.getById(userId); Anchor anchor = this.getByUserId(userId); if(user == null){ @@ -91,6 +91,7 @@ public class AnchorServiceImpl extends ServiceImpl impleme throw new ServiceException("取消主播失败"); } this.removeById(anchor.getId()); + return boo; } @Override diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/DynamicServiceImpl.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/DynamicServiceImpl.java index af9160a8..e844a711 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/DynamicServiceImpl.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/DynamicServiceImpl.java @@ -20,6 +20,7 @@ import com.ruoyi.cai.enums.SystemConfigEnum; import com.ruoyi.cai.manager.InnerUserFilter; import com.ruoyi.cai.manager.SystemConfigManager; import com.ruoyi.cai.mapper.DynamicMapper; +import com.ruoyi.cai.notice.YunxinHttpService; import com.ruoyi.cai.service.CitysService; import com.ruoyi.cai.service.DynamicImagesService; import com.ruoyi.cai.service.DynamicService; @@ -64,6 +65,8 @@ public class DynamicServiceImpl extends ServiceImpl impl private CitysService citysService; @Autowired private InnerUserFilter innerUserFilter; + @Autowired + private YunxinHttpService yunxinHttpService; @Override public void unTop(Long id){ @@ -215,4 +218,28 @@ public class DynamicServiceImpl extends ServiceImpl impl } } + @Override + public boolean auditSuccess(List ids) { + return this.update(Wrappers.lambdaUpdate(Dynamic.class) + .set(Dynamic::getAuditStatus,AuditStatusEnum.SUCCESS.getCode()) + .eq(Dynamic::getAuditStatus,AuditStatusEnum.AUDITING.getCode()) + .in(Dynamic::getId,ids)); + } + + @Override + public boolean auditFail(Long id) { + Dynamic dynamic = this.getById(id); + if(dynamic == null){ + return false; + } + boolean update = this.update(Wrappers.lambdaUpdate(Dynamic.class) + .set(Dynamic::getAuditStatus, AuditStatusEnum.FAIL.getCode()) + .eq(Dynamic::getAuditStatus, AuditStatusEnum.AUDITING.getCode()) + .eq(Dynamic::getId, id)); + if(update){ + yunxinHttpService.dynamicAuditMessage(dynamic.getUserId()); + } + return update; + } + } diff --git a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/RechargeOrderServiceImpl.java b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/RechargeOrderServiceImpl.java index 581983ef..05722de4 100644 --- a/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/RechargeOrderServiceImpl.java +++ b/ruoyi-cai/src/main/java/com/ruoyi/cai/service/impl/RechargeOrderServiceImpl.java @@ -129,6 +129,7 @@ public class RechargeOrderServiceImpl extends ServiceImpl implements Us @Resource private ImUserRefClient userRefClient; + @Autowired + private YunxinHttpService yunxinHttpService; + + @Override + public void resetNickname(Long id){ + User user = this.getById(id); + if(user == null){ + return; + } + boolean update = this.update(Wrappers.lambdaUpdate(User.class) + .eq(User::getId, user.getId()) + .set(User::getNickname, "用户" + user.getUsercode())); + if(!update){ + return; + } + UpdateUinfoReq uinfoReq = new UpdateUinfoReq(); + uinfoReq.setAccid(user.getId()+""); + uinfoReq.setName("用户"+user.getUsercode()); + YxCommonR r = userRefClient.updateUinfo(uinfoReq); + if(!r.isSuccess()){ + log.error("云信更新失败,需要检查!{}", JSON.toJSONString(r)); + } + yunxinHttpService.nickAuditMessage(id); + } @Override public void resetAvatar(Long id) { diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java index 0fb2c3f9..3621b8d8 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/constant/CacheConstants.java @@ -12,6 +12,11 @@ public interface CacheConstants { */ String ONLINE_TOKEN_KEY = "online_tokens:"; + /** + * 单天在线APP用户 + */ + String ONLINE_TODAY_TOKEN_KEY = "online_tokens_today:%s"; + /** * 验证码 redis key */ diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/OnlineTodayCache.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/OnlineTodayCache.java new file mode 100644 index 00000000..b3a927d2 --- /dev/null +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/OnlineTodayCache.java @@ -0,0 +1,36 @@ +package com.ruoyi.framework; + +import com.ruoyi.common.constant.CacheConstants; +import org.redisson.api.RSet; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.time.Duration; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Set; + +@Component +public class OnlineTodayCache { + + @Autowired + private RedissonClient redissonClient; + + private String getKey(){ + String today = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); + return String.format(CacheConstants.ONLINE_TODAY_TOKEN_KEY,today); + } + + public void addOnlineUserId(Long userId){ + RSet set = redissonClient.getSet(getKey()); + set.add(userId); + set.expire(Duration.ofDays(1)); + } + + public Set getAllOnlineToday(){ + RSet set = redissonClient.getSet(getKey()); + return set.readAll(); + } +} diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java index 08a41ec7..66399800 100644 --- a/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java +++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/listener/UserActionListener.java @@ -13,6 +13,7 @@ import com.ruoyi.common.helper.LoginHelper; import com.ruoyi.common.utils.ServletUtils; import com.ruoyi.common.utils.ip.AddressUtils; import com.ruoyi.common.utils.redis.RedisUtils; +import com.ruoyi.framework.OnlineTodayCache; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -30,6 +31,7 @@ import java.time.Duration; public class UserActionListener implements SaTokenListener { private final SaTokenConfig tokenConfig; + private final OnlineTodayCache onlineTodayCache; /** * 每次登录时触发 @@ -76,6 +78,7 @@ public class UserActionListener implements SaTokenListener { } else { RedisUtils.setCacheObject(CacheConstants.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(tokenConfig.getTimeout())); } + onlineTodayCache.addOnlineUserId(user.getUserId()); } } diff --git a/ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/service/YunxinWsService.java b/ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/service/YunxinWsService.java index 4e011fc7..e2c10b7a 100644 --- a/ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/service/YunxinWsService.java +++ b/ruoyi-yunxin/src/main/java/com/ruoyi/yunxin/service/YunxinWsService.java @@ -43,7 +43,7 @@ public class YunxinWsService { public void sendToCallNotifyAsync(Long toUid, Long fromUid, CallNoticeEnum status, Long calltime){ YunExecutor.YUN_EXECUTOR.execute(() -> { ImDataRes imDataRes = ImMsgGen.callNotice(status, fromUid, toUid, calltime); - YxDataR r = yunxin.sendToNotice(toUid, fromUid, imDataRes); + YxDataR r = yunxin.sendToUserNotice(toUid, fromUid, imDataRes); if(r == null || !r.isSuccess()){ log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r)); }