【无标题】jenkins消息模板(飞书)

news2024/11/19 4:37:58

这里写目录标题

    • Jenkins 安装的插件
  • 发送消息到飞书
    • 预览 1 (单Job)
    • 预览 2 (多Job,概览)

Jenkins 安装的插件

插件名称作用
Rebuilder
  1. Rebuilder。
    官方地址:https://plugins.jenkins.io/rebuild
    安装方式:在Jenkins插件当中直接搜索即可安装。
    功能说明:此插件可以直接重复上次构建,也可以用于查看一些参数比较复杂的构建时,上次构建所选的参数是什么。非常nice的一个插件。

  2. AnsiColor。
    官方地址:https://plugins.jenkins.io/ansicolor
    安装方式:在Jenkins插件当中直接搜索即可安装。
    功能说明:扩展支持我们在shell当中定义的echo -e指令,从而给一定的输出上颜色。
    使用方式:点此跳转到使用介绍。(opens new window)

  3. Maven Release Plug-in。
    maven风格插件。
    安装方式:在Jenkins插件当中直接搜索即可安装。

  4. user build vars。
    官方地址:https://wiki.jenkins.io/display/JENKINS/Build+User+Vars+Plugin
    安装方式:在Jenkins插件当中直接搜索即可安装。
    功能说明:通过此插件,让整个Jenkins系统中的用户参数成为一个可调用的变量。
    使用方式:在构建环境中选中Set Jenkins user build variables。

  5. Post build task
    功能说明:此功能允许您关联 shell 或批处理脚本,这些脚本根据构建日志输出在 Hudson
    上执行某些任务。如果日志文本与构建日志文件中的某处匹配,脚本将执行。例如:如果在日志文本中指定了“IOException”,并且构建日志文件包含字符串“IOException”,则脚本将执行。
    允许使用 Java 正则表达式,并且可以将组用作脚本参数。如果文本是“Last Build : #(\d+)”并且脚本是“script.sh”,那么如果日志包含一行“Last
    Build : #4”,脚本“script.sh 4”将被调用.

  6. MultiJob Phase
    功能说明:上下游执行

发送消息到飞书

预览 1 (单Job)

在这里插入图片描述

  • 对应shell
#!/usr/bin/env bash
url1="https://open.feishu.cn/open-apis/bot/v2/hook/" 
url2="https://open.feishu.cn/open-apis/bot/v2/hook/" 

#  1. 消息 接收地址
webhook_list=($url1 $url2)

# ========================
#  2. 消息 参数预处理
if [ ! "$GIT_BRANCH" ]; then
  echo "----------------- GIT_BRANCH 为空"
  GIT_BRANCH="''"
