云办公笔记

技术点

功能: 1.登录,每个用户有不同的权限,通过权限来查看各个部门的员工 2.功能: 职称管理、部门管理[存储过程实现部门添加]、员工职位管理、员工工资套管理等 3.添加员工使用rabbitmq及thymeleaf实现邮箱实时发送

1.SpringSecurity 单点登录安全认证及授权 2.google Kaptcha 验证码校验 3.Redis缓存菜单数据 注意: 对菜单做更新删除添加的时候要清空redis中的数据

学习点

1.RBAC 角色访问控制模型的关联查询+子查询+自连接查询+ResultMap映射

注意点: 自连接查询有层级关系的话,子表需要进行如下操作,并且在xml文件通过ResultMap进行映射

SELECT DISTINCT
t1.* , 
t2.id AS id2,
           t2.component AS component2,
           t2.enabled AS enabled2,
           t2.iconCls AS iconCls2,
           t2.keepAlive AS keepAlive2,
           t2.name AS name2,
           t2.parentId AS parentId2,
           t2.requireAuth AS requireAuth2,
           t2.path AS path2

完整查询SQL+XML文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ansel.server.mapper.MenuMapper">
​
​
    <resultMap id="BaseResultMap" type="com.ansel.server.entity.Menu">
        <id column="id" property="id" />
        <result column="url" property="url" />
        <result column="path" property="path" />
        <result column="component" property="component" />
        <result column="name" property="name" />
        <result column="iconCls" property="iconCls" />
        <result column="keepAlive" property="keepAlive" />
        <result column="requireAuth" property="requireAuth" />
        <result column="parentId" property="parentId" />
        <result column="enabled" property="enabled" />
    </resultMap>
    <resultMap id="Menus" type="com.ansel.server.entity.Menu" extends="BaseResultMap">
          <collection property="children" ofType="com.ansel.server.entity.Menu">
              <id column="id2" property="id" />
              <result column="url2" property="url" />
              <result column="path2" property="path" />
              <result column="component2" property="component" />
              <result column="name2" property="name" />
              <result column="iconCls2" property="iconCls" />
              <result column="keepAlive2" property="keepAlive" />
              <result column="requireAuth2" property="requireAuth" />
              <result column="parentId2" property="parentId" />
              <result column="enabled2" property="enabled" />
          </collection>
​
    </resultMap>
    <!--    自关联查询-->
    <select id="getMenusByAdminId" resultMap="Menus" parameterType="Integer">
​
​
                SELECT DISTINCT
                t1.* ,
                t2.id AS id2,
                           t2.component AS component2,
                           t2.enabled AS enabled2,
                           t2.iconCls AS iconCls2,
                           t2.keepAlive AS keepAlive2,
                           t2.name AS name2,
                           t2.parentId AS parentId2,
                           t2.requireAuth AS requireAuth2,
                           t2.path AS path2
                FROM t_menu t1 ,
                (
                SELECT
                   m.*
                FROM
                t_admin a
                LEFT JOIN t_admin_role ad ON a.id = ad.adminId
                LEFT JOIN t_menu_role mr ON ad.rid = mr.rid
                LEFT JOIN t_menu m ON m.id = mr.mid
                WHERE a.`id`= 1
                )
                t2
                WHERE t1.id = t2.parentId AND t2.enabled = 1
                ORDER BY t2.id ASC
    </select>
</mapper>

2.对于不存在数据库的字段,mybatis-plus要加@TableField(exist = flase)

@TableField(exist = false)
@ApiModelProperty("角色")
private List<Role> roles;

3.本项目授权校验方式

一、数据库中有url 和 role 两个字段 1.url代表可以访问的路径 2.role代表用户的角色

二、配置CustomFilter过滤器可以获取当前的请求

