public NutMap _topic(Topic topic, Map<Integer, UserProfile> authors, String mdrender) {
   yvrService.fillTopic(topic, authors);
   NutMap tp = new NutMap();
   tp.put("id", topic.getId());
   tp.put("author_id", "" + topic.getAuthor().getUserId());
   tp.put("tab", topic.getType().toString());
   tp.put(
       "content",
       "false".equals(mdrender)
           ? topic.getContent()
           : Markdowns.toHtml(topic.getContent(), urlbase));
   tp.put("title", StringEscapeUtils.unescapeHtml(topic.getTitle()));
   if (topic.getLastComment() != null)
     tp.put("last_reply_at", _time(topic.getLastComment().getCreateTime()));
   tp.put("good", topic.isGood());
   tp.put("top", topic.isTop());
   tp.put("reply_count", topic.getReplyCount());
   tp.put("visit_count", topic.getVisitCount());
   tp.put("create_at", _time(topic.getCreateTime()));
   UserProfile profile = topic.getAuthor();
   if (profile != null) {
     profile.setScore(yvrService.getUserScore(topic.getUserId()));
   }
   tp.put("author", _author(profile));
   return tp;
 }
  @Aop("redis")
  public CResult addReply(final String topicId, final TopicReply reply, final int userId) {
    if (userId < 1) return _fail("请先登录");
    if (reply == null || reply.getContent() == null || reply.getContent().trim().isEmpty()) {
      return _fail("内容不能为空");
    }
    final String cnt = reply.getContent().trim();
    final Topic topic = dao.fetch(Topic.class, topicId); // TODO 改成只fetch出type属性
    if (topic == null) {
      return _fail("主题不存在");
    }
    if (topic.isLock()) {
      return _fail("该帖子已经锁定,不能回复");
    }
    reply.setTopicId(topicId);
    reply.setUserId(userId);
    reply.setContent(Toolkit.filteContent(reply.getContent()));
    reply.setContentId(bigContentService.put(reply.getContent()));
    reply.setContent(null);
    dao.insert(reply);
    // 更新索引
    topicSearchService.add(topic);
    // 更新topic的时间戳
    Pipeline pipe = jedis().pipelined();
    if (topic.isTop()) {
      pipe.zadd(RKEY_TOPIC_TOP, reply.getCreateTime().getTime(), topicId);
    } else {
      pipe.zadd(RKEY_TOPIC_UPDATE + topic.getType(), reply.getCreateTime().getTime(), topicId);
      pipe.zadd(RKEY_TOPIC_UPDATE_ALL, reply.getCreateTime().getTime(), topicId);
    }
    pipe.zrem(RKEY_TOPIC_NOREPLY, topicId);
    if (topic.getTags() != null) {
      for (String tag : topic.getTags()) {
        pipe.zadd(
            RKEY_TOPIC_TAG + tag.toLowerCase().trim(), reply.getCreateTime().getTime(), topicId);
      }
    }
    pipe.hset(RKEY_REPLY_LAST, topicId, reply.getId());
    pipe.zincrby(RKEY_REPLY_COUNT, 1, topicId);
    pipe.zincrby(RKEY_USER_SCORE, 10, "" + userId);
    pipe.sync();

    notifyUsers(topic, reply, cnt, userId);

    return _ok(reply.getId());
  }