Fivk博客 - 项目实战
type: channel
link: https://blog.fivk.cn/category/project/
items: 【探花交友】推荐系统&实战简介
items: 【探花交友】数据统计与内容审核
items: 【探花交友】后台系统
items: 【探花交友】小视频方案
items: 【探花交友】附近的人
items: 【开发技巧】thymeleaf，一个现代服务器端 Java 模板引擎
items: 【探花交友】环信API方法介绍
items: 【探花交友】即时通信
items: 【探花交友】圈子互动
items: 【Mongo】分片集群搭建

【探花交友】推荐系统&实战简介
type: item
date: 2023-04-12T21:04:00+08:00
description: 1、了解推荐系统1.1、什么是推荐系统？为了解决信息过载和用户无明确需求的问题，找到用户感兴趣的物品，才有了个性化推荐系统。其实，解决信息过载的问题，代表性的解决方案是分类目录和搜索引擎，如hao123，电商首页的分类目录以及百度，360搜索等。不过分类目录和搜索引擎只能解决用户主动查找信息的需求，即用户知道自己想要什么，并不能解决用户没用明确需求很随便的问题。经典语录是：你想吃什么，随便！面对这种很随便又得罪不起的用户（女友和上帝），只能通过分析用户的历史行为给用户的兴趣建模，从而主动给用户推荐能够满足他们兴趣和需求的信息。比如问问女友的闺蜜，她一般什么时候喜欢吃什么。1.2、电商是推荐系统的先行者[ ] 电子商务网站是个性化推荐系统重要地应用的领域之一，亚马逊就是个性化推荐系统的积极应用者和推广者，亚马逊的推荐系统深入到网站的各类商品，为亚马逊带来了至少30%的销售额。[ ] 不光是电商类，推荐系统无处不在。QQ，微信的好友推荐；新浪微博的你可能感兴趣的人；优酷，土豆的电影推荐；豆瓣的图书推荐；大从点评的餐饮推荐；脉脉的同事推荐等。[ ] 推荐引擎的鼻祖思想源泉：http://portal.acm.org/citation.cfm?id=1070751[ ] 亚马逊最早提出基于物品的协同过滤推荐算法：http://portal.acm.org/citation.cfm?id=372071京东的推荐： 1.3、推荐系统业务流程推荐系统广泛存在于各类网站中，作为一个应用为用户提供个性化的推荐。它需要一些用户的历史数据，一般由三个部分组成：基础数据、推荐算法系统、前台展示。基础数据包括很多维度，包括用户的访问、浏览、下单、收藏，用户的历史订单信息，评价信息等很多信息；推荐算法系统主要是根据不同的推荐诉求由多个算法组成的推荐模型；前台展示主要是对客户端系统进行响应，返回相关的推荐信息以供展示。1.4、协同过滤推荐算法迄今为止，在个性化推荐系统中，协同过滤技术是应用最成功的技术。目前国内外有许多大型网站应用这项技术为用户更加智能（个性化、千人千面）的推荐内容。核心思想：协同过滤一般是在海量的用户中发掘出一小部分和你品位比较类似的，在协同过滤中，这些用户成为邻居，然后根据他们喜欢的其他东西组织成一个排序的目彔作为推荐给你。1.4.1、基于用户的推荐 UserCF对于用户A，根据用户的历史偏好，这里只计算得到一个邻居–用户C，然后将用户C 喜欢的物品D 推荐给用户A。基于用户的协同过滤算法先计算的是用户与用户的相似度（兴趣相投，物以类聚人以群分），然后将相似度比较接近的用户A购买的物品推荐给用户B，专业的说法是该算法用最近邻居（nearest-neighbor）算法找出一个用户的邻居集合，该集合的用户和该用户有相似的喜好，算法根据邻居的偏好对该用户进行预测。1.4.2、基于商品的推荐 ItemCF基于ItemCF的原理和基于UserCF类似，只是在计算邻居时采用物品本身，而不是从用户的角度，即基于用户对物品的偏好找到相似的物品，然后根据用户的历史偏好，推荐相似的物品给他。从计算的角度看，就是将所有用户对某个物品的偏好作为一个向量来计算物品之间的相似度，得到物品的相似物品后，根据用户历史的偏好预测当前用户还没有表示偏好的物品，计算得到一个排序的物品列表作为推荐。解释：对于物品A，根据所有用户的历史偏好，喜欢物品A 的用户都喜欢物品C，得出物品A 和物品C 比较相似，而用户C 喜欢物品A，那么可以推断出用户C 可能也喜欢物品C。1.5、ALS算法ALS 是交替最小二乘 （alternating least squares）的简称。在机器学习的上下文中，ALS 特指使用交替最小二乘求解的一个协同推荐算法。它通过观察到的所有用户给产品的打分，来推断每个用户的喜好并向用户推荐适合的产品。从协同过滤的分类来说，ALS算法属于User-Item CF，也叫做混合CF。它同时考虑了User和Item两个方面。用户和商品的关系，可以抽象为如下的三元组：<User,Item,Rating>。其中，Rating是用户对商品的评分，表征用户对该商品的喜好程度。如下：196 242 3 881250949 186 302 3 891717742 22 377 1 878887116 244 51 2 880606923 166 346 1 886397596 298 474 4 884182806 115 265 2 881171488 253 465 5 891628467 305 451 3 886324817 6 86 3 883603013 62 257 2 879372434 286 1014 5 879781125 200 222 5 8760423…