作用: 1.iMenuService 用于获取所有menu的url和t_menu_role表中的角色

                        SELECT
                        m.*,
                        r.id AS rid,
                        r.name AS `rname`,
                        r.nameZh AS rnameZh
                        FROM
                        t_role r
                        LEFT JOIN t_menu_role mr
                        ON r.id = mr.rid
                        LEFT JOIN t_menu m
                        ON mr.mid = m.id
                        ORDER BY m.id

2.AntPathMatcher antPathMatcher = new AntPathMatcher(); match方法匹配路径是否一样 3.CustomFilter实现FilterInvocationSecurityMetadataSource方法获取请求的到匹配url的地址的角色,返回ConfigAttribute

package com.ansel.server.filter;
​
import com.ansel.server.entity.Menu;
import com.ansel.server.entity.Role;
import com.ansel.server.service.IMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
​
import java.util.Collection;
import java.util.List;
​
/**
 * @author Ansel Zhong
 * @title eoffice
 * @Date 2023/3/3
 * @Description 根据url分析请求所需的角色
 */
@Component
public class CustomFilter implements FilterInvocationSecurityMetadataSource {
​
    @Autowired
    private IMenuService iMenuService;
​
​
    AntPathMatcher antPathMatcher = new AntPathMatcher();
​
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        //获取请求的url
        String requestUrl = ((FilterInvocation) o).getRequestUrl();
        List<Menu> menus = iMenuService.getMenuswithRole();
        System.out.println("menus-->" + menus);
        System.out.println("requestUrl-->" + requestUrl);
        for (Menu menu : menus) {
            if (menu.getId() != null) {
                //判断请求url是否与菜单角色匹配
                if (antPathMatcher.match(menu.getUrl(),requestUrl)){
                    //获取角色姓名
                    String[] str = menu.getRoles().stream()
                            .map(Role::getName).toArray(String[]::new);
                    return SecurityConfig.createList(str);
                }
            }
        }
        //如果没有就给一个默认的登陆角色
        return SecurityConfig.createList("ROLE_LOGIN");
​
​
    }
​
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }
​
    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

三、配置CustomDecisionManager继承AccessDecisionManager [PS:关于权限设置,是在这里设设置的:]

public UserDetailsService userDetailsService(){
    return username -> {
        Admin admin = adminService.getAdminByUserName(username);
        if (admin != null) {
            admin.setRoles(adminService.getRoles(admin.getId()));
            return admin;
        }
        throw new UsernameNotFoundException("用户名或者密码不正确");
    };
}
AdminServiceImpl类
//更新security登录用户对象
UsernamePasswordAuthenticationToken passwordAuthenticationToken
        = new UsernamePasswordAuthenticationToken(userDetails,null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(passwordAuthenticationToken);

此类获取authentication --> getAuthorities()方法获取authorities权限, 通过比对CustomFilter返回的权限来判断改用户是否有权限

package com.ansel.server.filter;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * @author Ansel Zhong
 * @title eoffice
 * @Date 2023/3/3
 * @Description
 * 判断用户角色
 */
@Component
public class CustomDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {
        for (ConfigAttribute configAttribute : collection) {
            //当前url所需要的角色
            String needRole = configAttribute.getAttribute();
            //判断角色是登陆即可访问的角色,此角色在CustomFilter中设置
            if ("ROLE_LOGIN".equals(needRole)){
                if (authentication instanceof AnonymousAuthenticationToken){
                    throw new AccessDeniedException("尚未登陆...");
                }else {
                    return;
                }
            }
            //
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
            //判断用户角色是否为url所需的角色
            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)){
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足,请联系管理员");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return false;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

Config类Configure这里新增

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
            .csrf()
            .disable()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .authorizeRequests()
            .antMatchers("/login", "/logout","/captcha")
            .permitAll()
            //除了上面所有请求都拦截
            .anyRequest()
            .authenticated()
            //TODO --> 动态权限配置
            .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                @Override
                public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                    o.setAccessDecisionManager(customDecisionManager);
                    o.setSecurityMetadataSource(customFilter);
                    return o;
                }
            })
            .and()
            //禁用缓存
            .headers()
            .cacheControl();
    //添加jwt 登录授权过滤器
    http
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    //添加自定义未授权登录结果返回
    http.exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler)
            .authenticationEntryPoint(authorizationEntryPoint);
}

