public void dealAfterOrderPayCallback(String userId,ReqOrderPayCallbackVO data) {
RedPacketExample example = new RedPacketExample();
final String packetNo = data.getPacketNo();
final String paySeq = data.getPaySeq();
final Integer payStatus = data.getPayStatus();
example.createCriteria().andPacketNoEqualTo(packetNo)
.andPaySeqEqualTo(paySeq)
.andOrderStatusEqualTo(1);//待支付状态
//更新订单支付状态
Date now = new Date();
RedPacket updateRedPacket = new RedPacket();
updateRedPacket.setOrderStatus(payStatus);
updateRedPacket.setUpdateTime(now);
updateRedPacket.setPayTime(now);
int i = redPacketMapper.updateByExampleSelective(updateRedPacket, example);
if (i != 1) {
throw new ServiceException(“订单状态更新失败”, ExceptionType.SYS_ERR);
}
if (payStatus == 2) {
RedPacketExample query = new RedPacketExample();
query.createCriteria().andPacketNoEqualTo(packetNo)
.andPaySeqEqualTo(paySeq)
.andOrderStatusEqualTo(2);
final RedPacket redPacket = redPacketMapper.selectByExample(query).get(0);
final List detailList = getRedPacketDetail(redPacket.getAmount(), redPacket.getNum());
final int size = detailList.size();
if (size <= 100) {
i = detailMapper.batchInsert(detailList, redPacket);
if (size != i) {
throw new ServiceException(“生成红包失败”, ExceptionType.SYS_ERR);
}
} else {
int times = size % 100 == 0 ? size / 100 : (size / 100 + 1);
for (int j = 0; j < times; j++) {
int fromIndex = 100 * j;
int toIndex = 100 * (j + 1) - 1;
if (toIndex > size - 1) {
toIndex = size - 1;
}
final List subList = detailList.subList(fromIndex, toIndex);
i = detailMapper.batchInsert(subList, redPacket);
if (subList.size() != i) {
throw new ServiceException(“生成红包失败”, ExceptionType.SYS_ERR);
}
}
}
final String redisKey = REDPACKET_NUM_PREFIX + redPacket.getPacketNo();
String lua = “local i = redis.call(‘setnx’,KEYS[1],ARGV[1])\r\n” +
“if i == 1 then \r\n” +
" local j = redis.call(‘expire’,KEYS[1],ARGV[2])\r\n" +
“end \r\n” +
“return i”;
//优化成lua脚本
final Long execute = redisTemplate.execute(new DefaultRedisScript<>(lua, Long.class), Arrays.asList(redisKey), size, 3600 * 24);
if (execute != 1L) {
throw new ServiceException(“生成红包失败”, ExceptionType.SYS_ERR);
}
//websocket通知在线用户收到新的红包
Websocket.sendMessageToUser(userId, JSONObject.toJSONString(redPacket));
}
}
/**
-
红包随机算法
-
@param amount 红包金额
-
@param num 红包数量
-
@return 随机红包集合
*/
private List getRedPacketDetail(BigDecimal amount, Integer num) {
List redPacketsList = new ArrayList<>(num);
//最小红包金额
final BigDecimal min = new BigDecimal(“0.01”);
//最少需要红包金额
final BigDecimal bigNum = new BigDecimal(num);
final BigDecimal atLastAmount = min.multiply(bigNum);
//出去最少红包金额后剩余金额
BigDecimal remain = amount.subtract(atLastAmount);
if (remain.compareTo(BigDecimal.ZERO) == 0) {
for (int i = 0; i < num; i++) {
redPacketsList.add(min);
}
return redPacketsList;
}
final Random random = new Random();
final BigDecimal hundred = new BigDecimal(“100”);
final BigDecimal two = new BigDecimal(“2”);
BigDecimal redPacket;
for (int i = 0; i < num; i++) {
if (i == num - 1) {
redPacket = remain;
} else {
//100内随机获得的整数
final int rand = random.nextInt(100);
redPacket = new BigDecimal(rand).multiply(remain.multiply(two).divide(bigNum.subtract(new BigDecimal(i)), 2, RoundingMode.CEILING)).divide(hundred, 2, RoundingMode.FLOOR);
}
if (remain.compareTo(redPacket) > 0) {
remain = remain.subtract(redPacket);
} else {
remain = BigDecimal.ZERO;
}
redPacketsList.add(min.add(redPacket));
}
return redPacketsList;
}
复制代码
页面加载成功后初始化websocket,监听后端新红包生成成功,动态添加红包到聊天窗口。
$(function (){
var websocket;
if(‘WebSocket’ in window) {
console.log(“此浏览器支持websocket”);
websocket = new WebSocket(“ws://127.0.0.1:8082/websocket/${session.id}”);
} else if(‘MozWebSocket’ in window) {
alert(“此浏览器只支持MozWebSocket”);
} else {
alert(“此浏览器只支持SockJS”);
}
websocket.onopen = function(evnt) {
console.log(“链接服务器成功!”)
};
websocket.onmessage = function(evnt) {
console.log(evnt.data);
var json = eval(‘(’+evnt.data+ ‘)’);
obj.addPacket(json.id,json.packetNo,json.userId)
};
websocket.onerror = function(evnt) {};
websocket.onclose = function(evnt) {
console.log(“与服务器断开了链接!”)
}
});
复制代码
抢红包设计
抢红包设计高并发,本地单机项目,通过原子Integer控制抢红包接口并发限制为20,
private AtomicInteger receiveCount = new AtomicInteger(0);
@PostMapping(“/receive”)
public CommonJsonResponse receiveOne(@Validated @RequestBody CommonJsonRequest vo) {
Integer num = null;
try {
//控制并发不要超过20
if (receiveCount.get() > 20) {
return new CommonJsonResponse(“9999”, “太快了”);
}
num = receiveCount.incrementAndGet();
final String s = orderService.receiveOne(vo.getData());
return StringUtils.isEmpty(s) ? CommonJsonResponse.ok() : new CommonJsonResponse(“9999”, s);
} finally {
if (num != null) {
receiveCount.decrementAndGet();
}
}
}
复制代码
对于没有领取过该红包的用户,在红包没有过期且红包还有剩余的情况下,抢红包成功,记录成功标识入redis,设置标识过期时间为5秒。
public String receiveOne(ReqReceiveRedPacketVO data) {
final Long redPacketId = data.getPacketId();
final String redPacketNo = data.getPacketNo();
final String redisKey = REDPACKET_NUM_PREFIX + redPacketNo;
if (!redisTemplate.hasKey(redisKey)) {
return “红包已经过期”;
}
final Integer num = (Integer) redisTemplate.opsForValue().get(redisKey);
if (num <= 0) {
return “红包已抢完”;
}
RedPacketDetailExample example = new RedPacketDetailExample();
example.createCriteria().andPacketIdEqualTo(redPacketId)
.andReceivedEqualTo(1)
.andUserIdEqualTo(data.getUserId());
final List details = detailMapper.selectByExample(example);
if (!details.isEmpty()) {
return “该红包已经领取过了”;
}
final String receiveKey = REDPACKET_RECEIVE_PREFIX + redPacketNo + “:” + data.getUserId();
//优化成lua脚本
String lua = “local i = redis.call(‘setnx’,KEYS[1],ARGV[1])\r\n” +
“if i == 1 then \r\n” +
" local j = redis.call(‘expire’,KEYS[1],ARGV[2])\r\n" +
“end \r\n” +
“return i”;
//优化成lua脚本
final Long execute = redisTemplate.execute(new DefaultRedisScript<>(lua, Long.class), Arrays.asList(receiveKey), 1, 5);
if (execute != 1L) {
return “太快了”;
}
return “”;
}
复制代码
拆红包设计
在用户抢红包成功标识未过期的状态下,且红包未过期红包未领完时,从数据库中领取一个红包,领取成功将领取记录写入redis以供查询过期时间为48小时。
@Transactional(rollbackFor = Exception.class)
public String openRedPacket(ReqReceiveRedPacketVO data) {
final Long packetId = data.getPacketId();
final String packetNo = data.getPacketNo();
final String userId = data.getUserId();
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
最后更多分享:前端字节跳动真题解析
g-42x1FzsO-1711787702681)]
[外链图片转存中…(img-5Iw4eLuJ-1711787702682)]
[外链图片转存中…(img-hX4NOttx-1711787702682)]
[外链图片转存中…(img-lZqisqGV-1711787702683)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024c (备注前端)
[外链图片转存中…(img-AlB4D6w9-1711787702684)]
最后更多分享:前端字节跳动真题解析
- [外链图片转存中…(img-MrD2hQzN-1711787702684)]