xxl-job企业微信告警实现

本文详细介绍了如何在XXL-Job中集成微信企业号告警功能,包括UI调整、配置参数、工具类准备、Curd操作及告警发送核心代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

xxl-job默认提供了邮箱告警,添加企微告警要折腾一番,并不是像营销文说的那么简单加个实现类就行,本文提供xxl-job企微告警实现。

效果展现

■ 编辑UI展示

在这里插入图片描述

■ 告警推送展示

在这里插入图片描述

列出改动清单

在这里插入图片描述

开始敲代码。。。

■ 获取corpid和corpsecret

企业ID和应用的凭证密钥,按官方文档获取。上传个图片创建应用就行了,我当时是登错了地方,进了企业微信服务商的管理后台一直找不到入口,坑了不少时间。

■ Properties

corpid=XXXXXXXXXXXXXXXXX
corpsecret=XXXXXX_XXXXXXXXXXXXXXXXXXXXX-XXXXXXXXXXXXXX

■ POM

<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpcore</artifactId>
	<version>4.4.6</version>
</dependency>
<dependency>
	<groupId>org.apache.httpcomponents</groupId>
	<artifactId>httpclient</artifactId>
	<version>4.5.5</version>
</dependency>

■ 工具类准备

为了节省开发时间,几个企微工具类我也是从网上搜索过来改造,并不是我喜欢的方式。
① IacsUrlDataVo

package com.xxl.job.admin.core.alarm.WeiUtils;

public class IacsUrlDataVo {
    String corpid;
    String corpsecret;
    String Get_Token_Url;
    String SendMessage_Url;

    public String getCorpid() {
        return corpid;
    }
    public void setCorpid(String corpid) {
        this.corpid = corpid;
    }
    public String getCorpsecret() {
        return corpsecret;
    }
    public void setCorpsecret(String corpsecret) {
        this.corpsecret = corpsecret;
    }
    public void setGet_Token_Url(String corpid,String corpsecret) {
        this.Get_Token_Url ="https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid="+corpid+"&corpsecret="+corpsecret;
    }
    public String getGet_Token_Url() {
        return Get_Token_Url;
    }
    public String getSendMessage_Url(){
        SendMessage_Url = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=";
        return SendMessage_Url;
    }
}

② IacsWeChatDataVo

package com.xxl.job.admin.core.alarm.WeiUtils;

public class IacsWeChatDataVo {
    String touser;
    String toparty;
    String msgtype;
    int agentid;
    Object text;//实际接收Map类型数据
    Object markdown;//实际接收Map类型数据

    public Object getText() {
        return text;
    }
    public void setText(Object text) {
        this.text = text;
    }

    public Object getMarkdown() {
        return markdown;
    }

    public void setMarkdown(Object markdown) {
        this.markdown = markdown;
    }

    public String getMsgtype() {
        return msgtype;
    }
    public void setMsgtype(String msgtype) {
        this.msgtype = msgtype;
    }
    public int getAgentid() {
        return agentid;
    }
    public void setAgentid(int agentid) {
        this.agentid = agentid;
    }
    public String getTouser() {
        return touser;
    }
    public void setTouser(String touser) {
        this.touser = touser;
    }

    public String getToparty() {
        return toparty;
    }

    public void setToparty(String toparty) {
        this.toparty = toparty;
    }
}

③ SendWeChatUtils

