This commit is contained in:
777
2025-09-26 15:40:59 +08:00
parent 0a3889a5cb
commit e2a03924a9
17 changed files with 391 additions and 4 deletions

View File

@@ -0,0 +1,55 @@
package com.ruoyi.cai.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 魅力榜单实时数据
*/
@Data
@TableName("cai_love_rank_today")
public class LoveRankToday implements Serializable {
@TableId(value = "id",type = IdType.AUTO)
private Long id;
/**
* 日期类型 1-日榜 2-周榜 3-月榜
*/
private Integer dataType;
/**
* 榜单期数
*/
private LocalDate rankTime;
/**
* 开始时间
*/
private LocalDate beginRankTime;
/**
* 结束时间
*/
private LocalDate endRankTime;
/**
* 数值
*/
private Long num;
/**
* 用户ID
*/
private Long userId;
/**
* 创建时间
*/
private LocalDateTime createTime;
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.cai.dto.app.vo.rank;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class RankNodeRecharge {
@Schema(description = "用户ID只有当前用户才有数据其他用户因为保密问题没有")
private Long userId;
@Schema(description = "头像")
private String avatar;
@Schema(description = "昵称")
private String nickname;
private BigDecimal money;
@Schema(description = "数值")
private Long value;
@Schema(description = "距离上一名差距")
private Long diffLastValue;
}

View File

@@ -7,6 +7,7 @@ import lombok.Getter;
public enum RankTypeEnum {
LOVE(1,"魅力榜"),
INVITE(2,"邀请榜"),
PAY(3,"土豪榜"),
;
private final Integer code;
private final String text;

View File

@@ -183,7 +183,7 @@ public class ConsumerManager {
accountTotalManager.incsTrdPayIncomeCoin(resp.getUserId(), resp.getPrice());
}
}catch (Exception e){
log.error("主播消费记录失败-充值",e);
log.error("用户充值记录失败-充值",e);
}
try {
Account account = accountService.getByUserId(resp.getUserId());

View File

@@ -4,9 +4,12 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.Account;
import com.ruoyi.cai.dto.admin.vo.AccountAdminVo;
import com.ruoyi.cai.dto.app.vo.rank.RankNodeRecharge;
import com.ruoyi.cai.rank.RankNode;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
/**
* 用户账户Mapper接口
@@ -38,4 +41,6 @@ public interface AccountMapper extends BaseMapper<Account> {
void incsPayTotal(@Param("userId") Long userId, @Param("rechargeCoin") Long rechargeCoin, @Param("price") BigDecimal price);
void incsTrdPayTotal(@Param("userId") Long userId, @Param("price") BigDecimal price);
List<RankNodeRecharge> rankTotalPay(@Param("limit") int limit);
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.cai.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.cai.domain.LoveRankToday;
import com.ruoyi.cai.dto.app.query.index.AnchorListQuery;
import com.ruoyi.cai.dto.app.vo.AnchorListVo;
import org.apache.ibatis.annotations.Param;
import java.time.LocalDate;
public interface LoveRankTodayMapper extends BaseMapper<LoveRankToday> {
void insRank(@Param("id") Long id, @Param("value") Long value);
Page<AnchorListVo> homePage(Page<Object> build, @Param("query") AnchorListQuery query, @Param("monday") LocalDate monday);
}

View File

@@ -1,9 +1,12 @@
package com.ruoyi.cai.mq.handle;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.cai.enums.rank.RankDataTypeEnum;
import com.ruoyi.cai.enums.rank.RankTypeEnum;
import com.ruoyi.cai.mq.CommonConsumerEnum;
import com.ruoyi.cai.mq.handle.dto.RankNotifyDTO;
import com.ruoyi.cai.rank.RankManager;
import com.ruoyi.cai.service.LoveRankTodayService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -14,10 +17,15 @@ public class RankNotifyHandle implements IHandle {
@Autowired
private RankManager rankManager;
@Autowired
private LoveRankTodayService loveRankTodayService;
@Override
public void run(String message) {
RankNotifyDTO rank = JSON.parseObject(message, RankNotifyDTO.class);
rankManager.addRank(rank);
if(RankTypeEnum.LOVE.getCode().equals(rank.getRankType())){
loveRankTodayService.addRank(rank);
}
}
@Override

View File

@@ -14,12 +14,14 @@ import com.ruoyi.cai.service.AccountService;
import com.ruoyi.cai.service.UserService;
import com.ruoyi.cai.util.CaiDateUtil;
import com.ruoyi.cai.util.CaiNumUtil;
import com.ruoyi.cai.ws.constant.RedisConstant;
import com.ruoyi.yunxin.YunExecutor;
import com.ruoyi.yunxin.Yunxin;
import com.ruoyi.yunxin.resp.SendMsgResp;
import com.ruoyi.yunxin.resp.YxCommonR;
import com.ruoyi.yunxin.resp.YxDataR;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -43,6 +45,8 @@ public class YunxinHttpService {
private CaiProperties caiProperties;
@Autowired
private UserService userService;
@Autowired
private RedissonClient redissonClient;
/**
* 注册赠送消息
@@ -247,14 +251,34 @@ public class YunxinHttpService {
});
}
private boolean checkFollowedSendMessage(Long toUid,
User followUser){
String key = String.format(RedisConstant.START_SEND_MESSAGE_CACHE, toUid);
Object o = redissonClient.getMap(key).get(followUser.getId());
return o != null;
}
private void setFollowedSendMessage(Long toUid,User followUser){
String key = String.format(RedisConstant.START_SEND_MESSAGE_CACHE, toUid);
redissonClient.getMap(key).put(followUser.getId(),1);
}
/**
* 关注发送消息
* @param toUid
* @param toUid 接收消息的用户
*/
public void followedSendMessage(Long toUid,
User followUser,
LocalDateTime followTime){
YunExecutor.YUN_EXECUTOR.execute(() -> {
// 检测是否发送过通知
boolean checked = checkFollowedSendMessage(toUid, followUser);
if(checked){
log.error("用户已经发送过关注通知,无需再次发送 被关注人id:{}, 关注人id{} 昵称:{} 编号:{}",
toUid, followUser.getId(), followUser.getNickname(),followUser.getUsercode());
return;
}
SendFollowNoticeData data = new SendFollowNoticeData();
data.setUserid(followUser.getId());
data.setNickname(followUser.getNickname());
@@ -268,6 +292,7 @@ public class YunxinHttpService {
if(r == null || !r.isSuccess()){
log.error("云信发送失败【sendCallAsync】r={}", JSON.toJSONString(r));
}
this.setFollowedSendMessage(toUid, followUser);
});
}

View File

@@ -0,0 +1,16 @@
package com.ruoyi.cai.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.cai.domain.LoveRankToday;
import com.ruoyi.cai.dto.app.query.index.AnchorListQuery;
import com.ruoyi.cai.dto.app.vo.AnchorListVo;
import com.ruoyi.cai.mq.handle.dto.RankNotifyDTO;
import com.ruoyi.common.core.domain.PageQuery;
import java.util.List;
public interface LoveRankTodayService extends IService<LoveRankToday> {
List<AnchorListVo> homePage(PageQuery pageQuery, AnchorListQuery query);
void addRank(RankNotifyDTO rank);
}

View File

@@ -0,0 +1,93 @@
package com.ruoyi.cai.service.impl;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.cai.domain.Anchor;
import com.ruoyi.cai.domain.LoveRankToday;
import com.ruoyi.cai.dto.app.query.index.AnchorListQuery;
import com.ruoyi.cai.dto.app.vo.AnchorListVo;
import com.ruoyi.cai.enums.rank.RankDataTypeEnum;
import com.ruoyi.cai.mapper.LoveRankTodayMapper;
import com.ruoyi.cai.mq.handle.dto.RankNotifyDTO;
import com.ruoyi.cai.service.AnchorService;
import com.ruoyi.cai.service.LoveRankTodayService;
import com.ruoyi.cai.util.IdLockManager;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.helper.LoginHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.temporal.TemporalAdjusters;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
@Service
public class LoveRankTodayServiceImpl extends ServiceImpl<LoveRankTodayMapper, LoveRankToday> implements LoveRankTodayService {
@Resource
private LoveRankTodayMapper loveRankTodayMapper;
@Autowired
private AnchorService anchorService;
@Override
public List<AnchorListVo> homePage(PageQuery pageQuery, AnchorListQuery query){
if(pageQuery.checkPageNum(20)){
return Collections.emptyList();
}
pageQuery.resetPageSize();
Anchor anchor = anchorService.getByUserId(LoginHelper.getUserId());
if(anchor != null && anchor.getHiddenStatus() == 1){
query.setHiddenStatusUser(true);
}
// 获取当前日期
LocalDate today = LocalDate.now();
// 获取本周的周一(如果今天是周一,则返回今天)
LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
Page<AnchorListVo> page = baseMapper.homePage(pageQuery.build(), query,monday);
return page.getRecords();
}
@Override
public void addRank(RankNotifyDTO rank) {
// 获取当前日期
LocalDate today = LocalDate.now();
// 获取本周的周一(如果今天是周一,则返回今天)
LocalDate monday = today.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
// 获取本周的周日(如果今天是周日,则返回今天)
LocalDate sunday = today.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
LoveRankToday one = this.getOne(Wrappers.lambdaQuery(LoveRankToday.class)
.eq(LoveRankToday::getUserId, rank.getUserId())
.eq(LoveRankToday::getDataType, RankDataTypeEnum.WEEK.getCode())
.eq(LoveRankToday::getBeginRankTime, monday)
.last("limit 1"));
if(one == null){
AtomicReference<LoveRankToday> atomicReference = new AtomicReference<>();
IdLockManager.executeWithLock(String.valueOf(rank.getUserId()), () -> {
LoveRankToday rankData = this.getOne(Wrappers.lambdaQuery(LoveRankToday.class)
.eq(LoveRankToday::getUserId, rank.getUserId())
.eq(LoveRankToday::getDataType, RankDataTypeEnum.WEEK.getCode())
.eq(LoveRankToday::getBeginRankTime, monday)
.last("limit 1"));
if(rankData == null){
LoveRankToday saveData = new LoveRankToday();
saveData.setDataType(RankDataTypeEnum.WEEK.getCode());
saveData.setRankTime(monday);
saveData.setBeginRankTime(monday);
saveData.setEndRankTime(sunday);
saveData.setNum(0L);
saveData.setUserId(rank.getUserId());
this.save(saveData);
atomicReference.set(saveData);
}
});
one = atomicReference.get();
}
loveRankTodayMapper.insRank(one.getId(), rank.getPrice());
}
}

View File

@@ -0,0 +1,38 @@
package com.ruoyi.cai.util;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class IdLockManager {
// 存储ID对应的锁对象
private static final ConcurrentHashMap<String, Lock> idLocks = new ConcurrentHashMap<>();
/**
* 获取指定ID的锁
*/
public static Lock getLock(String id) {
// 不存在则创建,存在则返回已有的锁
return idLocks.computeIfAbsent(id, k -> new ReentrantLock());
}
/**
* 执行带ID锁的任务
*/
public static void executeWithLock(String id, Runnable task) {
Lock lock = getLock(id);
try {
lock.lock(); // 获取锁
task.run(); // 执行任务
} finally {
lock.unlock(); // 确保释放锁
}
}
/**
* 清理不再使用的锁(可选)
*/
public static void cleanUpLock(String id) {
idLocks.remove(id);
}
}

View File

@@ -11,4 +11,5 @@ public class RedisConstant {
public static final String USER_ROOM_DATA = REDIS_P + "room:%s:%s";
public static final String INIT_ROOM_LOCK = REDIS_P + "lock:initRoom:%s-%s";
public static final String Y4X_REDIS_CACHE = REDIS_P + "shareUrl:y4x:%s";
public static final String START_SEND_MESSAGE_CACHE = REDIS_P + "starSendMessage:%s";
}