8_1_异步设计和站内邮件通知系统

一、需求描述

1. 利用Redis做消息队列,实现一个异步化服务框架;如图:

2. 利用搭建好的框架实现异步化发送点赞信息和登录异常信息 。

 

二、具体diamante实现

首先搭建应用Redis做消息队列的异步化框架

1.准备

JedisAdapter.java

类中加上lpush 和 bpop的代码用来实现消息队列;加上setObject 和 getObject实现序列化与反序列的过程(将事件存入消息队列的时候要序列化,从队列中取出事件的时候需要反序列化):

 public long lpush(String key, String value){
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.lpush(key, value);
        }catch (Exception e){
                logger.error("Jedis lpush 发生异常 " + e.getMessage());
                return 0;
        }finally {
            if(jedis != null){
                try {
                    jedis.close();
                }catch (Exception e){
                    logger.error("Jedis 关闭异常 " + e.getMessage());
                }
            }
        }

    }

    public List<String> brpop(int timeout, String key){

        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            return jedis.brpop(timeout, key);
        }catch (Exception e){
            logger.error("Jedis brpop发生异常 " + e.getMessage());
            return null;
        }finally {
            if (jedis != null){
                try {
                    jedis.close();
                }catch (Exception e){
                    logger.error("Jedis 关闭异常" + e.getMessage());
                }
            }
        }
    }

    //序列化
    public void setObject(String key, Object object){
        set(key, JSON.toJSONString(object));
    }

    //反序列化
    public <T> T getObject(String key, Class<T> clazz){

        String value = get(key);
        if(value != null){
            return JSON.parseObject(value, clazz);
        }
        return null;

    }

RedisKeyUtil.java

类中加上一个生成事件key的方法,以后的事件都存入这个key对应的set集合中。

private static String BIZ_EVENT = "DISLIKE";

    /**
     * 事件发生的时候,生成key
     * @return
     */
    public static String getEventQueueKey(){
        return BIZ_EVENT;
    }

2. 异步化框架

EventType.java :事件类型

package com.nowcoder.async;

/**
 * Created by Administrator on 2017/5/7.
 */
public enum EventType {
    LIKE(0),
    COMMENT(1),
    LOGIN(1),
    MAIL(3);

    private int value;
    public int getValue() {
        return value;
    }
    EventType(int value) {
        this.value = value;
    }
}

EventModel.java : 发生的事件的数据都打包成一个Model(然后对这个model中数据进行序列化)

package com.nowcoder.async;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by Administrator on 2017/5/7.
 */
public class EventModel {

    private EventType type;
    //事件触发者
    private int actorId;
    //表示一个触发事件的对象
    private int entityId;
    private int entityType;
    //事件对象的拥有者
    private int entityOwnerId;

    //存放触发的事件数据
    Map<String, String> exts = new HashMap<>();

    public EventModel(EventType type){
        this.type = type;
    }

    public String getExt(String key) {
        return exts.get(key);
    }

    public EventModel setExt(String key, String value) {
        exts.put(key, value);
        return this;
    }

    public EventModel(){

    }

    public EventType getType() {
        return type;
    }

    public EventModel setType(EventType type) {
        this.type = type;
        return this;
    }

    public int getActorId() {
        return actorId;
    }

    public EventModel setActorId(int actorId) {
        this.actorId = actorId;
        return this;
    }

    public int getEntityId() {
        return entityId;
    }

    public EventModel setEntityId(int entityId) {
        this.entityId = entityId;
        return this;
    }

    public int getEntityType() {
        return entityType;
    }

    public EventModel setEntityType(int entityType) {
        this.entityType = entityType;
        return this;
    }

    public int getEntityOwnerId() {
        return entityOwnerId;
    }

    public EventModel setEntityOwnerId(int entityOwnerId) {
        this.entityOwnerId = entityOwnerId;
        return this;
    }

    public Map<String, String> getExts() {
        return exts;
    }

    public void setExts(Map<String, String> exts) {
        this.exts = exts;
    }
}

