东莞型网站建设,怎么做网站推销自己的产品,如何设置网站默认首页,网站建设实验小结本博客为个人学习笔记#xff0c;学习网站与详细见#xff1a;黑马程序员Redis入门到实战 P88 - P95 目录
附近商铺
数据导入
功能实现
用户签到
签到功能
连续签到统计
UV统计 附近商铺
利用Redis中的GEO数据结构实现附近商铺功能#xff0c;常见命令如下图所示。… 本博客为个人学习笔记学习网站与详细见黑马程序员Redis入门到实战 P88 - P95 目录
附近商铺
数据导入
功能实现
用户签到
签到功能
连续签到统计
UV统计 附近商铺
利用Redis中的GEO数据结构实现附近商铺功能常见命令如下图所示。
key值由特定前缀与商户类型id组成每个GEO存储一个店铺id与该店铺的经纬度信息如下图所示。 数据导入
编写单元测试将MySql数据库中的所有商铺位置信息导入Redis中代码如下。
Test
void loadShopData() {// 1.查询所有店铺信息ListShop shops shopService.list();// 2.将店铺按照typeId分组typeId一致的放到一个集合中MapLong, ListShop map shops.stream().collect(Collectors.groupingBy(Shop::getTypeId));// 3.分批完成写入Redisfor (Map.EntryLong, ListShop entry : map.entrySet()) {// 3.1 获取类型idLong typeId entry.getKey();String key shop:geo: typeId;// 3.2 获取同类型的店铺集合ListShop list entry.getValue();// 3.3 写入redis GEOADD key 经度 纬度 memberListRedisGeoCommands.GeoLocationString locations new ArrayList(list.size());for (Shop shop : list) {locations.add(new RedisGeoCommands.GeoLocation(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}
} 功能实现 由于SpringDataRedis的2.3.9版本并不支持Redis 6.2提供的GEOSEARCH命令因此我们需要修改版本代码如下。
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactIdexclusionsexclusionartifactIdspring-data-redis/artifactIdgroupIdorg.springframework.data/groupId/exclusionexclusionartifactIdlettuce-core/artifactIdgroupIdio.lettuce/groupId/exclusion/exclusions
/dependency
dependencygroupIdorg.springframework.data/groupIdartifactIdspring-data-redis/artifactIdversion2.6.2/version
/dependency
dependencygroupIdio.lettuce/groupIdartifactIdlettuce-core/artifactIdversion6.1.6.RELEASE/version
/dependency
Controller层代码如下。
GetMapping(/of/type)
public Result queryShopByType(RequestParam(typeId) Integer typeId,RequestParam(value current, defaultValue 1) Integer current,RequestParam(value x, required false) Double x,RequestParam(value y, required false) Double y
) {return shopService.queryShopByType(typeId, current, x, y);
}
接口方法的具体实现代码如下。
Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {// 1.判断是否需要根据坐标查询if (x null || y null) {// 不需要坐标查询按数据库查询PageShop page query().eq(type_id, typeId).page(new Page(current, SystemConstants.DEFAULT_PAGE_SIZE));// 返回数据return Result.ok(page.getRecords());}// 2.计算分页参数int from (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end current * SystemConstants.DEFAULT_PAGE_SIZE;// 3.查询redis、按照距离排序、分页。结果shopId,distanceString key shop:geo: typeId;GeoResultsRedisGeoCommands.GeoLocationString results stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 4.解析出idif (results null)return Result.ok(Collections.emptyList());ListGeoResultRedisGeoCommands.GeoLocationString list results.getContent();// 如果没有下一页则结束if (list.size() from)return Result.ok(Collections.emptyList());// 4.1 截取从from~end的部分ArrayListObject ids new ArrayList(list.size());MapString, Distance distanceMap new HashMap(list.size());list.stream().skip(from).forEach(result - {// 4.2 获取店铺idString shopIdStr result.getContent().getName();ids.add(Long.valueOf(shopIdStr));// 4.2 获取距离Distance distance result.getDistance();distanceMap.put(shopIdStr, distance);});// 5.根据id查询ShopString idStr StrUtil.join(,, ids);ListShop shops query().in(id, ids).last(ORDER BY FIELD(id, idStr )).list();for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());}// 6.返回return Result.ok(shops);
} 用户签到
签到功能 Controller层代码如下。
PostMapping(/sign)
public Result sign() {return userService.sign();
}
接口方法的具体实现代码如下。
Override
public Result sign() {// 1.获取当前登录用户信息Long userId UserHolder.getUser().getId();// 2.获取当前日期LocalDateTime now LocalDateTime.now();// 3.拼接keyString keySuffix now.format(DateTimeFormatter.ofPattern(:yyyyMM));String key sign: userId keySuffix;// 4.计算今天是本月的第几天int dayOfMonth now.getDayOfMonth();// 5.写入Redis SETBIT key offset// true写入1// false写入0stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
} 连续签到统计 Controller层代码如下。
GetMapping(/sign/count)
public Result signCount() {return userService.signCount();
}
接口方法的具体实现代码如下。
Override
public Result signCount() {// 1.获取当前登录用户信息Long userId UserHolder.getUser().getId();// 2.获取当前日期LocalDateTime now LocalDateTime.now();// 3.拼接keyString keySuffix now.format(DateTimeFormatter.ofPattern(:yyyyMM));String key sign: userId keySuffix;// 4.计算今天是本月的第几天int dayOfMonth now.getDayOfMonth();// 5.获取本月截止今天为止的所有签到记录返回结果是一个十进制数字 BITFIELD sign:5:202203 GET u14 0ListLong result stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create() //创建子命令.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)//选择子命令);if (result null || result.isEmpty())return Result.ok(0);// 获取本月签到位图Long num result.get(0);if (num null || num 0)return Result.ok(0);// 6.循环遍历int cnt 0;//记录连续签到天数while (true) {if ((num 1) 0)break;num 1;cnt;}return Result.ok(cnt);
} UV统计 测试 我们直接利用单元测试向HyperLogLog中添加100万条数据看看统计效果如何测试代码如下。
Test
void testHyperLogLog() {// 准备数组装用户数据String[] users new String[1000];// 数组角标int index 0;for (int i 1; i 1000000; i) {// 赋值users[index] user_ i;// 每1000条发送一次if (i % 1000 0) {index 0;stringRedisTemplate.opsForHyperLogLog().add(hll1, users);}}// 统计数量Long size stringRedisTemplate.opsForHyperLogLog().size(hll1);System.out.println(size size);
}
测试结果如下图所示。误差 1 - 997593 / 1000000≈ 0.002 可忽略不计