fi
echo -e "GIT_BRANCH -> ${GIT_BRANCH}"
# ========================
#  3. 消息 执行发送
run_send_msg() {
  echo -e "\n 复制发送消息脚本 $HOME -> ${WORKSPACE}" && cp "$HOME"/send_msg_to_feishu.py "${WORKSPACE}"
  for ((i = 0; i < ${#webhook_list[@]}; i++)); do
    webhook=${webhook_list[$i]}
    echo -e "发送中 --> $webhook"
    python3 send_msg_to_feishu.py "${webhook}" -job_name "${JOB_NAME}" -job_url "${JOB_URL}" -build_name "${BUILD_ID}" -branch "${GIT_BRANCH}"
  done
  echo -e "发送完成 \n\n"
}

run_send_msg

  • 对应python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import json
import time

import requests

parser = argparse.ArgumentParser(description='Jenkins 发送消息到飞书',
                                 epilog="执行示例>>> python ${webhook} -job_name ${JOB_NAME} -job_url ${JOB_URL} -branch ${GIT_BRANCH} -build_name ${BUILD_NUMBER}  ")
parser.add_argument('webhook', help='机器人webhookURL')  # 必填
parser.add_argument('-job_name', '--JOB_NAME', help='作业Name', )  # 选填
parser.add_argument('-job_url', '--JOB_URL', help='作业URL', required=True, )  # 必填
parser.add_argument('-branch', '--GIT_BRANCH', help='git分支', default='')  # 选填
parser.add_argument('-build_name', '--BUILD_DISPLAY_NAME', help='编译Name')  # 选填
# parser.add_argument('-build_url', '--BUILD_URL', help='编译URL', required=True, )  # 必填

webhook = parser.parse_args().webhook
JOB_NAME = parser.parse_args().JOB_NAME
JOB_URL = parser.parse_args().JOB_URL
GIT_BRANCH = parser.parse_args().GIT_BRANCH
BUILD_DISPLAY_NAME = parser.parse_args().BUILD_DISPLAY_NAME
# BUILD_URL = parser.parse_args().BUILD_URL

BUILD_URL = JOB_URL + '/lastBuild'


def set_msg():
    # ============ 数据获取 ============
    result = requests.get(f'{BUILD_URL}/api/json', auth=jenkins_auth).json()
    # print(BUILD_STATUS.request.url)
    try:
        shortDescription = result['actions'][0]['causes'][0]['shortDescription']  # 启动者
    except KeyError:  # 安装 `Multijob plugin` 后位置变更
        shortDescription = result['actions'][1]['causes'][0]['shortDescription']  # 启动者

    print(result['timestamp'])
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(result['timestamp'] / 1000))  # 编译开始时间
    print(timestamp)
    BUILD_STATUS = result['result']  # 编译状态
    print('build_status --> ', BUILD_STATUS)
    duration = result['duration']  # 编译持续时间
    build_duration = int(duration) // 1000  # 毫秒到秒
    minutes, seconds = divmod(build_duration, 60)
    hours, minutes = divmod(minutes, 60)
    print(f'build duration --> {hours}H:{minutes}M:{seconds}S')
    # ============ 设置样式 ============
    if "SUCCESS" in BUILD_STATUS:  # 成功
        template_color = "green"
    elif "FAILURE" in BUILD_STATUS:  # 失败
        template_color = "red"
    elif "ABORTED" in BUILD_STATUS:  # 终止
        template_color = "yellow"
    else:
        template_color = "grey"
    # ============ 卡片模板 ============
    card = json.dumps({
        "config": {
            "wide_screen_mode": True
        },
        "elements": [
            {
                "tag": "markdown",
                "content": f"触发时间:{timestamp}\n"
                           f"分支名称:{GIT_BRANCH}\n"
                           f"构建编号:{BUILD_DISPLAY_NAME}\n"
                           f"构建状态:<font color={template_color}>{BUILD_STATUS}</font>\n"
            },
            {
                "tag": "note",
                "elements": [
                    {
                        "tag": "img",
                        "img_key": f"{img_icon}",
                        "alt": {
                            "tag": "plain_text",
                            "content": f"{JOB_URL}"
                        }
                    },
                    {
                        "tag": "plain_text",
                        "content": f"{shortDescription}"
                    }
                ]
            },
            {
                "tag": "hr"
            },
            {
                "tag": "action",
                "actions": [
                    {
                        "tag": "button",
                        "text": {
                            "tag": "plain_text",
                            "content": "报告链接"
                        },
                        "type": "primary",
                        "multi_url": {
                            "url": f"{BUILD_URL}/allure",
                            "pc_url": "",
                            "android_url": "",
                            "ios_url": ""
                        }
                    }
                ],
                "layout": "bisected"
            }
        ],
        "header": {
            "template": f"{template_color}",
            "title": {
                "content": f"作业名称: {JOB_NAME}",
                "tag": "plain_text"
            }
        }
    })

    body = json.dumps({"msg_type": "interactive", "card": card})
    headers = {"Content-Type": "application/json"}

    res = requests.post(url=webhook, data=body, headers=headers)

    print(res.text)


if __name__ == '__main__':
    img_icon = 'img_v2_098e80ae-e583-4148-b822-xxxxxx'  # img_key_id jenkinsIcon
    jenkins_auth = ('report', 'report')  # jenkins User:Pwd
    set_msg()

预览 2 (多Job,概览)

Jenkins 需安装Multijob插件
Multijob https://plugins.jenkins.io/jenkins-multijob-plugin/

在这里插入图片描述

  • 对应shell
#!/usr/bin/env bash

echo -e "\n\n 消息处理"
# ========================
#  消息发送
# ========================

#  1. 消息 接收地址
# -----------------------
group='https://open.feishu.cn/open-apis/bot/v2/hook/'

webhook_list=($group)
py_send='SendMsgFeishu.py'

# ========================
#  2. 文件处理
# -----------------------
echo -e "\n 复制发送消息脚本 $HOME -> ${WORKSPACE}" && cp "$HOME"/$py_send "${WORKSPACE}"

# ========================
#  3. 消息 执行发送
# -----------------------
run_send_msg() {
  for ((i = 0; i < ${#webhook_list[@]}; i++)); do
    webhook=${webhook_list[$i]}
    echo -e "发送中 --> $webhook"
    python3 $py_send "${webhook}" -job_url "${JOB_URL}"
  done
  echo -e "发送完成 \n\n"
}

run_send_msg
  • 对应python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import argparse
import datetime
import json
import subprocess
import time
from json import JSONDecodeError

import requests

parser = argparse.ArgumentParser(description='Jenkins 发送消息到飞书',
                                 epilog="执行示例>>> python ${webhook} -job_url ${JOB_URL}")
parser.add_argument('webhook', help='机器人webhookURL')  # 必填
parser.add_argument('-job_url', '--JOB_URL', help='作业URL', required=True, )  # 必填

webhook = parser.parse_args().webhook
JOB_URL = parser.parse_args().JOB_URL
BUILD_URL = JOB_URL + '/lastBuild'

job_name = []  # 运行名称
job_duration = []  # 运行时长
job_status = []  # 运行状态
job_url = []  # 运行结果
pass_rate = []  # 百分比显示

print('修改时间:2023-07-04 10:02:43')


def get_base_info():
    device_id = subprocess.getoutput('cat /etc/ding_issue')
    version_os = subprocess.getoutput('cat /etc/issue')
    version_browser = subprocess.getoutput('dingdao-dingtao-stable -version')
    device_sn = subprocess.getoutput('hostname').split('-')[-1]
    # print(deviceid,os_version,browser_version, sn)
    return device_id, version_os, version_browser, device_sn


def sending_alarms(text=None):
    local_network_info = subprocess.getoutput("networkctl status")
    if text is None:
        text = f"网络异常: 无法访问\n{BUILD_URL}\n{local_network_info}"
    payload_message = {"msg_type": "text", "content": {"text": text}}
    headers = {"Content-Type": "application/json"}
    res = requests.post(url=webhook, data=json.dumps(payload_message), headers=headers)
    print(f"告警信息发送状态:{res.text}")


def set_msg():
    get_result = requests.get(f'{BUILD_URL}/api/json', auth=jenkins_auth)
    # ------------------
    # ------ 数据获取 ------
    # ------------------
    JENKINS_URL = BUILD_URL.split('job')[0]  # JENKINS_URL
    # ------ begin 登陆异常 ------
    try:
        print(f"查询地址:{get_result.url}")
        result = get_result.json()
    # except JSONDecodeError:  # json解析失败 未登陆
    #     text = "Error 401 Unauthorized"
    #     sending_alarms(text)
    #     quit(text)
    # except RecursionError:  # BUG: https://github.com/jenkinsci/tikal-multijob-plugin/issues/255
    #     quit('递归错误:从 unicode 字符串解码 JSON 对象时超出最大递归深度')
    except OSError:  # 异常列表: 网络
        text = "No route to host"
        sending_alarms()
        quit(text)
    except Exception as e:  # 通用异常处理
        text = f"发生异常: {type(e).__name__} --> {str(e)}\n检查:{get_result.url}"
        sending_alarms(text)
        quit(text)
    # res = requests.get(f'{BUILD_URL}/api/json', auth=jenkins_auth)
    # if res.status_code == 401:
    #     quit('Error 401 Unauthorized')
    # else:
    #     result = res.json()
    # # ------ end 登陆异常 ------
    shortDescription = result['actions'][0]['causes'][0]['shortDescription']  # 启动者
    timestamp = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(result['timestamp'] / 1000))  # 编译开始时间
    # ------ begin 获取持续时间 ------
    if result['duration'] != 0:  # 获取字段
        duration = int(result['duration']) // 1000  # 编译持续时间
        minutes, seconds = divmod(duration, 60)
        hours, minutes = divmod(minutes, 60)
        build_duration = f'{hours}h {minutes}m {seconds}s'
        print(f"--> 通过响应值换算{build_duration}\n")
    else:  # 通过当前时间计算
        build_duration = datetime.datetime.now() - datetime.datetime.fromtimestamp(result['timestamp'] / 1000)
        build_duration = str(build_duration).split('.')[0]
        print(f"--> 通过当前时间计算耗时{build_duration}\n")

    # ------ end 获取持续时间 ------
    total_count = len(result['subBuilds'])  # 数量总计
    print(f'======= 项目概览 ======= \n'
          f'shortDescription:{shortDescription}\nbuild duration:{build_duration}\ntotal_count:{total_count}\ntimestamp:{timestamp}\n')

    print('提示: 没有allure就报错 无法运行 JSONDecodeError')

    for index in result['subBuilds']:  # 提取数据
        # print(index)
        # print(index['result'])
        # 数据预处理
        allure_summary = requests.get(f"{JENKINS_URL}{index['url']}/allure/widgets/summary.json").json()['statistic']
        allure_history_trend = requests.get(f"{JENKINS_URL}{index['url']}/allure/widgets/history-trend.json")
        # print(allure_history_trend.request.url)

        try:  # 获取历史数据
            allure_history_trend = allure_history_trend.json()[1]
        except IndexError:
            print('没有历史数据')
            allure_history_trend = allure_history_trend.json()[0]

        # 计算百分比
        if allure_summary['total'] != 0:  # 除数不能为0
            allure_pass_rate = allure_summary['passed'] / allure_summary['total']
        else:
            allure_pass_rate = 0  # 除数不能为0
        if allure_history_trend['data']['total'] != 0:
            allure_history = allure_history_trend['data']['passed'] / allure_history_trend['data']['total']
        else:
            allure_history = 0

        # ------------------
        # ------ 设置样式 ------
        # ------------------
        if "SUCCESS" == index['result']:  # 成功
            color = "green"
        elif "FAILURE" == index['result']:  # 失败
            color = "red"
        elif "ABORTED" == index['result']:  # 中止
            color = "yellow"
        else:  # 其他
            color = "grey"
        # 通过率对比
        allure_change = allure_pass_rate - allure_history
        print(f"{index['jobName']} --> 本次比上次通过率 {allure_change}")
        if allure_pass_rate > allure_history:
            allure_pass_rate = f'<font color=green>↑{allure_pass_rate:.2%}</font>'
        elif allure_pass_rate < allure_history or allure_pass_rate == 0:
            allure_pass_rate = f'<font color=red>↓{allure_pass_rate:.2%}</font>'
        else:
            allure_pass_rate = f' {allure_pass_rate:.2%}'

        # ------------------
        # ------ 载入数据 ------
        # ------------------
        job_name.append({"tag": "markdown", "content": f"{index['jobName']}", "text_align": "center"})
        job_duration.append({"tag": "markdown", "content": f"{index['duration']}", "text_align": "center"})
        job_status.append(
            {"tag": "markdown", "content": f"<font color={color}>{index['result']}</font>", "text_align": "center"})
        job_url.append(
            {"tag": "markdown", "content": f"[查看]({JENKINS_URL}{index['url']}/allure)", "text_align": "center"})
        pass_rate.append(
            {"tag": "markdown",
             "content": f"{allure_summary['passed']}/{allure_summary['total']}{allure_pass_rate} ",
             "text_align": "center"})

    print(f'======= 项目详情 ======= \n{job_name}\n{job_duration}\n{job_status}\n{pass_rate}\n{job_url}\n')


def set_msg_a(total_count=None, build_duration=None, timestamp=None, shortDescription=None):
    """
    # ------------------
    # ------ 卡片模板 ------
    # ------------------
    total_count: 总数量
    build_duration: 持续时间
    timestamp: 开始时间
    shortDescription: 启动者
    """
    card = json.dumps({
        "elements": [
            {
                "tag": "markdown",
                "content": "**项目总览**\n"
            },
            {
                "tag": "column_set",
                "flex_mode": "bisect",
                "background_style": "grey",
                "horizontal_spacing": "default",
                "columns": [
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "elements": [
                            {
                                "tag": "markdown",
                                "text_align": "center",
                                "content": f"项目数量\n**{total_count}**\n"
                            }
                        ]
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "elements": [
                            {
                                "tag": "markdown",
                                "text_align": "center",
                                "content": f"运行耗时\n**{build_duration}**"
                            }
                        ]
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "elements": [
                            {
                                "tag": "markdown",
                                "text_align": "center",
                                "content": f"执行时间\n**{timestamp}**\n"
                            }
                        ]
                    }
                ]
            },
            {
                "tag": "markdown",
                "content": "**项目信息**"
            },
            {
                "tag": "column_set",
                "flex_mode": "none",
                "background_style": "grey",
                "columns": [
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 2,
                        "vertical_align": "top",
                        "elements": [
                            {
                                "tag": "markdown",
                                "content": "**项目名**",
                                "text_align": "center"
                            }
                        ]
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": [
                            {
                                "tag": "markdown",
                                "content": "**运行时长**",
                                "text_align": "center"
                            }
                        ]
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": [
                            {
                                "tag": "markdown",
                                "content": "**运行状态**",
                                "text_align": "center"
                            }
                        ]
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": [
                            {
                                "tag": "markdown",
                                "content": "**passed/total/通过率**",
                                "text_align": "center"
                            }
                        ]
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": [
                            {
                                "tag": "markdown",
                                "content": "**Report**",
                                "text_align": "center"
                            }
                        ]
                    }
                ]
            },
            {
                "tag": "column_set",
                "flex_mode": "none",
                "background_style": "default",
                "columns": [
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 2,
                        "vertical_align": "top",
                        "elements": job_name
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "center",
                        "elements": job_duration
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": job_status
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": pass_rate
                    },
                    {
                        "tag": "column",
                        "width": "weighted",
                        "weight": 1,
                        "vertical_align": "top",
                        "elements": job_url
                    }
                ]
            },
            {
                "tag": "hr"
            },
            {
                "tag": "note",
                "elements": [
                    {
                        "tag": "img",
                        "img_key": f"{img_icon}",
                        "alt": {
                            "tag": "plain_text",
                            "content": ""
                        }
                    },
                    {
                        "tag": "plain_text",
                        "content": f"{shortDescription}\n颜色代表对比上次执行,绿色上升,红色下降"
                    }
                ]
            }
        ]
    })

    card_id = json.dumps({
        "type": "template",
        "data": {
            "template_id": "ctp_AA6DZMfkJekh",  # 卡片id,参数必填。可在搭建工具中通过“复制卡片ID”获取
            "template_variable":  # 卡片中绑定的变量的取值。如没有绑定变量,可不填此字段。

                {
                    "total_count": "29",
                    "group_table": [
                        {
                            "jobName": "test001",
                            "duration": "小于1小时",
                            "build_url": "baidu.com",
                            "build_status": "SUCCESS",
                            "tmp_color": "green"
                        },
                        {
                            "jobName": "test002",
                            "duration": "2小时",
                            "build_url": "baidu.com",
                            "build_status": "FAILURE",
                            "tmp_color": "red"
                        },
                        {
                            "jobName": "test003",
                            "duration": "3小时",
                            "build_url": "baidu.com",
                            "build_status": "ABORTED",
                            "tmp_color": "yellow"
                        },
                        {
                            "jobName": "test004",
                            "duration": "3小时",
                            "build_url": "baidu.com",
                            "build_status": "UNSTABLE",
                            "tmp_color": "grey"
                        }
                    ],
                    "duration": "15080",
                    "shortDescription": "Started by user admin",
                    "timestamp": "1686645721264",
                    "jobName": "",
                    "tmp_color": ""
                }
        }
    })
    body = json.dumps({"msg_type": "interactive", "card": card})  # 使用 当前模板
    # body = json.dumps({"msg_type": "interactive", "card": card_id})  # 使用 预置模板
    headers = {"Content-Type": "application/json"}

    res = requests.post(url=webhook, data=body, headers=headers)
    print(f'消息发送响应 -->\n\t {res.text}')