EventProducer.java : 将发生的事件推送到消息队列。

package com.nowcoder.async;

import com.alibaba.fastjson.JSONObject;
import com.nowcoder.util.JedisAdapter;
import com.nowcoder.util.RedisKeyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * Created by Administrator on 2017/5/7.
 */
@Service
public class EventProducer {

    private static final Logger logger = LoggerFactory.getLogger(EventProducer.class);
    @Autowired
    JedisAdapter jedisAdapter;

    /**
     * 将产生的事件model推送到redis的工作队列中
     * @param model
     * @return
     */
    public boolean fireEvent(EventModel model){

        try {
            //序列化
            String json = JSONObject.toJSONString(model);
            //产生key
            String eventkey = RedisKeyUtil.getEventQueueKey();
            //放入工作队列
            jedisAdapter.lpush(eventkey, json);
            return true;
        }catch (Exception e){
            logger.error("EventProducer fireEvent 发生异常 : " + e.getMessage());
            return false;
        }
    }

}

 EventConsumer.java : 从消息队列中获取事件交给Handler类进行处理。

package com.nowcoder.async;

import com.alibaba.fastjson.JSON;
import com.nowcoder.util.JedisAdapter;
import com.nowcoder.util.RedisKeyUtil;
import jdk.nashorn.api.scripting.JSObject;
import org.apache.commons.collections.map.HashedMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Administrator on 2017/5/7.
 */
@Service
public class EventConsumer implements InitializingBean, ApplicationContextAware{

    private static final Logger logger = LoggerFactory.getLogger(EventConsumer.class);

    //用来存储各种type事件的Handler
    private Map<EventType, List<EventHandler>> config = new HashMap<EventType, List<EventHandler>>();
    private ApplicationContext applicationContext;

    @Autowired
    JedisAdapter jedisAdapter;

    @Override
    public void afterPropertiesSet() throws Exception {
        //获取上下文所有实现EventHandler的类
        //使用BeanFatory的getBeansOfType()方法,该方法返回一个Map类型的实例,Map中的key为Bean的名,key对应的内容为Bean的实例。
        Map<String, EventHandler> beans = applicationContext.getBeansOfType(EventHandler.class);
        if (beans != null){
            for (Map.Entry<String, EventHandler> entry : beans.entrySet()){
                List<EventType> eventTypes = entry.getValue().getSupportEventType();
                for (EventType type : eventTypes){
                   //初始化的时候,若没有type,就加入
                    if(!config.containsKey(type)){
                        config.put(type, new ArrayList<EventHandler>());
                    }
                    config.get(type).add(entry.getValue());
                }
            }
        }

        //启动线程从工作队列中取出事件进行处理
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    String key = RedisKeyUtil.getEventQueueKey();
                    //从Redis数据库的键为key的set集合中获取存储的事件(事件Event为序列化过的,String类型)
                    List<String> events = jedisAdapter.brpop(0, key);
                    for (String message : events){
                        if (message.equals(key)){
                            continue;
                        }
                        EventModel eventModel = JSON.parseObject(message, EventModel.class);
                        //若事件没有注册过
                        if (!config.containsKey(eventModel.getType())){
                            logger.error("不能识别的事件 ");
                            continue;
                        }
                        //获取关注过该事件的handler,一一进行处理事件
                        for (EventHandler handler : config.get(eventModel.getType())){
                            handler.doHandle(eventModel);
                        }
                    }
                }
            }
        });
        thread.start();

    }

    /**
     * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

EventHandler.java: 接口,可以从消费者中获取事件交给对应的Handler实现类去处理:

package com.nowcoder.async;

import java.util.List;

/**
 * Created by Administrator on 2017/5/7.
 */
public interface EventHandler {

    //对EventConsumer中的event事件进行处理
    void doHandle(EventModel model);
    //获取哪些关注事件类型
    List<EventType> getSupportEventType();
}

LikeHandler.java: 实现点赞通知的类

package com.nowcoder.async.handler;