4.JsonFormat转换pattern

数据库timestamp, java localdatetime类型json转换

@JsonFormat(pattern = "yyyy-MM-dd", timezone = "Asia/Shanghai")
private LocalDateTime createDate;

5.存储过程的创建和调用

  • 存储过程就i是具有名字的一段代码,用来完成一个特定的功能

  • 创建存储过程保存在数据库的数据字典中

CREATE 
 #[指定当前用户]
 [DEFINER = {USER | CURRENT_USER}]
PROCEDURE 存储过程名字 ({proc_parameter[...]})

proc_parameter:
   [IN | OUT } INOUT] para_name TYPE
characteristic:
   COMMENT 'string' 
   LANGUAGE SQL 
 
 routine_body:
     valid SQL ROUTINE statement
     BEGIN 
     [statement_list]
     END 

MSQL存储过程的关键语法

DELIMITER $$ / //

声明存储过程

CREATE PROCEDURE demo_in_parameter (in p_in int)

变量赋值:

set @p_in = 1

调用过程

CALL sp_name[(传参)]
  • in: 表示调用者向过程传入值 可以是字面量或者变量

  • out: 返回的值

  • INOUT: 既可以传入又可以传出

简单创建

CREATE PROCEDURE GreetWolrd5() SELECT CONCAT (@greeting2, 'world')
SET @greeting2 = 'Hello';
CALL GreetWolrd5();

6.xml中写递归查询案例

    <resultMap id="BaseResultMap" type="com.ansel.server.entity.Department">
        <id column="id" property="id" />
        <result column="name" property="name" />
        <result column="parentId" property="parentId" />
        <result column="depPath" property="depPath" />
        <result column="enabled" property="enabled" />
        <result column="isParent" property="isParent" />
    </resultMap>

    <resultMap id="DepartmentWithChildren" type="com.ansel.server.entity.Department" extends="BaseResultMap">
        <collection property="children" ofType="com.ansel.server.entity.Department"
                    select="com.ansel.server.mapper.DepartmentMapper.getAllDepartments" column="id">
<!--             拿children再去调用递归的方法-->

        </collection>
    </resultMap>

    <select id="getAllDepartments" resultMap="DepartmentWithChildren">
       select
           *
           from t_department
           where parentId = #{id}
    </select>

7.模糊查询

SELECT 
a.*,
r.id AS rid,
r.name AS rname,
r.nameZh AS rnameZh
FROM 
t_admin a
LEFT JOIN t_admin_role ar 
ON a.id = ar.adminId
LEFT JOIN t_role r 
ON r.id = ar.rid 
WHERE a.id != 1
AND a.name LIKE CONCAT('%',"淑",'%');

8.分页+多条件查询

1.配置类

package com.ansel.server.config;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public PaginationInnerInterceptor paginationInnerInterceptor(){
        return new PaginationInnerInterceptor();
    }
}

2.Service

package com.ansel.server.service.impl;

import com.ansel.server.entity.Employee;
import com.ansel.server.entity.EmployeeEc;
import com.ansel.server.entity.RespPageBean;
import com.ansel.server.mapper.EmployeeEcMapper;
import com.ansel.server.mapper.EmployeeMapper;
import com.ansel.server.service.IEmployeeEcService;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author ansel
 * @since 2023-03-02
 */
@Service
public class EmployeeEcServiceImpl extends ServiceImpl<EmployeeEcMapper, EmployeeEc> implements IEmployeeEcService {

    @Autowired
    private EmployeeMapper employeeMapper;
    @Override
    public RespPageBean getEmployeeByPage(Integer currentPage,Integer size, Employee employee, LocalDate[] beginDateScope) {
        Page<Employee> page = new Page<>(currentPage,size);
        IPage<Employee> employeeByPage = employeeMapper.getEmployeeByPage(page, employee, beginDateScope);
        RespPageBean respPageBean = new RespPageBean(employeeByPage.getTotal(),employeeByPage.getRecords());
        return respPageBean;
    }
}