if __name__ == '__main__':
    img_icon = 'img_v2_098e80ae-e583-4148-b822-f42a05298d3g'  # img_key_id jenkinsIcon
    jenkins_auth = ('result', 'result')  # jenkins User:Pwd
    # get_base_info()
    set_msg()
    deviceid, os_version, browser_version, sn = get_base_info()

    print(deviceid, os_version, browser_version, sn)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/928064.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件研发CI/CD流水线图解

当谈到现代软件开发流程时&#xff0c;持续集成&#xff08;Continuous Integration&#xff0c;简称CI&#xff09;和持续交付&#xff08;Continuous Delivery&#xff0c;简称CD&#xff09;是两个关键的实践。它们旨在加速开发流程、提高软件质量&#xff0c;并使软件发布更…

java八股文面试[JVM]——元空间

JAVA8为什么要增加元空间 为什么要移除永久代&#xff1f; 知识来源&#xff1a; 【2023年面试】JVM8为什么要增加元空间_哔哩哔哩_bilibili

shopee开店后店铺怎么运营?这几点运营技巧要学会!

Shopee卖家开店后&#xff0c;这几点运营技巧要学会&#xff01; 1.Listing上架 产品在上架前&#xff0c;卖家要为主打产品需要精细化打造好文案和图片后再上传&#xff0c;尽量不要随意上架或者等到上架后再优化&#xff0c;避免浪费了新品最佳流量时期。 卖家可以从标题、…