import com.nowcoder.async.EventHandler;
import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventType;
import com.nowcoder.model.HostHolder;
import com.nowcoder.model.Message;
import com.nowcoder.model.User;
import com.nowcoder.service.MessageService;
import com.nowcoder.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * Created by Administrator on 2017/5/7.
 */
@Component
public class LikeHandler implements EventHandler{

    @Autowired
    MessageService messageService;
    @Autowired
    UserService userService;
    @Autowired
    HostHolder hostHolder;

    @Override
    public void doHandle(EventModel model) {

        System.out.print("有人点赞了");
       Message message = new Message();

        //测试方便查看:就是自己发送给自己站内信
        //int fromId = model.getActorId();
       // int toId = fromId;

        //正常情况下fromId是当前点赞用户id,toId是点赞的咨询news所在的id
        // actorId = hostHolder.getUser().getId();
        int fromId = model.getActorId();
        //entityOwnerId = news.getId()
        int toId = model.getEntityOwnerId();

        message.setHasRead(0);// 0 代表未读 1 代表已读
        message.setFromId(fromId);
        message.setToId(toId);
        message.setConversationId(fromId < toId ? String.format("%d_$d", fromId, toId) : String.format("%d_%d", toId, fromId));

        User user = userService.getUser(model.getActorId());
        message.setContent("用户" + user.getName()
                + "赞了你的资讯,http://127.0.0.1:8080/news/" + model.getEntityId());
        message.setCreatedDate(new Date());
        messageService.addMessage(message);
    }

    @Override
    public List<EventType> getSupportEventType() {
        return Arrays.asList(EventType.LIKE);
    }
}

LoginExceptionHandler:登录时发生登录异常到对应的站内信,以及实现邮件发送

package com.nowcoder.async.handler;

import com.nowcoder.async.EventHandler;
import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventType;
import com.nowcoder.model.Message;
import com.nowcoder.service.MessageService;
import com.nowcoder.util.MailSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * Created by Administrator on 2017/5/7.
 */
@Service
public class LoginExceptionHandler implements EventHandler{

    @Autowired
    MessageService messageService;

    @Autowired
    MailSender mailSender;

    @Override
    public void doHandle(EventModel model) {
        // 判断是否有异常登陆
        Message message = new Message();
        message.setToId(model.getActorId());
        message.setContent("你上次的登陆ip异常");
        message.setFromId(17);
        message.setCreatedDate(new Date());
        messageService.addMessage(message);

        //邮件发送
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("username", model.getExt("username"));
        mailSender.sendWithHTMLTemplate(model.getExt("email"), "登陆异常", "mails/welcome.html",
                map);
    }

    @Override
    public List<EventType> getSupportEventType() {
        return Arrays.asList(EventType.LOGIN);
    }
}

3. 邮件发送

引入jar包:

<dependency>
            <groupId>com.sun.mail</groupId>
            <artifactId>javax.mail</artifactId>
            <version>1.5.5</version>
        </dependency>

package com.nowcoder.util;

import org.apache.velocity.app.VelocityEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;

import javax.mail.internet.MimeUtility;

import org.springframework.ui.velocity.VelocityEngineUtils;

import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Map;
import java.util.Properties;

/**
 * Created by Administrator on 2017/5/7.
 */
@Service
public class MailSender implements InitializingBean {
    private static final Logger logger = LoggerFactory.getLogger(MailSender.class);
    private JavaMailSenderImpl mailSender;

    @Autowired
    private VelocityEngine velocityEngine;