package com.xxl.job.admin.core.alarm.WeiUtils;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class SendWeChatUtils {

    private CloseableHttpClient httpClient;
    private HttpPost httpPost;//用于提交登陆数据
    private HttpGet httpGet;//用于获得登录后的页面
    public static final String CONTENT_TYPE = "Content-Type";
    SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    public static String token = "";
    private static Gson gson = new Gson();


//    /**
//     * 微信授权请求,GET类型,获取授权响应,用于其他方法截取token
//     *
//     * @param Get_Token_Url
//     * @return String 授权响应内容
//     * @throws IOException
//     */
    public String toAuth(String Get_Token_Url) throws IOException {

        httpClient = HttpClients.createDefault();
        httpGet = new HttpGet(Get_Token_Url);
        CloseableHttpResponse response = httpClient.execute(httpGet);
        String resp;
        try {
            HttpEntity entity = response.getEntity();
            resp = EntityUtils.toString(entity, "utf-8");
            EntityUtils.consume(entity);
        } finally {
            response.close();
        }
        LoggerFactory.getLogger(getClass()).info(" resp:{}", resp);
        return resp;
    }

    /**
     * 获取toAuth(String Get_Token_Url)返回结果中键值对中access_token键的值
     *
     * @param corpid 应用组织编号   corpsecret 应用秘钥
     */
    public void refreshToken() throws IOException {
        String corpid = Global.getConfig("corpid");
        String corpsecret = Global.getConfig("corpsecret");
        IacsUrlDataVo uData = new IacsUrlDataVo();
        uData.setGet_Token_Url(corpid, corpsecret);
        String resp = toAuth(uData.getGet_Token_Url());

        Map<String, Object> map = gson.fromJson(resp,
                new TypeToken<Map<String, Object>>() {
                }.getType());
        SendWeChatUtils.token = map.get("access_token").toString();
    }


    /**
     * @param touser         发送消息接收者    ,msgtype消息类型(文本/图片等),
     * @param application_id 应用编号。
     * @return String
     * @Title:创建微信发送请求post数据
     */
    public String createpostdata(String touser, String toparty, String msgtype,
                                 int application_id, String contentKey, String contentValue) {
        IacsWeChatDataVo wcd = new IacsWeChatDataVo();
        wcd.setTouser(touser);
        wcd.setToparty(toparty);
        wcd.setAgentid(application_id);
        wcd.setMsgtype(msgtype);
        Map<Object, Object> content = new HashMap<Object, Object>();
        content.put(contentKey, contentValue + "\n--------\n" + df.format(new Date()));
        if("markdown".equals(msgtype)) {
            wcd.setMarkdown(content);
        } else {
            wcd.setText(content);
        }
        return gson.toJson(wcd);
    }

    /**
     * @param charset 消息编码    ,contentType 消息体内容类型,
     * @param url     微信消息发送请求地址,data为post数据,token鉴权token
     * @return String
     * @Title 创建微信发送请求post实体
     */
    public String post(String charset, String contentType, String url,
                       String data, String token) throws IOException {
       return post(charset, contentType, url, data, token, 3);
    }

    public String post(String charset, String contentType, String url,
                       String data, String token, int count) throws IOException {
        CloseableHttpClient httpclient = HttpClients.createDefault();
        httpPost = new HttpPost(url + token);
        httpPost.setHeader(CONTENT_TYPE, contentType);
        httpPost.setEntity(new StringEntity(data, charset));
        CloseableHttpResponse response = httpclient.execute(httpPost);
        String resp;
        try {
            HttpEntity entity = response.getEntity();
            resp = EntityUtils.toString(entity, charset);
            EntityUtils.consume(entity);
        } finally {
            response.close();
        }

        Map<String, Object> map = gson.fromJson(resp,
                new TypeToken<Map<String, Object>>() {
                }.getType());
        String errcode = map.get("errcode").toString();
        if (!"0.0".equals(errcode) && !"0".equals(errcode) && count > 0) {
            refreshToken();
            LoggerFactory.getLogger(getClass()).info("token失效");
            return post(charset, contentType, url, data, SendWeChatUtils.token, --count);
        }

        LoggerFactory.getLogger(getClass()).info(
                "call [{}], param:{}, resp:{}", url, data, resp);
        return resp;
    }
}

④ Global
获取配置工具类

/**
 * Copyright &copy; 2012-2014 <a href="https://github.com/thinkgem/jeesite">JeeSite</a> All rights reserved.
 */
package com.xxl.job.admin.core.alarm.WeiUtils;

import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;

/**
 * 全局配置类
 *
 * @author bbq
 * @version 2021-05-13
 */
@Component
public class Global implements EnvironmentAware {

    private static Environment environment;

    public static String getConfig(String key) {
        return environment.getProperty(key);
    }

    public static String getConfig(String key, String defaultValue) {
        String value = getConfig(key);
        return value != null ? value : defaultValue;
    }

    public static Integer getConfigInteger(String key) {
        return environment.getProperty(key, Integer.class);
    }

    public static Integer getConfigInteger(String key, Integer defaultValue) {
        Integer value = getConfigInteger(key);
        return value != null ? value : defaultValue;
    }

    @Override
    public void setEnvironment(Environment environment) {
        Global.environment = environment;
    }

    /**
     * 当前对象实例
     */
    private static Global global = new Global();

    /**
     * 获取当前对象实例
     */
    public static Global getInstance() {
        return global;
    }

}

■ UI修改

① message_zh_CN.properties
在这里插入图片描述

jobinfo_field_weimail=企业微信告警
jobinfo_field_weimail_placeholder=例:user1|user2&&party1|party2,不发给部门省略&&以及后面,多个对象|分隔

② jobinfo.index.1.js
在这里插入图片描述

$("#updateModal .form input[name='alarmWei']").val( row.alarmWei );

③ jobinfo.index.ftl
在这里插入图片描述

<div class="form-group">
	<label for="lastname" class="col-sm-2 control-label">${I18n.jobinfo_field_weimail}<font color="black">*</font></label>
	<div class="col-sm-8"><input type="text" class="form-control" name="alarmWei" placeholder="${I18n.jobinfo_field_weimail_placeholder}" maxlength="100" ></div>