【基于Arduino的仿生蚂蚁机器人】

【基于Arduino的仿生蚂蚁机器人】 1. 概述2. Arduino六足位移台–蚂蚁机器人3D模型3. 3D 打印零件4. 组装Arduino六足位移台5. Arduino蚂蚁机器人电路图6. 为Arduino Hexapod设计PCB7. 组装电路板8. 系统代码9. Arduino蚂蚁机器人安卓应用程序在本教程中,我将向您展示如何构建…

Total Uninstall | Windows 专业卸载清理工具

前言&#xff1a;Total Uninstall PC 专业卸载清理工具&#xff1a;彻底清除软件痕迹的神器 在日常使用电脑的过程中&#xff0c;我们常常需要安装和卸载各种软件。然而&#xff0c;传统的卸载方法往往并不能彻底清除软件的痕迹&#xff0c;使得电脑逐渐变慢或者出现各种问题。…

Idea常用快捷键--让你代码效率提升一倍(一)

一、代码编辑相关快捷键 1.单行复制(实现快速创建多个对象)CtrlD 2.空出下一行 ShiftEnter 3.单行注释快捷键 ctrl / 4.快速构建构造函数&#xff0c;setter&#xff0c;getter、toString方法 AltInsert 4.显示快速修复和操作的菜单 altenter 5.格式化代码&#xff1a;C…

