新鲜事开发
类似于微博的最新动态,最常见的就是在个人页看到自己关注的人的动态
xxx给xxx点了个赞、xxx转发了xxxx
像这样的
这时候我们要新建一个新鲜事的表用来保存新鲜事
属性有id,用来确定当前记录的唯一性
created_date用来进行排序
user_id是触发新鲜事的人,可以是你关注的人
data中的数据用json格式保存
type代表新鲜事的类型,是评论还是点赞
model层
@Data
public class Feed {
private int id;
private int type;
private int userId;
private Date createdDate;
private String data;
private JSONObject dataJson = null;
public void setData(String data) {
this.data = data;
dataJson = JSONObject.parseObject(data);
}
public String get(String key) {
return dataJson == null ? null : dataJson.getString(key);
}dao层
增加feed功能、通过id获取feed、通过user_id获取feed列表
@Mapper
public interface FeedDAO {
String TABLE_NAME = " feed ";
String INSERT_FIELDS = " user_id, data, created_date, type ";
String SELECT_FIELDS = " id, " + INSERT_FIELDS;
@Insert({"insert into ", TABLE_NAME, "(", INSERT_FIELDS,
") values (#{userId},#{data},#{createdDate},#{type})"})
int addFeed(Feed feed);
@Select({"select", SELECT_FIELDS, " from ", TABLE_NAME, " where id=#{id}"})
Feed getFeedById(int id);
@SelectProvider(type = FeedDynaSqlProvider.class, method = "selectUserFeeds")
List<Feed> selectUserFeeds(@Param("maxId") int maxId,
@Param("userIds") List<Integer> userIds,
@Param("count") int count);
}public class FeedDynaSqlProvider {
public String selectUserFeeds(@Param("maxId") int maxId,
@Param("userIds") List<Integer> userIds,
@Param("count") int count) {
int num = userIds.size();
StringBuffer sb = new StringBuffer();
sb.append(" select ");
sb.append(" id, created_date,user_id, data, type ");
sb.append(" from ");
sb.append(" feed where 1=1 and ");
sb.append(" id < " + maxId);
if (userIds.size() != 0) {
sb.append(" and user_id in ");
sb.append(" ( ");
for (Integer userId : userIds) {
if (num != 1) {
sb.append("" + userId + ",");
} else if (num == 1) {
sb.append("" + userId);
}
num--;
}
sb.append(" ) ");
}
sb.append("ORDER BY id DESC LIMIT "+ count);
System.out.println(sb.toString());
return sb.toString();
}
}service层
@Service
public class FeedService {
@Autowired
FeedDAO feedDAO;
public List<Feed> getUserFeeds(int maxId, List<Integer> userIds, int count) {
return feedDAO.selectUserFeeds(maxId, userIds, count);
}
public boolean addFeed(Feed feed) {
feedDAO.addFeed(feed);
return feed.getId() > 0;
}
public Feed getById(int id) {
return feedDAO.getFeedById(id);
}
}controller层
这里是获取feed的两种模式
一种是推,一种是拉
推,简单来说就是在一个事件触发之后,主动将这个事件推给自己的粉丝,这样确实比较直接,但是也会带来很大的困扰,像谢娜那种1亿粉丝的,一推就推给1亿人,对服务器的压力太大了,而且1亿人中活跃的粉丝占比也不会太高,这样就白白浪费掉了服务器资源
拉,就是自己的关注者发了一个动态,不会第一时间就放到我的动态里,要我主动的去获取,才能获得到这个feed
大部分网页都是推拉结合的
@Controller
public class FeedController {
private static final Logger logger = LoggerFactory.getLogger(FeedController.class);
@Autowired
FeedService feedService;
@Autowired
FollowService followService;
@Autowired
HostHolder hostHolder;
@Autowired
JedisAdapter jedisAdapter;
@RequestMapping(path = {"/pushfeeds"}, method = {RequestMethod.GET, RequestMethod.POST})
public String getPushFeeds(Model model) {
int localUserId = hostHolder.getUser() != null ? hostHolder.getUser().getId() : 0;
//获取当前用户存储在redis中的feed集合
List<String> feedIds = jedisAdapter.lrange(RedisKeyUtil.getTimelineKey(localUserId), 0, 10);
List<Feed> feeds = new ArrayList<Feed>();
for (String feedId : feedIds) {
//将feed通过feed的id查询出来,放进集合中,然后转发到页面中
Feed feed = feedService.getById(Integer.parseInt(feedId));
if (feed != null) {
feeds.add(feed);
}
}
model.addAttribute("feeds", feeds);
return "feeds";
}
@RequestMapping(path = {"/pullfeeds"}, method = {RequestMethod.GET, RequestMethod.POST})
public String getPullFeeds(Model model) {
System.out.println(hostHolder.getUser().getId());
int localUserId = hostHolder.getUser() != null ? hostHolder.getUser().getId() : 0;
List<Integer> followees = new ArrayList<>();
if (localUserId != 0) {
// 关注的人
followees = followService.getFollowees(localUserId, EntityType.ENTITY_USER, Integer.MAX_VALUE);
}
List<Feed> feeds = feedService.getUserFeeds(Integer.MAX_VALUE, followees, 10);
model.addAttribute("feeds", feeds);
return "feeds";
}
}
FeedHandler
/**
* 所有feed都会通过feedHandler的事件***
* 当监听到事件,就会把事件传递到数据库里面
* 显示数据的时候先找自己关注的人,看他发生了多少事件
* 然后统一拉取出来,渲染页面
*/
private String buildFeedData(EventModel model) {
Map<String, String> map = new HashMap<String ,String>();
// 触发用户是通用的
User actor = userService.getUser(model.getActorId());
if (actor == null) {
return null;
}
map.put("userId", String.valueOf(actor.getId()));
map.put("userHead", actor.getHeadUrl());
map.put("userName", actor.getName());
if (model.getType() == EventType.COMMENT ||
(model.getType() == EventType.FOLLOW && model.getEntityType() == EntityType.ENTITY_QUESTION)) {
Question question = questionService.getById(model.getEntityId());
if (question == null) {
return null;
}
map.put("questionId", String.valueOf(question.getId()));
map.put("questionTitle", question.getTitle());
return JSONObject.toJSONString(map);
}
return null;
}
@Override
public void doHandle(EventModel model) {
// 构造一个新鲜事
Feed feed = new Feed();
feed.setCreatedDate(new Date());
feed.setType(model.getType().getValue());
feed.setUserId(model.getActorId());
feed.setData(buildFeedData(model));
if (feed.getData() == null) {
// 不支持的feed
return;
}
feedService.addFeed(feed);
// 获得所有粉丝
List<Integer> followers = followService.getFollowers(EntityType.ENTITY_USER, model.getActorId(), Integer.MAX_VALUE);
// 系统队列
followers.add(0);
// 给所有粉丝推事件
for (int follower : followers) {
String timelineKey = RedisKeyUtil.getTimelineKey(follower);
jedisAdapter.lpush(timelineKey, String.valueOf(feed.getId()));
// 限制最长长度,如果timelineKey的长度过大,就删除后面的新鲜事
}
}
@Override
public List<EventType> getSupportEventTypes() {
return Arrays.asList(new EventType[]{EventType.COMMENT, EventType.FOLLOW});
}待完善功能
- 多好友合并去重
- 关联实体删除清理
- 取消/新增关注实时更新
- 分时段存储
- 增加更多种类的新鲜事
- 推拉结合模式

京公网安备 11010502036488号