    public boolean sendWithHTMLTemplate(String to, String subject,
                                        String template, Map<String, Object> model) {
        try {
            String nick = MimeUtility.encodeText("阮宏宝");
            InternetAddress from = new InternetAddress(nick + "<[email protected]>");
            MimeMessage mimeMessage = mailSender.createMimeMessage();
            MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage);
            String result = VelocityEngineUtils
                    .mergeTemplateIntoString(velocityEngine, template, "UTF-8", model);
            mimeMessageHelper.setTo(to);
            mimeMessageHelper.setFrom(from);
            mimeMessageHelper.setSubject(subject);
            mimeMessageHelper.setText(result, true);
            mailSender.send(mimeMessage);
            return true;
        } catch (Exception e) {
            logger.error("发送邮件失败" + e.getMessage());
            return false;
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        mailSender = new JavaMailSenderImpl();
        mailSender.setUsername("[email protected]");
        mailSender.setPassword("***********");
        mailSender.setHost("smtp.qq.com");
        mailSender.setPort(465);
        mailSender.setProtocol("smtps");
        mailSender.setDefaultEncoding("utf8");
        Properties javaMailProperties = new Properties();
        javaMailProperties.put("mail.smtp.ssl.enable", true);
        mailSender.setJavaMailProperties(javaMailProperties);
    }
}

4. 测试

LikeController.java:

点赞的时候加入异步点赞通知:

//异步发送
        eventProducer.fireEvent(new EventModel(EventType.LIKE)
                    .setActorId(hostHolder.getUser().getId())
                    .setEntityId(newsId)
                    .setEntityType(EntityType.ENTITY_NEWS)
                    .setEntityOwnerId(news.getUserId()));

LoginController.java

登录时加上登录异常的通知:

 eventProducer.fireEvent(new EventModel(EventType.LOGIN)
                        .setActorId(18)
                        .setExt("username", username).setExt("email", "1032335358   @qq.com"));

5 相关代码

LoginController.java

package com.nowcoder.controller;

import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventProducer;
import com.nowcoder.async.EventType;
import com.nowcoder.service.UserService;
import com.nowcoder.util.ToutiaoUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * Created by Administrator on 2017/4/8.
 */
@Controller
public class LoginController {

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    @Autowired
    UserService userService;
    @Autowired
    EventProducer eventProducer;