leetcode 767. Reorganize String(重组字符串)

重新排列字符串s中的字母&#xff0c;使得任意两个相邻的字母都不相同。 思路&#xff1a; 让相邻字母不同&#xff0c;能想到的办法是先把相同的字母排列&#xff0c; 然后在相同字母的缝隙中插入另一种字母。 比如"aab", 先把"a a"排出来&#xff0c;再…

Python“牵手”拼多多商品列表数据,关键词搜索拼多多API接口数据,拼多多API接口申请指南

拼多多平台API接口是为开发电商类应用程序而设计的一套完整的、跨浏览器、跨平台的接口规范&#xff0c;拼多多API接口是指通过编程的方式&#xff0c;让开发者能够通过HTTP协议直接访问拼多多平台的数据&#xff0c;包括商品信息、店铺信息、物流信息等&#xff0c;从而实现拼…

ssm中小型企业财务管理系统源码和论文

ssm中小型企业财务管理系统源码和论文067 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 1、研究目的意义 社会经济的迅速发展和科学技术的全面进步&#xff0c;计算机技术的飞速发展&#xff0c;以及计算机与…

开启网络安全量化时代丨Fortinet AI 驱动型安全运营方案让价值看得见

网络安全投资如何才能体现价值&#xff0c;一直都是困扰企业安全负责人的头等大事。这关系到企业何时进行网络安全产品的更新换代&#xff0c;又该如何判断是否应新增部署工具、人员或流程&#xff0c;以及已有投资是否值得……诸多关键问题。Fortinet与知名研究机构Enterprise…

