You are a wizard, Harry!
1. 项目介绍及环境配置
2. 短信验证码登录
3. 用户信息
4. MongoDB
5.推荐好友列表/MongoDB集群/动态发布与查看
6. 圈子动态/圈子互动
7. 即时通讯(基于第三方API)
8. 附近的人(百度地图APi)
9. 小视频
10.网关配置
11.后台管理
编辑tanhua-app-server/src/main/java/com/tanhua/server/controller/TanhuaController.java
文件:
@RestController
@RequestMapping("/tanhua")
public class TanhuaController {
@Autowired
private TanhuaService tanhuaService;
/**
* 今日佳人
*/
@GetMapping("/todayBest")
public ResponseEntity todayBest() {
TodayBest vo = tanhuaService.todayBest();
return ResponseEntity.ok(vo);
}
/**
* 查询分页推荐好友列表
*/
@GetMapping("recommendation")
public ResponseEntity recommendation(RecommendUserDto dto) {
PageResult pr = tanhuaService.recommendation(dto);
return ResponseEntity.ok(pr);
}
}
编辑 tanhua-app-server/src/main/java/com/tanhua/server/service/TanhuaService.java
文件:
@Service
public class TanhuaService {
@DubboReference
private RecommendUserApi recommendUserApi;
@DubboReference
private UserInfoApi userInfoApi;
// 查询今日佳人数据
public TodayBest todayBest() {
// 1. 获取用户id
Long userId = UserHolder.getUserId();
// 2. 调用api查询
RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(userId);
// 3. 设置默认值
if(recommendUser == null) {
recommendUser = new RecommendUser();
recommendUser.setUserId(1l);
recommendUser.setScore(99d);
}
// 4. 将recommendUser 转化成 todayBest对象
UserInfo userInfo = userInfoApi.findById(recommendUser.getUserId());
TodayBest vo = TodayBest.init(userInfo, recommendUser);
// 5. 返回
return vo;
}
// 查询分页推荐好友列表
public PageResult recommendation(RecommendUserDto dto) {
// 1. 获取用户id
Long userId = UserHolder.getUserId();
// 2. 调用RecommendUserApi查询数据列表(PageResult -- RecommendUser)
PageResult pr = recommendUserApi.queryRecommendUserList(dto.getPage(), dto.getPagesize(), userId);
// 3. 获取分页中的RecommendUser数据列表
List<RecommendUser> items = (List<RecommendUser>) pr.getItems();
// 4. 判断列表数据是否为空
if (items == null) {
return pr;
}
// 5. 循环RecommendUser推荐列表,根据推荐的用户id查询用户详情
List<TodayBest> list = new ArrayList<>();
for (RecommendUser item : items) {
Long recommendUserId = item.getUserId();
UserInfo userInfo = userInfoApi.findById(recommendUserId);
if(userInfo != null) {
// 条件判断
if(!StringUtils.isEmpty(dto.getGender()) && !dto.getGender().equals(userInfo.getGender())) {
continue;
}
if (dto.getAge() != null && dto.getAge() < userInfo.getAge()) {
continue;
}
TodayBest vo = TodayBest.init(userInfo, item);
list.add(vo);
}
}
// 6. 构造返回值
pr.setItems(list);
return pr;
}
}
编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/RecommendUserApi.java
文件:
public interface RecommendUserApi {
// 查询今日佳人
RecommendUser queryWithMaxScore(Long toUserId);
// 分页查询推荐好友列表
PageResult queryRecommendUserList(Integer page, Integer pagesize, Long toUserId);
}
编辑tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/RecommendUserApiImpl.java
文件:
@DubboService
public class RecommendUserApiImpl implements RecommendUserApi{
@Autowired
private MongoTemplate mongoTemplate;
// 查询今日佳人
public RecommendUser queryWithMaxScore(Long toUserId) {
// 根据toUserId查询,根据评分score排序,获取第一条
// 1. 构建Criteria
Criteria criteria = Criteria.where("toUserId").is(toUserId);
// 2. 构建Query
Query query = Query.query(criteria).with(Sort.by(Sort.Order.desc("score")))
.limit(1); // 查询第一条(第一页第一条)
// 3. 调用mongoTemplate查询
return mongoTemplate.findOne(query, RecommendUser.class);
}
// 分页查询推荐好友列表
public PageResult queryRecommendUserList(Integer page, Integer pagesize, Long toUserId) {
// 1. 构建Criteria对象
Criteria criteria = Criteria.where("toUserId").is(toUserId);
// 2. 构造query对象
Query query = Query.query(criteria);
// 3. 查询总数
long count = mongoTemplate.count(query, RecommendUser.class);
// 4. 查询数据列表
query.with(Sort.by(Sort.Order.desc("score"))).limit(pagesize).skip((page - 1) * pagesize);
List<RecommendUser> list = mongoTemplate.find(query, RecommendUser.class);
// 5. 构造返回值
return new PageResult(page, pagesize, count, list);
}
}
新建 tanhua-model/src/main/java/com/tanhua/model/dto/RecommendUserDto.java
文件:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RecommendUserDto {
private Integer page = 1; //当前页数
private Integer pagesize = 10; //页尺寸
private String gender; //性别 man woman
private String lastLogin; //近期登陆时间
private Integer age; //年龄
private String city; //居住地
private String education; //学历
}
编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/UserInfoApi.java
文件:
public interface UserInfoApi {
public void save(UserInfo userInfo);
public void update(UserInfo userInfo);
// 根据id查询
UserInfo findById(Long id);
/**
* 批量查詢用戶詳情
* 返回值:Map
*/
Map<Long, UserInfo> findByIds(List<Long> userIds, UserInfo info);
}
编辑 tanhua-dubbo/tanhua-dubbo-db/src/main/java/com/tanhua/dubbo/api/UserInfoApiImpl.java
文件:
@DubboService
public class UserInfoApiImpl implements UserInfoApi{
@Autowired
private UserInfoMapper userInfoMapper;
@Override
public void save(UserInfo userInfo) {
userInfoMapper.insert(userInfo);
}
@Override
public void update(UserInfo userInfo) {
userInfoMapper.updateById(userInfo);
}
@Override
public UserInfo findById(Long id) {
return userInfoMapper.selectById(id);
}
/**
* 批量查询用戶详情
* @param userIds 用户id列表
* @param info 用户详情信息
* @return
*/
@Override
public Map<Long, UserInfo> findByIds(List<Long> userIds, UserInfo info) {
// 1. 用户id列表
QueryWrapper qw = new QueryWrapper<>();
qw.in("id", userIds);
// 2. 添加筛选条件
if(info != null) {
if(info.getAge() != null) {
qw.lt("age", info.getAge());
}
if(!StringUtils.isEmpty(info.getGender())) {
qw.eq("gender", info.getGender());
}
}
// 3. 获取用户详情列表 转换成map集合
List<UserInfo> list = userInfoMapper.selectList(qw);
Map<Long, UserInfo> map = CollUtil.fieldValueMap(list, "id");
// 4. 返回
return map;
}
}
新建 tanhua-app-server/src/test/java/com/tanhua/test/UserInfoApiTest.java
文件:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class UserInfoApiTest {
@DubboReference
private UserInfoApi userInfoApi;
@Test
public void testFindUsers() {
List ids = new ArrayList<>();
ids.add(1l);
ids.add(2l);
ids.add(3l);
ids.add(4l);
// 添加判断条件
UserInfo userInfo = new UserInfo();
userInfo.setAge(23);
// Map map = userInfoApi.findByIds(ids, null);
Map map = userInfoApi.findByIds(ids, userInfo);
map.forEach((k, v) -> System.out.println(k + "---" + v));
}
}
编辑 tanhua-app-server/src/main/java/com/tanhua/server/service/TanhuaService.java
文件:
@Service
public class TanhuaService {
@DubboReference
private RecommendUserApi recommendUserApi;
@DubboReference
private UserInfoApi userInfoApi;
// 查询今日佳人数据
public TodayBest todayBest() {
// 1. 获取用户id
Long userId = UserHolder.getUserId();
// 2. 调用api查询
RecommendUser recommendUser = recommendUserApi.queryWithMaxScore(userId);
// 3. 设置默认值
if(recommendUser == null) {
recommendUser = new RecommendUser();
recommendUser.setUserId(1l);
recommendUser.setScore(99d);
}
// 4. 将recommendUser 转化成 todayBest对象
UserInfo userInfo = userInfoApi.findById(recommendUser.getUserId());
TodayBest vo = TodayBest.init(userInfo, recommendUser);
// 5. 返回
return vo;
}
// 查询分页推荐好友列表
public PageResult recommendation(RecommendUserDto dto) {
// 1. 获取用户id
Long userId = UserHolder.getUserId();
// 2. 调用RecommendUserApi查询数据列表(PageResult -- RecommendUser)
PageResult pr = recommendUserApi.queryRecommendUserList(dto.getPage(), dto.getPagesize(), userId);
// 3. 获取分页中的RecommendUser数据列表
List<RecommendUser> items = (List<RecommendUser>) pr.getItems();
// 4. 判断列表数据是否为空
if (items == null) {
return pr;
}
// // 5. 循环RecommendUser推荐列表,根据推荐的用户id查询用户详情
// List list = new ArrayList<>();
// for (RecommendUser item : items) {
// Long recommendUserId = item.getUserId();
// UserInfo userInfo = userInfoApi.findById(recommendUserId);
// if(userInfo != null) {
// // 条件判断
// if(!StringUtils.isEmpty(dto.getGender()) && !dto.getGender().equals(userInfo.getGender())) {
// continue;
// }
// if (dto.getAge() != null && dto.getAge() < userInfo.getAge()) {
// continue;
// }
// TodayBest vo = TodayBest.init(userInfo, item);
// list.add(vo);
// }
// }
//
// 5. 提取所有推荐的用户id列表
List<Long> ids = CollUtil.getFieldValues(items, "userId", Long.class);
UserInfo userInfo = new UserInfo();
userInfo.setAge(dto.getAge());
userInfo.setGender(dto.getGender());
// 6. 构建查询条件,批量查询所有的用户详情
Map<Long, UserInfo> map = userInfoApi.findByIds(ids, userInfo);
// 7. 循环推荐的数据列表,构建vo对象
List<TodayBest> list = new ArrayList<>();
for (RecommendUser item : items) {
UserInfo info = map.get(item.getUserId());
if(info != null) {
TodayBest vo = TodayBest.init(info, item);
list.add(vo);
}
}
// 6. 构造返回值
pr.setItems(list);
return pr;
}
}
单机 MongoDB 并不适用于企业场景,存在两个问题亟需解决
为了 解决单点故障 和 海量数据存储 问题,MongoDB提供了三种集群形式来支持:
单点故障、适用于中小型应用(数据量适中)、故障转移、读写分离
Sharding (分片集群)该模式适合处理大量数据,它将数据分开存储,不同服务器保存不同的数据,所有服务器数据的总和即为整个数据集。
MongoDB 通过分片策略,决定数据存储的分片服务器。mongoDB 有两种分片策略,根据集合字段来指定。
支持海量数据存储、分片集群内部结构、分片策略
需求:
最简单的设计思路,包含 好友表 与 动态表:
优点: 开发难度较小、易于理解
缺点: 动态对特定好友可见/不可见,实现难度较大、效率较低
优点: 开发难度较小、可以完成所有业务功能
缺点: 索引空间占用、效率较低
优点: 可以完成所有业务功能、数据结构清晰
缺点: 开发难度较复杂
分片规则: 动态表(根据用户id)、分片时间线表(根据好友id分片)
好友关系表(friends):
{
"_id": ObjectId("6018bc055098b2230031e2da"),
"created": NumberLong("1612233733056"),
"userId": NumberLong("1"),
"friendId": NumberLong("106"),
"_class": "com.itheima.domain.mongo.Friend"
}
动态表(movement):
{
"_id": ObjectId("5e82dc416401952928c211d8"),
"pid": NumberLong("10064"),
"userId": NumberLong("6"),
"textContent": "最悲伤却又是最痛苦的谎言,就是我还好,没有关系。",
"medias": [
"https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1.jpg",
"https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567349498.jpg",
"https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567352977.jpg",
"https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/photo/7/1564567360406.jpg"
],
"longitude": "121.588627",
"latitude": "30.935781",
"state": NumberInt("0"),
"locationName": "中国上海市奉贤区人民路445弄",
"created": NumberLong("1585634369493"),
"_class": "com.tanhua.dubbo.server.pojo.Publish"
}
时间线表(movement_timeline):
{
"_id": ObjectId("609cf6538743d448c02c61f0"),
"movementId": ObjectId("609cf6538743d448c02c61ef"),
"userId": NumberLong("106"),
"friendId": NumberLong("1"),
"created": NumberLong("1620899411043"),
"_class": "com.tanhua.model.mongo.MovementTimeLine"
}
新建 tanhua-model/src/main/java/com/tanhua/model/mongo/Friend.java
文件:
/**
* 好友表:好友关系表
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "friend")
public class Friend implements java.io.Serializable{
private static final long serialVersionUID = 6003135946820874230L;
private ObjectId id;
private Long userId; //用户id
private Long friendId; //好友id
private Long created; //时间
}
新建 tanhua-model/src/main/java/com/tanhua/model/mongo/Movement.java
文件:
//动态详情表
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement")
public class Movement implements java.io.Serializable {
private ObjectId id; //主键id
// redis实现, 或者使用MongoDB实现
private Long pid; //Long类型,用于推荐系统的模型(自动增长)
private Long created; //发布时间
private Long userId;
private String textContent; //文字
private List<String> medias; //媒体数据,图片或小视频 url
private String longitude; //经度
private String latitude; //纬度
private String locationName; //位置名称
private Integer state = 0;//状态 0:未审(默认),1:通过,2:驳回
}
新建 tanhua-model/src/main/java/com/tanhua/model/mongo/MovementTimeLine.java
文件:
/**
* 好友时间线表,用于存储好友发布的数据
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Document(collection = "movement_timeLine")
public class MovementTimeLine implements java.io.Serializable {
private static final long serialVersionUID = 9096178416317502524L;
private ObjectId id;
private ObjectId movementId;//动态id
private Long userId; //发布动态用户id
private Long friendId; // 可见好友id
private Long created; //发布的时间
}
新建 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/MovementApi.java
文件:
public interface MovementApi {
}
新建 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java
文件:
@DubboService
public class MovementApiImpl implements MovementApi {
@Autowired
private MongoTemplate mongoTemplate;
}
新建 tanhua-model/src/main/java/com/tanhua/model/mongo/Sequence.java
文件:
@Document(collection = "sequence")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Sequence {
private ObjectId id;
private long seqId; //自增序列
private String collName; //集合名称
}
新建 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/utils/IdWorker.java
文件:
@Component
public class IdWorker {
@Autowired
private MongoTemplate mongoTemplate;
public Long getNextId(String collName) {
Query query = new Query(Criteria.where("collName").is(collName));
Update update = new Update();
update.inc("seqId", 1);
FindAndModifyOptions options = new FindAndModifyOptions();
options.upsert(true);
options.returnNew(true);
Sequence sequence = mongoTemplate.findAndModify(query, update, options, Sequence.class);
return sequence.getSeqId();
}
}
新建 tanhua-dubbo/tanhua-dubbo-mongo/src/test/java/com/tanhua/dubbo/IdWorkerTest.java
测试类:
@RunWith(SpringRunner.class)
@SpringBootTest
public class IdWorkerTest {
@Autowired
private IdWorker idWorker;
@Test
public void test() {
Long id = idWorker.getNextId("test");
System.out.println(id);
}
}
新建 tanhua-app-server/src/main/java/com/tanhua/server/controller/MovementController.java
文件:
@RestController
@RequestMapping("/movements")
public class MovementController {
@Autowired
private MovementService movementService;
/**
* 发布动态
* @return
*/
@PostMapping
public ResponseEntity movements(Movement movement, MultipartFile imageContent[]) throws IOException {
movementService.publishMovement(movement, imageContent);
return ResponseEntity.ok(null);
}
}
新建 tanhua-app-server/src/main/java/com/tanhua/server/service/MovementService.java
文件:
@Service
public class MovementService {
@Autowired
private OssTemplate ossTemplate;
@DubboReference
private MovementApi movementApi;
/**
* 发布动态
*/
public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {
// 1. 判断发布动态的内容是否存在
if(StringUtils.isEmpty(movement.getTextContent())) {
throw new BusinessException(ErrorResult.contentError());
}
// 2. 获取当前登录的用户id
Long userId = UserHolder.getUserId();
// 3. 将文件内容上传到阿里云OSS, 获取请求地址
List<String> medias = new ArrayList<>();
for (MultipartFile multipartFile : imageContent) {
String upload = ossTemplate.upload(multipartFile.getOriginalFilename(), multipartFile.getInputStream());
medias.add(upload);
}
// 4. 将数据封装到movement对象
movement.setUserId(userId);
movement.setMedias(medias);
//5. 调用API完成动态发布
movementApi.publish(movement);
}
}
编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/MovementApi.java
文件:
public interface MovementApi {
// 发布动态
void publish(Movement movement);
}
编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java
文件:
@DubboService
public class MovementApiImpl implements MovementApi {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private IdWorker idWorker;
// 发布动态
public void publish(Movement movement) {
try {
// 1. 保存动态详情
// 1.1 设置PID(同名时序列自增)
movement.setPid(idWorker.getNextId("movement"));
// 1.2 设置时间
movement.setCreated(System.currentTimeMillis());
// 1.3 保存动态
mongoTemplate.save(movement);
// 2. 查询当前用户的好友数据
Criteria criteria = Criteria.where("userId").is(movement.getUserId());
Query query = Query.query(criteria);
List<Friend> friends = mongoTemplate.find(query, Friend.class);
// 3. 循环好友数据, 构建时间线数据存入数据库
for (Friend friend : friends) {
MovementTimeLine timeLine = new MovementTimeLine();
timeLine.setMovementId(movement.getId());
timeLine.setUserId(friend.getUserId());
timeLine.setFriendId(friend.getFriendId());
timeLine.setCreated(System.currentTimeMillis());
mongoTemplate.save(timeLine);
}
} catch (Exception e) {
// 忽略事物处理
e.printStackTrace();
}
}
}
新建 tanhua-app-server/src/test/java/com/tanhua/test/MovementApiTest.java
文件:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = AppServerApplication.class)
public class MovementApiTest {
@DubboReference
private MovementApi movementApi;
@Test
public void testPublish() {
Movement movement = new Movement();
movement.setUserId(106l);
movement.setTextContent("你的酒窝没有酒,我却醉的像条狗");
List<String> list = new ArrayList<>();
list.add("https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/tanhua/avatar_1.png");
list.add("https://tanhua-dev.oss-cn-zhangjiakou.aliyuncs.com/images/tanhua/avatar_2.png");
movement.setMedias(list);
movement.setLatitude("40.066355");
movement.setLongitude("116.350426");
movement.setLocationName("中国北京市昌平区建材城西路16号");
movementApi.publish(movement);
}
}
大量的时间线数据同步写入的问题如何解决 ?
@Async: Spring提供的异步处理注解,被此注解标注的方法会在新的线程中执行,其实就相当于我们自己new Thread。
新建 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/utils/TimeLineService.java
文件:
@Component
public class TimeLineService {
@Autowired
private MongoTemplate mongoTemplate;
@Async // 异步多线程调用
public void saveTimeLine(Long userId, ObjectId movementId) {
// 2. 查询当前用户的好友数据
Criteria criteria = Criteria.where("userId").is(userId);
Query query = Query.query(criteria);
List<Friend> friends = mongoTemplate.find(query, Friend.class);
// 睡眠时间
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 3. 循环好友数据, 构建时间线数据存入数据库
for (Friend friend : friends) {
MovementTimeLine timeLine = new MovementTimeLine();
timeLine.setMovementId(movementId);
timeLine.setUserId(friend.getUserId());
timeLine.setFriendId(friend.getFriendId());
timeLine.setCreated(System.currentTimeMillis());
mongoTemplate.save(timeLine);
}
}
}
编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/DubboMongoApplication.java
文件:
@SpringBootApplication
@EnableAsync // 开启Spring @Async支持
public class DubboMongoApplication {
public static void main(String[] args) {
SpringApplication.run(DubboMongoApplication.class,args);
}
}
编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java
文件:
@DubboService
public class MovementApiImpl implements MovementApi {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private IdWorker idWorker;
@Autowired
private TimeLineService timeLineService;
// 发布动态
public void publish(Movement movement) {
try {
// 1. 保存动态详情
// 1.1 设置PID(同名时序列自增)
movement.setPid(idWorker.getNextId("movement"));
// 1.2 设置时间
movement.setCreated(System.currentTimeMillis());
// 1.3 保存动态
mongoTemplate.save(movement);
// // 2. 查询当前用户的好友数据
// Criteria criteria = Criteria.where("userId").is(movement.getUserId());
// Query query = Query.query(criteria);
// List friends = mongoTemplate.find(query, Friend.class);
//
// // 3. 循环好友数据, 构建时间线数据存入数据库
// for (Friend friend : friends) {
// MovementTimeLine timeLine = new MovementTimeLine();
// timeLine.setMovementId(movement.getId());
// timeLine.setUserId(friend.getUserId());
// timeLine.setFriendId(friend.getFriendId());
// timeLine.setCreated(System.currentTimeMillis());
// mongoTemplate.save(timeLine);
// }
// 异步多线程调用
timeLineService.saveTimeLine(movement.getUserId(), movement.getId());
} catch (Exception e) {
// 忽略事物处理
e.printStackTrace();
}
}
}
新建 tanhua-model/src/main/java/com/tanhua/model/vo/MovementsVo.java
文件:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MovementsVo implements Serializable {
private String id; //动态id
private Long userId; //用户id
private String avatar; //头像
private String nickname; //昵称
private String gender; //性别 man woman
private Integer age; //年龄
private String[] tags; //标签
private String textContent; //文字动态
private String[] imageContent; //图片动态
private String distance; //距离
private String createDate; //发布时间 如: 10分钟前
private Integer likeCount; //点赞数
private Integer commentCount; //评论数
private Integer loveCount; //喜欢数
private Integer hasLiked; //是否点赞(1是,0否)
private Integer hasLoved; //是否喜欢(1是,0否)
public static MovementsVo init(UserInfo userInfo, Movement item) {
MovementsVo vo = new MovementsVo();
//设置动态数据
BeanUtils.copyProperties(item, vo);
vo.setId(item.getId().toHexString());
//设置用户数据
BeanUtils.copyProperties(userInfo, vo);
if(!StringUtils.isEmpty(userInfo.getTags())) {
vo.setTags(userInfo.getTags().split(","));
}
//图片列表
vo.setImageContent(item.getMedias().toArray(new String[]{}));
//距离
vo.setDistance("500米");
Date date = new Date(item.getCreated());
vo.setCreateDate(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
//设置是否点赞(后续处理)
vo.setHasLoved(0);
vo.setHasLiked(0);
return vo;
}
}
编辑 tanhua-app-server/src/main/java/com/tanhua/server/controller/MovementController.java
文件:
@RestController
@RequestMapping("/movements")
public class MovementController {
@Autowired
private MovementService movementService;
/**
* 发布动态
* @return
*/
@PostMapping
public ResponseEntity movements(Movement movement, MultipartFile imageContent[]) throws IOException {
movementService.publishMovement(movement, imageContent);
return ResponseEntity.ok(null);
}
/**
* 查询我的动态
*/
@GetMapping("/all")
public ResponseEntity findByUserId(Long userId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pagesize) {
PageResult pr = movementService.findByUserId(userId, page, pagesize);
return ResponseEntity.ok(pr);
}
}
编辑 tanhua-app-server/src/main/java/com/tanhua/server/service/MovementService.java
文件:
@Service
public class MovementService {
@Autowired
private OssTemplate ossTemplate;
@DubboReference
private MovementApi movementApi;
@DubboReference
private UserInfoApi userInfoApi;
/**
* 发布动态
*/
public void publishMovement(Movement movement, MultipartFile[] imageContent) throws IOException {
// 1. 判断发布动态的内容是否存在
if(StringUtils.isEmpty(movement.getTextContent())) {
throw new BusinessException(ErrorResult.contentError());
}
// 2. 获取当前登录的用户id
Long userId = UserHolder.getUserId();
// 3. 将文件内容上传到阿里云OSS, 获取请求地址
List<String> medias = new ArrayList<>();
for (MultipartFile multipartFile : imageContent) {
// String upload = ossTemplate.upload(multipartFile.getOriginalFilename(), multipartFile.getInputStream());
// !!! 阿里云OSS收费, 这里暂时跳过
String upload = "https://img-blog.csdnimg.cn/bb419b491ec1445d84046aa1852946bd.jpeg";
medias.add(upload);
}
// 4. 将数据封装到movement对象
movement.setUserId(userId);
movement.setMedias(medias);
//5. 调用API完成动态发布
movementApi.publish(movement);
}
// 查询我的动态
public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {
// 1. 根据用户id, 调用API查询个人动态内容(PageResult -- Movement)
PageResult pr = movementApi.findByUserId(userId, page, pagesize);
// 2. 获取PageResult中item列表对象
List<Movement> items = (List<Movement>) pr.getItems();
// 3. 非空判断
if(items == null) {
return pr;
}
// 4. 循环数据列表
UserInfo userInfo = userInfoApi.findById(userId);
List<MovementsVo> vos = new ArrayList<>();
for (Movement item : items) {
// 5. 一个Movement构建一个VO对象
MovementsVo vo = MovementsVo.init(userInfo, item);
vos.add(vo);
}
// 6. 构建返回值
pr.setItems(vos);
return pr;
}
}
编辑 tanhua-dubbo/tanhua-dubbo-interface/src/main/java/com/tanhua/dubbo/api/MovementApi.java
文件:
public interface MovementApi {
// 发布动态
void publish(Movement movement);
// 查询我的动态
PageResult findByUserId(Long userId, Integer page, Integer pagesize);
}
编辑 tanhua-dubbo/tanhua-dubbo-mongo/src/main/java/com/tanhua/dubbo/api/MovementApiImpl.java
文件:
@DubboService
public class MovementApiImpl implements MovementApi {
@Autowired
private MongoTemplate mongoTemplate;
@Autowired
private IdWorker idWorker;
@Autowired
private TimeLineService timeLineService;
// 发布动态
public void publish(Movement movement) {
try {
// 1. 保存动态详情
// 1.1 设置PID(同名时序列自增)
movement.setPid(idWorker.getNextId("movement"));
// 1.2 设置时间
movement.setCreated(System.currentTimeMillis());
// 1.3 保存动态
mongoTemplate.save(movement);
// // 2. 查询当前用户的好友数据
// Criteria criteria = Criteria.where("userId").is(movement.getUserId());
// Query query = Query.query(criteria);
// List friends = mongoTemplate.find(query, Friend.class);
//
// // 3. 循环好友数据, 构建时间线数据存入数据库
// for (Friend friend : friends) {
// MovementTimeLine timeLine = new MovementTimeLine();
// timeLine.setMovementId(movement.getId());
// timeLine.setUserId(friend.getUserId());
// timeLine.setFriendId(friend.getFriendId());
// timeLine.setCreated(System.currentTimeMillis());
// mongoTemplate.save(timeLine);
// }
// 异步多线程调用
timeLineService.saveTimeLine(movement.getUserId(), movement.getId());
} catch (Exception e) {
// 忽略事物处理
e.printStackTrace();
}
}
// 查询我的动态
@Override
public PageResult findByUserId(Long userId, Integer page, Integer pagesize) {
Criteria criteria = Criteria.where("userId").is(userId);
Query query = Query.query(criteria).skip((page - 1) * pagesize).limit(pagesize)
.with(Sort.by(Sort.Order.desc("created")));
List<Movement> movements = mongoTemplate.find(query, Movement.class);
return new PageResult(page, pagesize, 0l, movements);
}
}