3.mapper

@Mapper
public interface EmployeeMapper extends BaseMapper<Employee> {
    IPage<Employee> getEmployeeByPage(Page<Employee> page, Employee employee, LocalDate[] beginDateScope);
}

4.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ansel.server.mapper.EmployeeMapper">


    <resultMap id="BaseResultMap" type="com.ansel.server.entity.Employee">
        <id column="id" property="id"></id>
        <result column="name" property="name"></result>
    </resultMap>


    <resultMap id="EmployInfo" type="com.ansel.server.entity.Employee" extends="BaseResultMap">
           <association property="nation" javaType="com.ansel.server.entity.Nation">
               <id column="nid" property="id"></id>
               <result column="nname" property="name"></result>
           </association>
            <association property="politicsStatus" javaType="com.ansel.server.entity.PoliticsStatus">
                <id column="pid" property="id"></id>
                <result column="pname" property="name"></result>
            </association>
        <association property="department" javaType="com.ansel.server.entity.Department">
            <id column="did" property="id"></id>
            <result column="dname" property="name"></result>
        </association>
        <association property="joblevel" javaType="com.ansel.server.entity.Joblevel">
            <id column="did" property="id"></id>
            <result column="dname" property="name"></result>
        </association>
        <association property="position" javaType="com.ansel.server.entity.Position">
            <id column="posid" property="id"></id>
            <result column="posname" property="name"></result>
        </association>
    </resultMap>