【探花交友】数据统计与内容审核
type: item
date: 2023-04-10T09:31:00+08:00
description: 1、用户冻结解冻用户冻结/解冻使用管理员在后台系统对用户的惩罚措施。对于发布不当言论或者违法违规内容的用户，可以暂时、永久禁止其登录，评论，发布动态、后台中解冻/冻结，就是将用户状态写入数据库中APP端用户在进行登录，评论，发布动态时检测Redis中冻结状态1.1 用户冻结ManageController//用户冻结 @PostMapping("/users/freeze") public ResponseEntity freeze(@RequestBody Map params) { Map map = managerService.userFreeze(params); return ResponseEntity.ok(map); }ManageService//用户冻结 public Map userFreeze(Map params) { Integer freezingTime = (Integer) params.get("freezingTime"); Long userId = (Long) params.get("userId"); int days = 0; if (freezingTime == 1) { days = 3; } if (freezingTime == 2) { days = 7; } if (freezingTime == 3) { days = -1; } String value = JSON.toJSONString(params); redisTemplate.opsForValue().set(Constants.FREEZE_USER+userId,value,days, TimeUnit.MINUTES); Map map = new HashMap(); map.put("message","冻结成功"); return map; }1.2 用户解冻ManageController//用户解冻 @PostMapping("/users/unfreeze") public ResponseEntity unfreeze(@RequestBody Map params) { Map map = managerService.userUnfreeze(params); return ResponseEntity.ok(map); }ManageService//用户解冻 public Map userUnfreeze(Map params) { Long userId = (Long) params.get("userId"); String reasonsForThawing = (String) params.get("reasonsForThawing"); redisTemplate.delete(Constants.FREEZE_USER+userId); Map map = new HashMap(); map.put("message","解冻成功"); return map; }1.3 查询数据列表UserInfo添加字段//用户状态,1为正常，2为冻结 @TableField(exist = false) private String userStatus = "1";ManageServicepublic ResponseEntity findById(Long userId) { UserInfo info = userInfoApi.findById(userId); if(redisTemplate.hasKey(Constants.FREEZE_USER+info.getId())) { info.setUserStatus("2"); } return ResponseEntity.ok(info); }1.4 探花系统修改UserFreezeService@Service public class UserFreezeService { @Autowired private RedisTemplate<String,String> redisTemplate; public void checkUserStatus(Integer state,Long userId) { String value = redisTemplate.opsForValue().get(Constants.FREEZE_USER + userId); if(!StringUtils.isEmpty(value)) { Map map = JSON.parseObject(value, Map.class); Integer freezingRange = (Integ…