python替换—Series.replace()与Series.str.replace()的区别及为何replace()无效的解决方法

文章目录 前言一、Series.replace()方法二、Series.str.replace()方法三、replace()与str.replace() 使用方法的区别四、常见的坑&#xff1a;python中replace方法不起作用 前言 在Pandas中&#xff0c;Series是一个由一维数组表示的DataFrame列&#xff0c;而replace和str.re…

借助流程引擎表单,一举创造高效率办公流程化管理!

办公流程化管理&#xff0c;是很多企业的追求目标和发展愿景。如何实现这一目标&#xff1f;如何让企业内部的数据更好地实现价值&#xff0c;创造辉煌前景&#xff1f;借助流程引擎表单&#xff0c;可以助力企业实现高效率办公&#xff0c;让每一个表单制作场景更为顺畅和高效…

Redis 重写 AOF 日志期间,主进程可以正常处理命令吗?

重写 AOF 日志的过程是怎样的&#xff1f; Redis 的重写 AOF 过程是由后台子进程 bgrewriteaof 来完成的&#xff0c;这么做有以下两个好处。 子进程进行 AOF 重写期间&#xff0c;主进程可以继续处理命令请求&#xff0c;从而避免阻塞主进程子进程带有主进程的数据副本。这里…

Verilog 实现状态机自动售卖机