<!--    分页获取所有员工-->
    <select id="getEmployeeByPage" resultMap="EmployInfo">

                SELECT
                e.*,
                n.id AS nid,
                n.name AS nname,
                p.id AS pid,
                p.name AS pname,
                d.id AS did,
                d.name AS dname,
                 j.id AS jid,
                j.name AS jname,
                pos.id AS posid,
                pos.name AS posname
                FROM
                t_employee e,
                t_nation n,
                t_politics_status p,
                t_department d,
                t_joblevel j,
                t_position pos
                WHERE
                e.nationId = n.`id`
                AND
                e.`politicId` = p.`id`
                AND
                e.`jobLevelId` = j.`id`
                AND
                e.posId = pos.`id`
                <if test="employee.name != null and employee.name != ''">
                    AND e.name LIKE CONCAT("%", #{employee.name}, '%')
                </if>
                <if test="employee.politicId != null">
                    AND e.`politicId`= #{employee.politicId}
                </if>
                <if test="employee.nationId != null">
                    AND e.`nationId`= #{employee.nationId}
                </if>
                <if test="employee.jobLevelId != null">
                    AND e.`jobLevelId`= #{employee.jobLevelId}
                </if>
                <if test="employee.posId != null">
                    AND e.`posId`= #{employee.posId}
                </if>
                <if test="employee.engageForm != null">
                    AND e.`engageForm`= #{employee.engageForm}
                </if>
                <if test="employee.departmentId != null">
                    AND e.`departmentId`= #{employee.departmentId}
                </if>
                <if test="beginDateScope != null and 2 == beginDateScope.length">
                    AND e.`beginDate` between #{beginDateScope[0]} and #{beginDateScope[1]}
                </if>
                ORDER BY e.`id`
    </select>
</mapper>

9.LocalDateTime的一些方法

两个LocalDate的天数相差多少天

@Test
public void testUntil() {
    LocalDate start = LocalDate.of(2023, 3, 5);
    LocalDate end = LocalDate.of(2023, 9, 1);
    long until = start.until(end, ChronoUnit.DAYS);
    System.out.println(until);
}

10.发邮件 (注意: 实体类一定要序列化)

导包

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-mail</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

1.打开POP3/SMTP服务 2.启动类:

@Bean
public Queue queue(){
    return new Queue("mail.welcome");
}

3.controller类

@Autowired
RabbitTemplate rabbitTemplate;
@ApiOperation(value = "发邮件给员工")
@GetMapping("/")
@ResponseBody
public RespPageBean mail() {
    //发送信息
    Employee employee = new Employee();
    employee.setName("ansel");
    employee.setEmail("anxelswanz@163.com");
    //一定要实现serializable接口
    rabbitTemplate.convertAndSend("mail.welcome",employee);
    return null;
}

4.MailReceiver

package com.ansel.server.mail;

import com.ansel.server.entity.Employee;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import java.util.Date;


/**
 * @author Ansel Zhong
 * @title email
 * @Date 2023/3/6
 * @Description 消息接收
 */
@Component
@Slf4j
public class MailReceiver {
  private static final Logger LOGGER = LoggerFactory.getLogger(MailReceiver.class);
   @Autowired
    JavaMailSender javaMailSender;

   @Autowired
    private MailProperties properties;

   @Autowired
    private TemplateEngine templateEngine;

    private String DEFAULTEMAIL = "1035205314@qq.com";
   @RabbitListener(queues = "mail.welcome")
 public void handler(Employee employee) throws MessagingException {
      // employee.setEmail("anxelswanz@163.com");
       if (employee.getEmail() != null) {
           this.DEFAULTEMAIL = employee.getEmail();
       }
    try {
     MimeMessage mimeMessage = javaMailSender.createMimeMessage();
     MimeMessageHelper helper = new MimeMessageHelper(mimeMessage);
     //发件人
     helper.setFrom(properties.getUsername());
     helper.setTo(DEFAULTEMAIL);
     helper.setSubject("入职欢迎");
     helper.setSentDate(new Date());
     Context context = new Context();
     context.setVariable("name", employee.getName());
     String mail = templateEngine.process("mail", context);
     helper.setText(mail, true);
     //发送邮件
     javaMailSender.send(mimeMessage);
    } catch (MessagingException e) {
     LOGGER.error("邮件发送失败!--》{}", e.getMessage());

    } catch (MailException e) {
     e.printStackTrace();
    }
   }

}

11.消息回调机制确保消息可靠性

上游发送消息,写入数据库,下游收到消息会发confirm信息,如果发送成功,则设置status为1,如果发送失败,则调用定时任务重复上述步骤,如果超过三次,就设置status为2

 

1.数据库

 

2.Entity

package com.ansel.server.entity;

import com.baomidou.mybatisplus.annotation.TableName;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * <p>
 *
 * </p>
 *
 * @author ansel
 * @since 2023-03-02
 */
@TableName("t_mail_log")
public class MailLog implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 消息id
     */
    private String msgId;

    /**
     * 接收员工id
     */
    private Integer eid;

    /**
     * 状态(0:消息投递中 1:投递成功 2:投递失败)
     */
    private Integer status;

    /**
     * 路由键
     */
    private String routeKey;

    /**
     * 交换机
     */
    private String exchange;

    /**
     * 重试次数
     */
    private Integer count;

    /**
     * 重试时间
     */
    private LocalDateTime tryTime;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;

    public String getMsgId() {
        return msgId;
    }

    public void setMsgId(String msgId) {
        this.msgId = msgId;
    }

    public Integer getEid() {
        return eid;
    }

    public void setEid(Integer eid) {
        this.eid = eid;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getRouteKey() {
        return routeKey;
    }

    public void setRouteKey(String routeKey) {
        this.routeKey = routeKey;
    }

    public String getExchange() {
        return exchange;
    }

    public void setExchange(String exchange) {
        this.exchange = exchange;
    }

    public Integer getCount() {
        return count;
    }

    public void setCount(Integer count) {
        this.count = count;
    }

    public LocalDateTime getTryTime() {
        return tryTime;
    }

    public void setTryTime(LocalDateTime tryTime) {
        this.tryTime = tryTime;
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    public void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public LocalDateTime getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(LocalDateTime updateTime) {
        this.updateTime = updateTime;
    }

    @Override
    public String toString() {
        return "MailLog{" +
                "msgId=" + msgId +
                ", eid=" + eid +
                ", status=" + status +
                ", routeKey=" + routeKey +
                ", exchange=" + exchange +
                ", count=" + count +
                ", tryTime=" + tryTime +
                ", createTime=" + createTime +
                ", updateTime=" + updateTime +
                "}";
    }
}

3.Controller

@Autowired
RabbitTemplate rabbitTemplate;
@ApiOperation(value = "发邮件给员工")
@GetMapping("/")
@ResponseBody
public RespPageBean mail() {
    //发送信息
    Employee employee = new Employee();
    employee.setName("ansel");
    employee.setEmail("anxelswanz@163.com");
    employee.setId(1);
    String msgID = UUID.randomUUID().toString();
    MailLog mailLog = new MailLog();
    mailLog.setMsgId(msgID);
    mailLog.setEid(employee.getId());
    mailLog.setStatus(0);
    mailLog.setRouteKey(MailConstants.MAIL_ROUTING_KEY_NAME);
    mailLog.setExchange(MailConstants.MAIL_EXCHANGE_NAME);
    mailLog.setCount(0);
    mailLog.setTryTime(LocalDateTime.now().plusMinutes(MailConstants.MSG_TIME_OUT));
    mailLog.setCreateTime(LocalDateTime.now());
    mailLog.setUpdateTime(LocalDateTime.now());
    mailLogMapper.insert(mailLog);

    //一定要实现serializable接口
    rabbitTemplate.convertAndSend("MailConstants.MAIL_EXCHANGE_NAME",
            MailConstants.MAIL_ROUTING_KEY_NAME,employee, new CorrelationData(msgID));


    return null;
}

4.config

package com.ansel.server.config;

import com.ansel.server.entity.MailConstants;
import com.ansel.server.entity.MailLog;
import com.ansel.server.service.IMailLogService;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Ansel Zhong
 * @title email
 * @Date 2023/3/6
 * @Description
 */
@Configuration
@Slf4j
public class RabbitMQConfig {

    @Autowired
    private CachingConnectionFactory cachingConnectionFactory;

    @Autowired
    private IMailLogService iMailLogService;
    @Bean
    public RabbitTemplate rabbitTemplate(){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);

        //确认消息是否到达broker
        /**
         * data: 消息唯一标识 uuid
         * ack : 确认结果
         * cause: 失败原因
         */
        rabbitTemplate.setConfirmCallback((data,ack,cause) -> {
            String msgId = data.getId();
            if (ack){
               log.info("{}成功", msgId);
               iMailLogService.update(new UpdateWrapper<MailLog>().set("status",1).eq("msgId", msgId));
            }else {
                log.info("{}发送失败", msgId);
            }
        });
        /**
         * 消息失败回调, 比如router步到queue
         * msg 消息主题
         * repCode 响应码
         * repText 响应描述
         * exchange 交换机
         * routingKey 路由键
         */
        rabbitTemplate.setReturnCallback((msg,repCode, repText, exchange, routingkey)->{
           log.info("消息发送queue时失败");
        });
        return rabbitTemplate;
    }

    @Bean
    public Queue queue(){
        return new Queue(MailConstants.MAIL_QUEUE_NAME);
    }

    @Bean
    public DirectExchange directExchange(){
        return new DirectExchange(MailConstants.MAIL_EXCHANGE_NAME);
    }

    @Bean
    public Binding binding(){
        return BindingBuilder.bind(queue()).to(directExchange()).with(MailConstants.MAIL_ROUTING_KEY_NAME);
    }
}

5.config

# rabbitmq配置
rabbitmq:
  # 用户名
  username: ansel
  # 密码
  password: zrh
  # 服务器地址
  host: 192.168.221.128
  # 端口
  port: 5672
  listener:
    simple:
      #开启手动确认
      acknowledge-mode: manual
  publisher-confirm-type: correlated
  publisher-returns: true

6.定时任务

package com.ansel.server.task;

import com.ansel.server.entity.Employee;
import com.ansel.server.entity.MailConstants;
import com.ansel.server.entity.MailLog;
import com.ansel.server.service.IEmployeeEcService;
import com.ansel.server.service.IMailLogService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import net.bytebuddy.asm.Advice;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @author Ansel Zhong
 * @title email
 * @Date 2023/3/6
 * @Description 邮件发送服务定时任务
 */
@Component
public class MailTask {

    @Autowired
    private IMailLogService iMailLogService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    /**
     * 十秒执行一次
     */
    @Scheduled(cron = "0/10 * * * * ?")
    public void mailTask(){
        List<MailLog> list = iMailLogService.list(new QueryWrapper<MailLog>()
                .eq("status", 0).lt("tryTime", LocalDateTime.now()));
        list.forEach(mailLog -> {

            //如果重试超过三次就更新失败
            if (mailLog.getCount() >= 3) {
                iMailLogService.update(new UpdateWrapper<MailLog>().set("status",2).eq("msgId", mailLog.getMsgId()));
            }
            iMailLogService.update(new UpdateWrapper<MailLog>().set("count",mailLog.getCount() + 1).set("updateTime",LocalDateTime.now().plusMinutes(MailConstants.MSG_TIME_OUT)));
            Employee employee = new Employee();
            employee.setId(1);
            employee.setName("ansel");
            employee.setEmail("anxelswanz@163.com");
            rabbitTemplate.convertAndSend("MailConstants.MAIL_EXCHANGE_NAME",MailConstants.MAIL_ROUTING_KEY_NAME,
                     employee,new CorrelationData(mailLog.getMsgId()));

        });
    }
}

7.消息接收

package com.ansel.server.mail;

import com.ansel.server.entity.Employee;
import com.ansel.server.entity.MailConstants;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.mail.MailProperties;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;

import java.util.Date;


/**
 * @author Ansel Zhong
 * @title email
 * @Date 2023/3/6
 * @Description 消息接收
 */
@Component
@Slf4j
public class MailReceiver {
  private static final Logger LOGGER = LoggerFactory.getLogger(MailReceiver.class);
   @Autowired
    JavaMailSender javaMailSender;

   @Autowired
    private MailProperties properties;

   @Autowired
    private TemplateEngine templateEngine;

    private String DEFAULTEMAIL = "1035205314@qq.com";
   @RabbitListener(queues = MailConstants.MAIL_QUEUE_NAME)
 public void handler(Employee employee) throws MessagingException {
      // employee.setEmail("anxelswanz@163.com");
       if (employee.getEmail() != null) {
           this.DEFAULTEMAIL = employee.getEmail();
       }
    try {
     MimeMessage mimeMessage = javaMailSender.createMimeMessage();
     MimeMessageHelper helper = new MimeMessageHelper(mimeMessage);
     //发件人
     helper.setFrom(properties.getUsername());
     helper.setTo(DEFAULTEMAIL);
     helper.setSubject("入职欢迎");
     helper.setSentDate(new Date());
     Context context = new Context();
     context.setVariable("name", employee.getName());
     String mail = templateEngine.process("mail", context);
     helper.setText(mail, true);
     //发送邮件
     javaMailSender.send(mimeMessage);
    } catch (MessagingException e) {
     LOGGER.error("邮件发送失败!--》{}", e.getMessage());

    } catch (MailException e) {
     e.printStackTrace();
    }
   }

}

12.WebSocket

1.websocket是html5开始提供的全双工通讯协议。 2.WebSocket使的客户端和服务器之间的数据交换变得简单,只需要一次握手。 3.最大的特点就是服务器可以主动向客户端推送消息,客户端也可以以主动向服务器推送消息。真正的双向数据传输。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值