    @RequestMapping(path = {"/reg/"}, method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public String reg(Model model,
                      @RequestParam("username") String username,
                      @RequestParam("password") String password,
                      @RequestParam(value = "rember", defaultValue = "0") int rember,
                      HttpServletResponse response){
        try {
           Map<String, Object> map = userService.register(username, password);
           if(map.containsKey("ticket")){
               Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
               cookie.setPath("/");
                //有记住我,就设置时间长一点
               if(rember > 0){
                   cookie.setMaxAge(3600 * 24 * 5);
               }
               response.addCookie(cookie);
               return  ToutiaoUtil.getJSONString(0, "注册成功");
           }else {
               return  ToutiaoUtil.getJSONString(1, map);
           }
        }catch (Exception e){
            logger.error("注册异常" + e.getMessage());
            return ToutiaoUtil.getJSONString(1, "注册异常");
        }
    }

    @RequestMapping(path = {"/login/"}, method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public String login(Model model,
                        @RequestParam("username") String username,
                        @RequestParam("password") String password,
                        @RequestParam(value = "rember", defaultValue = "0") int rememberme,
                        HttpServletResponse response){

        try {
            Map<String, Object> map = userService.login(username, password);
            if (map.containsKey("ticket")) {
                Cookie cookie = new Cookie("ticket", map.get("ticket").toString());
                cookie.setPath("/");
                if (rememberme > 0) {
                    cookie.setMaxAge(3600*24*5);
                }
                response.addCookie(cookie);
                eventProducer.fireEvent(new EventModel(EventType.LOGIN)
                        .setActorId(18)
                        .setExt("username", username).setExt("email", "1032335358   @qq.com"));
                return ToutiaoUtil.getJSONString(0, "登录成功");
            } else {
                return ToutiaoUtil.getJSONString(1, map);
            }

        } catch (Exception e) {
            logger.error("登录异常" + e.getMessage());
            return ToutiaoUtil.getJSONString(1, "登录异常");
        }

    }

    @RequestMapping(path = {"/logout/"}, method = {RequestMethod.POST, RequestMethod.GET})
    public String logout(@CookieValue("ticket") String ticket){
        userService.logout(ticket);
        return "redirect:/";
    }

}

LikeController.java

package com.nowcoder.controller;

import com.nowcoder.async.EventModel;
import com.nowcoder.async.EventProducer;
import com.nowcoder.async.EventType;
import com.nowcoder.model.EntityType;
import com.nowcoder.model.HostHolder;
import com.nowcoder.model.News;
import com.nowcoder.service.LikeService;
import com.nowcoder.service.NewsService;
import com.nowcoder.util.ToutiaoUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Created by Administrator on 2017/5/1.
 */
@Controller
public class LikeController {

    @Autowired
    LikeService likeService;
    @Autowired
    NewsService newsService;
    @Autowired
    HostHolder hostHolder;
    @Autowired
    EventProducer eventProducer;

    @RequestMapping(path = {"/like"}, method = {RequestMethod.GET, RequestMethod.POST})
    @ResponseBody
    public String like(@RequestParam("newsId") int newsId){
        //在likeKey对应的集合中加入当前用户
        long likeCount = likeService.like(hostHolder.getUser().getId(), EntityType.ENTITY_NEWS, newsId);

        //资讯上更新点赞数
        News news = newsService.getById(newsId);
        newsService.updateLikeCount(newsId, (int)likeCount);

        //异步发送
        eventProducer.fireEvent(new EventModel(EventType.LIKE)
                    .setActorId(hostHolder.getUser().getId())
                    .setEntityId(newsId)
                    .setEntityType(EntityType.ENTITY_NEWS)
                    .setEntityOwnerId(news.getUserId()));

        return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
    }

    @RequestMapping(path = {"/dislike"}, method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String disLike(@RequestParam("newsId") int newsId){

        //在disLikeKey对应的集合中加入当前用户
        long likeCount = likeService.disLike(hostHolder.getUser().getId(), EntityType.ENTITY_NEWS, newsId);
        if(likeCount <= 0){
            likeCount = 0;
        }

        //资讯上更新喜欢数
        newsService.updateLikeCount(newsId, (int)likeCount);
        return ToutiaoUtil.getJSONString(0, String.valueOf(likeCount));
    }

}

  

时间: 2024-10-09 08:40:31

8_1_异步设计和站内邮件通知系统的相关文章

站内邮件的设计思路

这个是做中国电信天天生活助理的时候做的,还是大四实习的时候弄得,当时设计的时候想了半天,后来觉得这个思路还是不错的,所以记录下来, 一般的话就是用户表,消息表和用户消息表了,用户消息表主要就是记录用户是否已读或者是否删除从而使得用户表和消息表独立了起来,查的时候只需要关联查询就好了 消息表:消息编号(message_id).消息标题(message_title).消息内容(message_content)消息创建时间(message_createDate).是否审核通过(flag:通过:1,不通

站内信

http://daihaixiang.blog.163.com/blog/static/3830134201111155381735/ 如果一个网站到了百万级的用户量了,那我不得不膜拜该网站和网站经营者了,因为经营这样的网站一直是笔者的梦想:)好了,回归正题,如果这样的系统 放你面前,让你设计一个站内信群发数据库,你该何去何从,总之,上面两种常规的办法肯定是行不通了的,因为庞大的数据量会让消息表撑爆,即使你分区也无济 于事.这时候作为一个系统架构师的你,可能不仅仅要从技术的角度去考虑这个问题,更

sqlalchemy中使用event设置条件触发短信与邮件通知

一.原因 近期在做短信与邮件通知系统.使用到了这一块.例如,当订单完成以后进行邮件短信的通知.虽然可以采用直接调用接口的方式实现,但有几个原因让我希望使用条件触发的方式 1.由于系统中支持线上线下以及代充值等多种方式,所以在多个地方订单改变状态.这样就让触发通知的代码凌乱分布. 2.系统将来扩建,需要新增加接口.则需要新增加调用的代码. 总而言之,直接调用将会增加维护难度.因此准备在订单的状态首次被置为支付成功时候进行短信与邮件的通知. 二.模块需求 短信与邮件的通知不能影响内部系统的运行,但由

【内容建设】以DEDECMS为例,讲解站内文章编辑的一些注意点,有益无害

以DEDECMS为例,讲解站内文章编辑的一些注意点,有益无害. 1.    标题:尽量拓展长尾词,攻略性为主 2.    自定义属性:方便前台调用.首页更新带动 3.    Tag标签:尽量控制在4-6个字,数量不超过3个 4.    关键词:1-3个,提取标题中的长尾词 5.    内容摘要(描述/尽量不要自动获取): 1) 不超过3行(少5-6字) 2) 包含1-2次标题中的长尾词 3)突出三点:标题(长尾词).医院名称(医师).诊断手法 小结:关键词.描述内容均以标题为中心展开. 正文部分

[转载] 站内消息DB设计思路

[转载]:点击打开链接  原文标题:两年后,再议“站内信”的实现  注:仅因为在工作中用到相似的东西,故借鉴作者的文章,如有不便请告知,即刻删除 两年前,万仓一黍在博客园发了两篇关于站内信的设计实现博文,<群发“站内信”的实现>.<群发“站内信”的实现(续)>,其中阐述了他关于站内信群发的设计思想,很具有借鉴意义.他在设计时考虑到用户量和存储空间的占用等问题.当然,在他的两篇博文中强调了站内信的设计要考虑具体情况,没有理想的设计方案,他的设计只是对于群发(点到面)的解决方案. 在此

易宝典文章——玩转Office 365中的Exchange Online服务 之二十五 配置出站垃圾邮件策略通知

企业的邮件服务器应该遵从良好的发件规范,尽量减小发送垃圾邮件和批量邮件到外部邮件系统.这样可以保护企业的邮件服务器发送IP不被收集到垃圾邮件服务器列表中,从而保证从企业的邮件服务器或企业域名发送的邮件,收件方能够正常接收.基于此种情况,出站垃圾邮件筛选即尤为重要,因为它起到了保护企业邮件系统发送邮件的可靠性,同时也保护了企业在商业交往中的信誉和利益.Exchange Online的出站垃圾邮件筛选与入站筛选类似,均有连接筛选和内容筛选构成,但是出站筛选是无法进行配置的.不过可以配置出站垃圾邮件策

【设计用例】站内信

[站内信]也被称作[站内消息],如果验证它呢?需要考虑的无非是2个方面: 功能验证 - 是否覆盖了所有的场景(开发人员会在代码里注释出来 会触发站内消息的场景 把这些沟通并记录下来 以免设计用例有所遗漏) 样式验证 - 站内消息里面包含的文字描述是否得到了正常的展示

“站内信”的实现

站内信分为"点到点"和"点到面","点到点"属于私信,用户之间传递的信息,一对一传递."点到面",属于系统消息或者公共信息,属于一对多发送. 站内信的设计既要考虑到投递的准确性(也就是该收到的人能收到信息),也要考虑信息持久化存储空间占用问题,在他的第一篇博文中详细进行了介绍. 我们在此仅把第三种情况拿出来说明,也就是用户量为百万级,活跃用户只占其中的一部分. 数据库的设计: 表名:Message ID:编号:SendID:发

百度站内搜索应该注意哪些方面?

想过给自己网站添加一个站内搜索功能吗?几乎每个网站都会有站内搜索,因为他能够很好的提升用户体验,使用户快速的找到自己想要的内容!百度为站长们提供了一个站内搜索功能,而且使用它对网站的页面收录有一定的帮助!申请百度站内搜索很简单,按照他的步骤做就行!下面说说要注意的几点:     一.注意本地主机测试不能显示搜索结果页面 当你获取到代码并且把它写入网页后,如果你在本地测试的话你会发现不能显示搜索结果页面!因为百度站长平台验证的是你的网站,而且你新建搜索引擎填写的域名也是你网站的域名,这跟你本地主机