</div>

■ curd代码

① xml
XxlJobInfoMapper.xml
在这里插入图片描述

<result column="alarm_wei" property="alarmWei" />
t.alarm_wei,
alarm_wei,
#{alarmWei},
alarm_wei = #{alarmWei},

② bean
com.xxl.job.admin.core.model.XxlJobInfo
在这里插入图片描述

private String alarmWei;	// 企业微信告警

③ servicempl
com.xxl.job.admin.service.impl.XxlJobServiceImpl
在这里插入图片描述

exists_jobInfo.setAlarmWei(jobInfo.getAlarmWei());

④ 告警thread
com.xxl.job.admin.core.thread.JobFailMonitorHelper
在这里插入图片描述

if (info != null && ((info.getAlarmEmail() != null && info.getAlarmEmail().trim().length() > 0)
										|| (info.getAlarmWei() != null && info.getAlarmWei().trim().length() > 0))) {

⑤ WeiJobAlarm
com.xxl.job.admin.core.alarm.impl.WeiJobAlarm
终于来到了具体的告警实现,也就是网上所谓的复制粘贴改改就好了。。。

package com.xxl.job.admin.core.alarm.impl;

import com.xxl.job.admin.core.alarm.JobAlarm;
import com.xxl.job.admin.core.alarm.WeiUtils.IacsUrlDataVo;
import com.xxl.job.admin.core.alarm.WeiUtils.SendWeChatUtils;
import com.xxl.job.admin.core.conf.XxlJobAdminConfig;
import com.xxl.job.admin.core.model.XxlJobGroup;
import com.xxl.job.admin.core.model.XxlJobInfo;
import com.xxl.job.admin.core.model.XxlJobLog;
import com.xxl.job.admin.core.util.I18nUtil;
import com.xxl.job.core.biz.model.ReturnT;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.text.MessageFormat;

/**
 * job alarm by wei
 *
 * @author bbq 2021-05-12
 */
@Component
public class WeiJobAlarm implements JobAlarm {
    private static Logger logger = LoggerFactory.getLogger(WeiJobAlarm.class);
    private static String token = "";

    /**
     * fail alarm
     *
     * @param jobLog
     */
    @Override
    public boolean doAlarm(XxlJobInfo info, XxlJobLog jobLog){
        boolean alarmResult = true;

        // send monitor email
        if (info!=null && info.getAlarmWei()!=null && info.getAlarmWei().trim().length()>0) {

            // alarmContent
            String alarmContent = "Alarm Job LogId=" + jobLog.getId();
            if (jobLog.getTriggerCode() != ReturnT.SUCCESS_CODE) {
                alarmContent += "\nTriggerMsg=\n" + jobLog.getTriggerMsg().replaceAll("<br>","|| ");
            }
            if (jobLog.getHandleCode()>0 && jobLog.getHandleCode() != ReturnT.SUCCESS_CODE) {
                alarmContent += "\nHandleCode=" + jobLog.getHandleMsg();
            }

            // email info
            XxlJobGroup group = XxlJobAdminConfig.getAdminConfig().getXxlJobGroupDao().load(Integer.valueOf(info.getJobGroup()));
            String content = MessageFormat.format(loadEmailJobAlarmTemplate(),
                    group!=null?group.getTitle():"null",
                    info.getId(),
                    info.getJobDesc(),
                    alarmContent);

            SendWeChatUtils msgUtils = new SendWeChatUtils();
            try {
                String touser = info.getAlarmWei().trim();
                String toparty = "";
                if(touser.contains("&&")) {
                    String str[] = touser.split("&&");
                    touser = str[0];
                    if(str.length > 1) {
                        toparty = str[1];
                    }
                }
                String postdata = msgUtils.createpostdata(touser, toparty,"markdown", 应用id, "content", content);
                String resp = msgUtils.post("utf-8", SendWeChatUtils.CONTENT_TYPE, (new IacsUrlDataVo()).getSendMessage_Url(), postdata, SendWeChatUtils.token);
//                System.out.println("请求数据======>" + postdata);
//                System.out.println("发送微信的响应数据======>" + resp);
            } catch (IOException e) {
                e.printStackTrace();
                alarmResult = false;
            }
        }

        return alarmResult;
    }

    /**
     * load email job alarm template
     *
     * @return
     */
    private static final String loadEmailJobAlarmTemplate(){
        String mailBodyTemplate = "# {2}\n"
                + "> `**事项详情**` \n"
                + "> " + I18nUtil.getString("jobinfo_field_jobgroup") + ":<font color=\"comment\">{0}</font> \n"
                + "> " + I18nUtil.getString("jobinfo_field_id")+":<font color=\"warning\">{1}</font>\n"
                + "> " + I18nUtil.getString("jobconf_monitor_alarm_title") + ":<font color=\"comment\">"
                + I18nUtil.getString("jobconf_monitor_alarm_type") + "</font>\n"
                + "> "+ I18nUtil.getString("jobconf_monitor_alarm_content") + ":\n<font color=\"info\">{3}</font> \n";

        return mailBodyTemplate;
    }

}

⑥ 数据库
最后别忘了往你的数据库添加字段即完成。

最后

然后就可以类似配置邮箱一样配置企微了,可配置部门和用户。

<think>好的,我需要帮助用户寻找能够为Java简历增加亮点的项目经验和技能。首先,用户提到有1-2年的经验,但可能项目经验不够丰富,特别是可能只有类似图书管理系统这样的基础项目。所以,我需要考虑如何包装这样的项目,使其看起来更具竞争力,同时推荐一些能提升简历的额外技能和项目类型。 根据引用[1],用户需要将新手项目包装成1-2年经验,重点突出被问到的技能点。引用[3]提到个人在线简历的搭建是一个加分项,这可能涉及前端技术,但作为后端开发,如何结合呢?可能需要全栈的项目展示。另外,引用[2]强调了Java核心架构知识点的重要性,如JVM、高并发、分布式等,这些应该在技能部分突出。 接下来,我需要列举具体的项目示例,比如微服务架构、电商秒杀系统、分布式任务调度平台等,这些都是市场需求较高的项目类型。同时,技能点要包括主流框架、数据库优化、缓存技术、消息队列等。此外,优化方法如性能调优、JVM参数调整、分库分表等也是关键。 还要考虑如何将简单的项目如图书管理系统进行包装,加入技术亮点,比如引入Redis缓存、使用Spring Security进行权限控制等。另外,建议用户搭建个人技术博客或开源项目,展示持续学习的能力,这也是引用[3]提到的在线简历的延伸。 最后,生成相关问题,帮助用户进一步深入,比如如何设计高并发系统,如何优化数据库性能等。这些都需要结合引用中的资料,特别是引用[2]中的核心架构知识点。</think>### Java简历加分项项目与技能建议 #### 一、高价值项目经验示例 1. **微服务架构电商平台** - 使用Spring Cloud Alibaba(Nacos/Seata/Sentinel)构建商品、订单、支付模块 - 实现分布式事务(TCC模式)和接口限流(QPS 5000+) - 采用Redis+Lua脚本实现分布式锁,解决超卖问题[^2] - 项目成果:TPS提升300%,订单处理耗时降低至50ms内 2. **高并发秒杀系统** - 使用RabbitMQ削峰填谷处理10万+/秒请求 - 通过Redis Cluster预减库存+本地缓存二级校验 - 采用CDN静态资源分发+限流熔断策略 - 技术指标:成功承载双11级别流量冲击[^2] 3. **分布式任务调度平台** - 基于XXL-JOB二次开发,支持动态分片执行 - 实现任务编排(DAG工作流)+ 执行日志追踪 - 集成邮件/企业微信告警机制 - 应用场景:日处理百万级定时任务[^3] #### 二、核心技能亮点组合 | 技能类别 | 推荐技术栈 | |----------------|--------------------------------------------------------------------------| | 基础架构 | JVM调优(GC日志分析)、多线程(ThreadLocal/CAS)、NIO(Netty) | | 分布式 | Dubbo/Zookeeper、Seata分布式事务、Redisson分布式锁 | | 数据库 | MySQL索引优化(EXPLAIN)、分库分表(ShardingSphere)、ES全文检索 | | 高可用 | Sentinel熔断降级、SkyWalking全链路监控、K8S容器化部署 | | 工程化 | Jenkins流水线、Sonar代码质量检测、Spring Boot Starter自定义开发 | #### 三、项目包装技巧(以图书管理系统为例) ```java // 原始描述 "开发基于SSM的图书借阅系统,实现CRUD功能" // 升级后描述 "主导图书管理中台建设,采用领域驱动设计(DDD)划分借阅、库存、统计子域; 引入Elasticsearch实现多维度图书检索(响应时间<200ms); 通过Spring AOP+自定义注解实现细粒度权限控制(RBAC+ABAC混合模型); 使用Redisson实现分布式锁保障库存扣减一致性" ``` #### 四、差异化加分项 1. **技术影响力建设** - GitHub开源项目(Star 100+) - 技术博客(原创文章30篇+) - 公司内部技术分享主讲人 2. **全栈能力展示** - 使用Vue+ElementUI搭建管理后台 - 基于Docker实现自动化部署 - 编写API文档(Swagger+Markdown) 3. **性能优化案例** $$TPS = \frac{Concurrent\ Users \times (1 + Think\ Time)}{Response\ Time}$$ 通过线程池优化+慢SQL治理,将系统吞吐量从800 TPS提升至3500 TPS[^2]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值