【探花交友】后台系统
type: item
date: 2023-04-02T16:01:00+08:00
description: 1、项目架构1.1 概述API网关有很多实现方式，我们通过SpringCloud Gateway实现使用Nacos作为配置中心1.2 API网关1.2.1 搭建网关依赖<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- nacos配置中心依赖支持 <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> </dependency> --> <dependency> <groupId>com.itheima</groupId> <artifactId>tanhua-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>引导类@SpringBootApplication public class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } }跨域问题配置类package com.itheima.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.reactive.CorsWebFilter; import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; import org.springframework.web.util.pattern.PathPatternParser; /** * 跨域支持 */ @Configuration public class CorsConfig { @Bean public CorsWebFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); config.addAllowedMethod("*"); config.addAllowedOrigin("*"); config.addAllowedHeader("*"); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser()); source.registerCorsConfiguration("/**", config); return new CorsWebFilter(source); } }配置文件server: port: 8888 spring: profiles: active: prod application: name: tanhua-gateway cloud: nacos: discovery: server-addr: 192.168.136.160:8848 gateway: globalcors: add-to-simple-url-handler-mapping: true corsConfig…

【探花交友】小视频方案
type: item
date: 2023-03-29T11:33:00+08:00
description: 我的动态（查询思路）圈子的互动（点赞，喜欢，评论）数据表：quanzi_comment(commentType记录操作类型)点赞，喜欢，评论（保存记录）取消点赞，取消喜欢（删除记录）为了保证效率（结合redis缓存，互动数量记录到动态表）课程说明小视频功能说明FastDFS环境搭建小视频的功能实现1、我的访客查询别人来访了我的主页的信息，其他用户在浏览我的主页时，需要记录访客数据。访客在一天内每个用户只记录一次。查询数据时，如果用户查询过列表，就需要记录这次查询数据的时间，下次查询时查询大于等于该时间的数据。如果，用户没有记录查询时间，就查询最近的5个来访用户。1.1、dubbo服务1.1.1、实体对象@Data @NoArgsConstructor @AllArgsConstructor @Document(collection = "visitors") public class Visitors implements java.io.Serializable{ private static final long serialVersionUID = 2811682148052386573L; private ObjectId id; private Long userId; //我的id private Long visitorUserId; //来访用户id private String from; //来源，如首页、圈子等 private Long date; //来访时间 private String visitDate;//来访日期 private Double score; //得分 }1.1.2、定义接口package com.tanhua.dubbo.server.api; import com.tanhua.dubbo.server.pojo.Visitors; import java.util.List; public interface VisitorsApi { /** * 保存访客数据 * * @param userId 我的id * @param visitorUserId 访客id * @param from 来源 * @return */ String save(Long userId, Long visitorUserId, String from); /** * 查询我的访客数据，存在2种情况： * 1. 我没有看过我的访客数据，返回前5个访客信息 * 2. 之前看过我的访客，从上一次查看的时间点往后查询5个访客数据 */ List<Visitors> queryMyVisitor(Long userId); }1.1.3、编写实现package com.tanhua.dubbo.server.api; import cn.hutool.core.convert.Convert; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.dubbo.config.annotation.Service; import com.tanhua.dubbo.server.pojo.RecommendUser; import com.tanhua.dubbo.server.pojo.Visitors; import org.bson.types.ObjectId; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.redis.core.RedisTemplate; import java.util.List; @Service(version = "1.0.0") public class VisitorsApiImpl implements VisitorsApi { @Autowire…

【探花交友】附近的人
type: item
date: 2023-03-25T16:51:00+08:00
description: 课程说明实现探花功能MongoDB geo搜附近上报地理位置搜附近1、探花探花功能是将推荐的好友随机的通过卡片的形式展现出来，用户可以选择左滑、右滑操作，左滑：“不喜欢”，右滑：“喜欢”。喜欢：如果双方喜欢，那么就会成为好友。如果已经喜欢或不喜欢的用户在列表中不再显示。1.1、查询推荐列表dubbo服务1.1.1、实体对象package com.tanhua.dubbo.server.pojo; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.bson.types.ObjectId; import org.springframework.data.mongodb.core.index.Indexed; import org.springframework.data.mongodb.core.mapping.Document; @Data @NoArgsConstructor @AllArgsConstructor @Document(collection = "user_like") public class UserLike implements java.io.Serializable { private static final long serialVersionUID = 6739966698394686523L; private ObjectId id; @Indexed private Long userId; //用户id，自己 @Indexed private Long likeUserId; //喜欢的用户id，对方 private Boolean isLike; // 是否喜欢 private Long created; //创建时间 private Long updated; // 更新时间 }1.1.2、定义接口RecommendUserApi/** * 查询探花列表，查询时需要排除喜欢和不喜欢的用户 */ List<RecommendUser> queryCardsList(Long userId, int count);1.1.3、编写实现RecommendUserApiImpl/** * 查询探花列表，查询时需要排除喜欢和不喜欢的用户 * 1、排除喜欢，不喜欢的用户 * 2、随机展示 * 3、指定数量 */ public List<RecommendUser> queryCardsList(Long userId, int counts) { //1、查询喜欢不喜欢的用户ID List<UserLike> likeList = mongoTemplate.find(Query.query(Criteria.where("userId").is(userId)), UserLike.class); List<Long> likeUserIdS = CollUtil.getFieldValues(likeList, "likeUserId", Long.class); //2、构造查询推荐用户的条件 Criteria criteria = Criteria.where("toUserId").is(userId).and("userId").nin(likeUserIdS); //3、使用统计函数，随机获取推荐的用户列表 TypedAggregation<RecommendUser> newAggregation = TypedAggregation.newAggregation(RecommendUser.class, Aggregation.match(criteria),//指定查询条件 Aggregation.sample(counts) ); AggregationResults<RecommendUser> results = mongoTemplate.aggregate(newAggregation, RecommendUser.class); //4、构造返回 return results.getMappedResults(); }1.1.4、单元测试//com.tanhua.dubbo.server.api.TestRecommendUserApi @Test public void testQueryCardList(){ this.recommendUserApi.queryCardsList(106l, 10) .forEach(recommendUser -> System.out.println(recommendUser)…

【开发技巧】thymeleaf，一个现代服务器端 Java 模板引擎
type: item
date: 2023-03-06T13:04:00+08:00
description: 一、thymeleaf简介Thymeleaf是一种Java模板引擎，它可以将HTML、XML、JavaScript或文本模板转换为可以在Web浏览器中渲染的模板。Thymeleaf可以用于创建静态页面，也可以与Spring框架集成来创建动态Web应用程序。使用Thymeleaf，您可以将动态数据绑定到HTML模板中的任何元素上，例如表单字段、文本、图片、链接等。它支持标准HTML标签和属性，同时提供了额外的属性和标签，以支持Thymeleaf的特定功能。Thymeleaf的优点包括：易于学习和使用：Thymeleaf的语法与标准的HTML相似，使得学习和使用它变得非常容易。可以与Spring框架无缝集成：Thymeleaf是Spring框架的推荐模板引擎，它可以与Spring的各种组件无缝集成，例如Spring MVC、Spring Boot等。支持多种模板模式：Thymeleaf支持HTML、XML、JavaScript和纯文本模板，使得它可以适用于各种Web应用程序场景。支持国际化：Thymeleaf提供了良好的国际化支持，使得您可以轻松地创建多语言Web应用程序。可以在浏览器端预览：由于Thymeleaf使用标准的HTML语法，因此您可以在浏览器中预览和调试模板。二、使用案例假设有一个Web应用程序需要显示所有用户的姓名和年龄信息。使用Thymeleaf可以创建一个HTML模板，并将动态数据绑定到模板上。HTML模板代码如下： User List User List Name Age 在此模板中，使用Thymeleaf的特定属性（例如“th:each”和“th:text”）来绑定动态数据。在表格中，使用“th:each”属性遍历所有用户，并使用“th:text”属性将用户的姓名和年龄绑定到表格单元格中。接下来，在Java代码中创建用户对象列表，并将其绑定到HTML模板中：<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>@Controller public class UserController { @GetMapping("/users") public String getUserList(Model model) { List<User> users = userService.getUserList(); model.addAttribute("users", users); return "userList"; } }在此代码中，使用Spring MVC的@Controller注解来创建一个控制器，并使用@GetMapping注解来定义请求路径。在getUserList方法中，调用userService.getUserList()方法获取所有用户对象，并将它们添加到模型中。最后，返回模板名称userList。通过上述代码，Thymeleaf会将模板与动态数据合并并生成最终的HTML输出，该输出将在浏览器中呈现所有用户的姓名和年龄信息的表格。Thymeleaf的模板文件放置在src/main/resources/templates目录下，然后在控制器中指定要使用的模板文件名称，Spring Boot框架会自动将该模板文件渲染成HTML页面并返回给客户端浏览器。三、评价Thymeleaf是一个功能强大且易于学习和使用的Java模板引擎，它提供了丰富的标签和属性来绑定动态数据和控制模板生成。它的语法类似于标准的HTML，使得开发人员可以很容易地将其集成到Web应用程序中。另外，Thymeleaf提供了良好的国际化支持，使得开发多语言Web应用程序变得非常简单。与其他模板引擎相比，Thymeleaf的一个优点是可以在浏览器端预览模板，从而方便开发人员调试模板。此外，Thymeleaf也可以与Spring框架无缝集成，因此在使用Spring框架的Java Web应用程序中使用Thymeleaf会更加方便。总的来说，Thymeleaf是一种非常实用和强大的Java模板引擎，它可以提高开发人员的工作效率，同时提供了良好的灵活性和扩展性，因此在Java Web应用程序开发中广泛应用。

【探花交友】环信API方法介绍
type: item
date: 2023-03-01T09:39:00+08:00
description: 安装如果你的项目使用Maven构建并且spring boot是2.4.3以上版本，可以在pom.xml中添加下面代码：<dependency> <groupId>com.easemob.im</groupId> <artifactId>im-sdk-core</artifactId> <version>0.5.9</version> </dependency>如果你使用的spring-boot是2.4.3以下版本的，还需要在pom.xml中添加:<dependencyManagement> <dependencies> <dependency> <groupId>io.netty</groupId> <artifactId>netty-bom</artifactId> <version>4.1.59.Final</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-bom</artifactId> <version>2020.0.4</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>准备在使用Server SDK之前，需要准备环信appkey、Client ID、ClientSecre。如果你有环信管理后台账号并创建过应用，请先登录环信管理后台，点击 这里，然后到“应用列表” → 点击“查看”即可获取到appkey、Client ID、ClientSecret。使用EMService是所有API的入口，可以这样初始化：EMProperties properties = EMProperties.builder() .setAppkey(cliProperties.getAppkey()) .setClientId(cliProperties.getClientId()) .setClientSecret(cliProperties.getClientSecret()) .build(); EMService service = new EMService(properties);根据业务资源，API分为：Attachment 用于上传下载附件Block 用于限制访问Contact 用于管理联系人Group 用于管理群组Message 用于发送消息User 用于管理用户Room 用于管理聊天室后续用到的API所有的API上面链接可查// 创建环信用户 service.user().create("user01", "123456").block(); // 添加好友 service.contact().add("user01, "user02").block(); // 删除好友 service.contact().remove("user01", "uer02").block(); // 服务端发送消息 Set<String> set = CollUtil.newHashSet("123"); service.message().send("user01", "users", set, new EMTextMessage().text("java"), null) .block();

【探花交友】即时通信
type: item
date: 2023-03-01T09:33:00+08:00
description: 1、即时通信1.1、什么是即时通信？1.2、功能说明在探花交友项目中也提供了类似微信的聊天功能，用户可以和好友或陌生人聊天。如果是陌生人，通过《聊一下》功能进行打招呼，如果对方同意后，就成为了好友，可以进行聊天了。陌生人之间如果相互喜欢，那么就会成为好友，也就可以聊天了。在消息界面中也可以查看：点赞、评论、喜欢、公告等消息信息。1.3、技术方案对于高并发的即时通讯实现，还是很有挑战的，所需要考虑的点非常多，除了要实现功能，还要考虑并发、流量、负载、服务器、容灾等等。虽然有难度也并不是高不可攀。对于现实即时通讯往往有两种方案：方案一：自主实现，从设计到架构，再到实现。技术方面可以采用：Netty + WebSocket + RocketMQ + MongoDB + Redis + ZooKeeper + MySQL方案二：对接第三方服务完成。这种方式简单，只需要按照第三方的api进行对接就可以了。如：环信、网易、容联云通讯等。如何选择呢？如果是中大型企业做项目可以选择自主研发，如果是中小型企业研发中小型的项目，选择第二种方案即可。方案一需要有大量的人力、物力的支持，开发周期长，成本高，但可控性强。方案二，成本低，开发周期短，能够快速的集成起来进行功能的开发，只是在可控性方面来说就差了一些。探花交友项目选择方案二进行实现。2、环信官网：https://www.easemob.com/ 稳定健壮，消息必达，亿级并发的即时通讯云2.1、开发简介平台架构：集成：环信和用户体系的集成主要发生在2个地方，服务器端集成和客户端集成。探花集成：探花前端使用AndroidSDK进行集成文档：http://docs-im.easemob.com/im/android/sdk/import后端集成用户体系文档：http://docs-im.easemob.com/im/server/ready/user2.2、环信Console需要使用环信平台，那么必须要进行注册，登录之后即可创建应用。环信100以内的用户免费使用，100以上就要注册企业版了。企业版价格：创建应用：创建完成：2.3、接口说明2.3.1、Appkey 数据结构当您申请了 AppKey 后，会得到一个 xxxx#xxxx 格式的字符串，字符串只能由小写字母数字组成，AppKey是环信应用的唯一标识。前半部分 org_name 是在多租户体系下的唯一租户标识，后半部分 app_name 是租户下的app唯一标识（在环信后台创建一个app时填写的应用 id 即是 app_name ）。下述的 REST API 中，/{org_name}/{app_name}的请求，均是针对一个唯一的appkey进行的。目前环信注册的appkey暂不能由用户自己完成删除操作，如果对 APP 删除需要联系环信操作完成。Appkeyxxxx分隔符xxxx环信应用的唯一标识org_name#app_name2.3.2、环信 ID 数据结构环信作为一个聊天通道，只需要提供环信 ID （也就是 IM 用户名）和密码就够了。名称字段名数据类型描述环信 IDusernameString在 AppKey 的范围内唯一用户名。用户密码passwordString用户登录环信使用的密码。2.3.4、获取管理员权限环信提供的 REST API 需要权限才能访问，权限通过发送 HTTP 请求时携带 token 来体现，下面描述获取 token 的方式。说明：API 描述的时候使用到的 {APP 的 client_id} 之类的这种参数需要替换成具体的值。重要提醒：获取 token 时服务器会返回 token 有效期，具体值参考接口返回的 expires_in 字段值。由于网络延迟等原因，系统不保证 token 在此值表示的有效期内绝对有效，如果发现 token 使用异常请重新获取新的 token，比如“http response code”返回 401。另外，请不要频繁向服务器发送获取 token 的请求，同一账号发送此请求超过一定频率会被服务器封号，切记，切记！！client_id 和 client_secret 可以在环信管理后台的 APP 详情页面看到。HTTP Request/{org_name}/{app_name}/token Request Headers参数说明Content-Typeapplication/jsonRequest Body参数说明grant_typeclient_credentialsclient_idApp的client_id，可在app详情页找到client_secretApp的client_secret，可在app详情页找到Response Body参数说明access_token有效的tok…

【探花交友】圈子互动
type: item
date: 2023-01-11T15:49:00+08:00
description: 课程说明圈子动态查询圈子实现评论圈子实现点赞、喜欢功能圈子实现评论1、动态查询我的动态：查询个人发布的动态列表（分页查询），和之前实现的好友动态，推荐动态实现逻辑是一致。1.1、查询好友动态查询好友动态与查询推荐动态显示的结构是一样的，只是其查询数据源不同1.1.1、接口文档API接口文档：http://192.168.136.160:3000/project/19/interface/api/1421.1.2、代码步骤Controller层接受请求参数Service数据封装调用API查询好友动态详情数据调用API查询动态发布人详情构造VO对象API层根据用户ID查询好友发布动态详情查询好友时间线表查询动态详情1.1.3、代码实现MovementController /** * 查询好友动态 */ @GetMapping public ResponseEntity movements(@RequestParam(defaultValue = "1") Integer page, @RequestParam(defaultValue = "10") Integer pagesize) { PageResult pr = movementService.findFriendMovements(page,pagesize); return ResponseEntity.ok(pr); }MovementService//好友动态 public PageResult findFriendMovements(Integer page, Integer pagesize) { //1、获取当前用户id Long userId = UserHolder.getUserId(); //2、查询数据列表 List<Movement> items = movementApi.findFriendMovements(userId,page,pagesize); //3、非空判断 if(CollUtil.isEmpty(items)) { return new PageResult(); } //4、获取好友用户id List<Long> userIds = CollUtil.getFieldValues(items, "userId", Long.class); //5、循环数据列表 Map<Long, UserInfo> userMaps = userInfoApi.findByIds(userIds, null); List<MovementsVo> vos = new ArrayList<>(); for (Movement item : items) { //5、一个Movement构建一个Vo对象 UserInfo userInfo = userMaps.get(item.getUserId()); MovementsVo vo = MovementsVo.init(userInfo, item); vos.add(vo); } //6、构建返回值 return new PageResult(page,pagesize,0L,vos); }movementApi@Override public List<Movement> findFriendMovements(Long friendId, Integer page, Integer pagesize) { //1、查询好友时间线表 Query query = Query.query(Criteria.where("friendId").in(friendId)) .skip((page - 1)*pagesize).limit(pagesize) .with(Sort.by(Sort.Order.desc("created"))); List<MovementTimeLine> lines = mongoTemplate.find(query, MovementTimeLine.class); //2、提取动态id集合 List<ObjectId> movementIds = CollUtil.getFieldValues(lines, "movementId", ObjectId.class); //3、根据动态id查询动态详情 Query movementQuery = Query.query(Criteria.where("id").in(movementIds)); return mongoTemplate.find(movementQuery, Movement.class); }1.2、查询推荐动态推荐动态是通过推荐系统计算出的结果，现在我们只需要实现查询即可，推荐系统在后…

【Mongo】分片集群搭建
type: item
date: 2023-01-10T15:25:00+08:00
description: 分片集群高数据量和吞吐量的数据库应用会对单机的性能造成较大压力,大的查询量会将单机的CPU耗尽,大的数据量对单机的存储压力较大,最终会耗尽系统的内存而将压力转移到磁盘IO上。 为了解决这些问题,有两个基本的方法: 垂直扩展和水平扩展。 垂直扩展：增加更多的CPU和存储资源来扩展容量。 水平扩展：将数据集分布在多个服务器上。水平扩展即分片。原理介绍分片（sharding）是MongoDB用来将大型集合分割到不同服务器（或者说一个集群）上所采用的方法。例如，如果数据库1tb的数据集，并有4个分片，然后每个分片可能仅持有256 GB的数据。如果有40个分片，那么每个切分可能只有25GB的数据。MongoDB中数据的分片是以集合为基本单位的，集合中的数据通过片键（Shard key）被分成多部分。其实片键就是在集合中选一个键，用该键的值作为数据拆分的依据。一般片键采用范围或哈希的方式进行分片。集群搭建环境准备先创建一个如下的目录结果:在root目录下创建mongodb目录。内部依次创建文件夹（node1，node2，node3）。在node1文件夹下创建：config-server1，mongos1，shard11，shard21文件夹config-server1目录下创建backup，config，db文件夹mongos1，shard11，shard21同上创建在node2和node3文件夹下创建配置服务创建容器进入node1，node2，node3的config-serverx目录。执行docker命令创建并启动docker容器配置服务1#进入目录 cd /root/mongodb/node1/config-server1 #创建并启动容器。 docker run --restart=always --privileged=true -p 10021:27019 -v $PWD/config:/etc/mongod -v $PWD/db:/data/db -d --name pro-file-server-config1 mongo:4.0.0 -f /etc/mongod/config.conf --configsvr --replSet "rs-file-server-config-server" --bind_ip_all配置服务2#进入目录 cd /root/mongodb/node2/config-server2 #创建并启动容器。 docker run --restart=always --privileged=true -p 10022:27019 -v $PWD/config:/etc/mongod -v $PWD/db:/data/db -d --name pro-file-server-config2 mongo:4.0.0 -f /etc/mongod/config.conf --configsvr --replSet "rs-file-server-config-server" --bind_ip_all配置服务2#进入目录 cd /root/mongodb/node3/config-server3 #创建并启动容器。 docker run --restart=always --privileged=true -p 10023:27019 -v $PWD/config:/etc/mongod -v $PWD/db:/data/db -d --name pro-file-server-config3 mongo:4.0.0 -f /etc/mongod/config.conf --configsvr --replSet "rs-file-server-config-server" --bind_ip_all`配置Mongo将3个配置服务关联到一起通过docker进入任意一台config-server配置三台服务器之间的联系#进入pro-file-server-config1配置服务器 docker exec -it pro-file-server-config1 /bin/bash #连接mongo 配置服务器暴露的默认端口为27019 mongo -port 27019 #执行配置内容 rs.initiate({ _id: "rs-file-server-config-server", configsvr: true, members: [ { _id : 0,host : "192.168.136.163:10021" }, { _id : 1,host : "192.168.136.163:10022" }, { _id : 2, host : "192.168.136.163:10023" } ] }…