Verilog 实现状态机自动售卖机 教学视频&#xff1a;https://www.bilibili.com/video/BV1Ve411x75W?p33&spm_id_frompageDriver&vd_source19ae31dff4056e52d2729a4ca212602b 功能需求 使用1元、2元、5元面值的纸币进行支付&#xff0c;获取6元的物品&#xff0c;不设…

天津大数据培训机构 全面分析大数据怎么样?

数字化和信息化时代的发展&#xff0c;推动了大数据的高速发展&#xff0c;在这个大数据时代的浪潮中&#xff0c;越来越多人加入到大数据培训&#xff0c;都希望在大数据培训机构中学习最前沿的知识。 大数据人才行业需求 大数据行业近年来仍然呈现出增长态势&#xff0c;并…

Linux centos7 bash编程小训练

训练要求&#xff1a; 求比一个数小的最大回文数 知识点&#xff1a; 一个数字正读反读都一样&#xff0c;我们称为回文数&#xff0c;如5、11、55、121、222等。 我们训练用bash编写一个小程序&#xff0c;由我们标准输入一个整数&#xff0c;计算机将显示出一个比这个数小…

相对位置关系对排斥能的影响

( A, B )---3*30*2---( 1, 0 )( 0, 1 ) 让网络的输入只有3个节点&#xff0c;AB训练集各由5张二值化的图片组成&#xff0c;让A中有2个1&#xff0c;B中有1个1&#xff0c;且不重合&#xff0c;排列组合&#xff0c;统计迭代次数并排序。 其中有5组数据 构造平均列A 构造平均…

SPI总线协议

简述 SPI协议是一种芯片与芯片之间的通讯&#xff0c;全称是Serial Peripheral Interface SPI通讯采用一主多从模式&#xff0c;产生时钟的一侧称为主机&#xff0c;另一侧称为从机。只有一个主机&#xff08;一般来说可以是微控制器/MCU&#xff09;&#xff0c;但是可以有一…

不要“妖魔化”外包,看完我悟了......

在IT圈子里&#xff0c;经常能看到一个很有意思的现象&#xff0c;对于外包工作&#xff0c;不同看法的网友们常常吵的不可开交。 新人程序员试图从“前辈”的经验中找出答案&#xff0c;以作为自己要不要去外包公司的参考&#xff0c;但往往刷帖无数&#xff0c;却依旧举棋不…

element 下拉组件获取对象

// 选择数据user:[{name:"小白",id:1,money:"100",love:"蛋糕"},{name:"小黑",id:2,money:"200",love:"奶茶"},{name:"小红",id:3,money:"300",love:"烧烤"},] <div><el…