Files
gloria/JIRA_INTEGRATION.md
Developer 5d0cafac32 feat: 交接班报告支持 Confluence/Jira 集成,添加 N/A 记录时间归属功能
- 集成 Confluence API 获取船舶报告数据
- 集成 Jira API 查询故障数量
- 支持船号显示 (462#、463# 等)
- 支持故障次数/故障率、人工介入次数/介入率显示
- 跨班作业使用 Card 69 按时间查询效率
- 不跨班作业使用整船效率(剔除异常)
- N/A 记录根据作业时间归属到对应船舶
- 更新 AGENTS.md 和 README.md 文档
- 删除 daily_report_gui.py
2026-03-14 02:52:23 +08:00

6.4 KiB
Raw Permalink Blame History

Jira 集成说明

概述

为了获取准确的故障次数,系统现在支持从 Jira 直接查询 issue 数量。这样可以避免 Confluence 页面中表格数据为空的问题。

问题背景

463# 信荣海 的例子:

  • Confluence 页面中"故障次数"表格字段是空的(<td><br /></td>
  • 但实际在 Jira 中有 2 个故障FZ-2042、FZ-2043
  • 需要从 Jira 查询获取准确的故障数量

新增文件

jira_client.py           # Jira API 客户端

环境变量配置

.env 文件中添加 Jira 配置:

# Jira 配置
JIRA_URL=https://jira.westwell-lab.com
JIRA_USERNAME=your_username
JIRA_TOKEN=your_api_token

使用方法

1. 修改 shift_report.py 启用 Jira 查询

shift_report.py 中,找到 ShiftReportGenerator.__init__ 方法,添加 Jira 客户端:

from jira_client import JiraClient

class ShiftReportGenerator:
    def __init__(self):
        self.feishu_manager = FeishuScheduleManager()
        
        # Confluence 配置
        try:
            self.confluence_manager = VesselReportManager()
            self.confluence_enabled = True
            
            # 设置 Jira 客户端(用于查询故障数量)
            jira_url = os.getenv("JIRA_URL")
            jira_username = os.getenv("JIRA_USERNAME")
            jira_token = os.getenv("JIRA_TOKEN")
            if jira_url and jira_username and jira_token:
                jira_client = JiraClient(jira_url, jira_username, jira_token)
                self.confluence_manager.set_jira_client(jira_client)
                print("Jira 客户端已启用")
        except Exception as e:
            print(f"Confluence 数据源初始化失败: {e}")
            self.confluence_enabled = False

2. 测试 Jira 查询

创建测试脚本 test_jira.py

import os
import sys
from dotenv import load_dotenv

load_dotenv()

project_root = os.path.dirname(os.path.abspath(__file__))
if project_root not in sys.path:
    sys.path.insert(0, project_root)

from jira_client import JiraClient
from confluence import VesselReportManager

# 初始化客户端
jira_client = JiraClient(
    os.getenv("JIRA_URL"),
    os.getenv("JIRA_USERNAME"),
    os.getenv("JIRA_TOKEN")
)

confluence_manager = VesselReportManager()
confluence_manager.set_jira_client(jira_client)

# 获取 463# 的详细数据
print("=== 测试 463# 信荣海的 Jira 查询 ===\n")

# 从 Confluence 获取页面 JQL
import requests
import re

token = os.getenv("CONFLUENCE_TOKEN")
headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
url = "https://confluence.westwell-lab.com/rest/api/content/165416156/child/page"
params = {"limit": 100, "expand": "body.storage"}

response = requests.get(url, headers=headers, params=params, timeout=30)
if response.status_code == 200:
    data = response.json()
    for page in data.get('results', []):
        if '463#' in page.get('title', ''):
            body = page.get('body', {}).get('storage', {}).get('value', '')
            jqls = confluence_manager._extract_jira_jqls(body, "463")
            
            print(f"找到 {len(jqls)} 个 Jira 宏:")
            total_issues = 0
            for i, jql in enumerate(jqls, 1):
                count = jira_client.count_issues(jql)
                total_issues += count
                print(f"\n{i}:")
                print(f"  JQL: {jql[:100]}...")
                print(f"  Issue 数量: {count}")
            
            print(f"\n总计故障数量: {total_issues}")
            
            # 获取具体的 issue keys
            all_issues = []
            for jql in jqls:
                issues = jira_client.get_issue_keys(jql)
                all_issues.extend(issues)
            
            print(f"\nIssue Keys: {all_issues}")
            break

工作原理

1. 提取 JQL 查询

从 Confluence 页面中提取所有 Jira 宏的 JQL 查询:

def _extract_jira_jqls(self, body: str, vessel_number: str) -> List[str]:
    # 查找所有 Jira 宏
    jira_macros = re.findall(
        r'<ac:structured-macro[^>]*ac:name="jira"[^>]*>(.*?)</ac:structured-macro>',
        body, re.DOTALL
    )
    
    for macro in jira_macros:
        # 提取 JQL
        jql_match = re.search(
            r'<ac:parameter[^>]*name="jqlQuery"[^>]*>(.*?)</ac:parameter>',
            macro, re.DOTALL
        )
        if jql_match:
            jql = jql_match.group(1)
            # 检查是否匹配当前船次
            if f'实船船次 = "{vessel_number}"' in jql:
                jqls.append(jql)

2. 查询 Jira

对每个 JQL 查询执行 Jira API 调用:

def count_issues(self, jql: str) -> int:
    url = f"{self.base_url}/rest/api/2/search"
    params = {
        "jql": jql,
        "maxResults": 0  # 只返回总数
    }
    response = requests.get(url, auth=self.auth, params=params)
    return response.json().get("total", 0)

3. 故障计数逻辑

  1. 首先尝试从 Confluence 表格中提取故障次数
  2. 如果表格中没有数据(为 0则从 Jira 查询
  3. 汇总所有匹配的 Jira 宏的 issue 数量

注意事项

1. Jira API Token

需要在 Jira 中生成 API Token

  1. 登录 Jira
  2. 点击头像 -> 账户设置
  3. 安全 -> 创建和管理 API 令牌
  4. 创建新令牌并保存

2. 查询性能

每个船舶报告可能有 3-4 个 Jira 宏,每个宏执行一次 Jira 查询。对于包含多个船舶的报告,可能需要几秒钟完成所有查询。

3. 权限要求

Jira 账户需要有权限访问项目 FZ 的 issues。

故障排查

Jira 查询返回 0

检查:

  1. Jira 认证信息是否正确
  2. 账户是否有权限访问 FZ 项目
  3. JQL 查询是否正确(可以在 Jira 中手动测试)

Confluence 页面没有 Jira 宏

有些船舶页面可能没有创建 Jira 宏,这时会显示表格中的数据(可能为 0 或空)。

示例输出

启用 Jira 查询后的输出示例:

实船作业463# 信荣海
上场车辆数6
作业量/效率28TEU20尺*20 40尺*40.00循环/车/小时
故障次数/故障率2次14.29%  <-- 从 Jira 查询获得
人工介入次数/介入率0次0.00%

如果没有启用 Jira

实船作业463# 信荣海
上场车辆数6
作业量/效率28TEU20尺*20 40尺*40.00循环/车/小时
故障次数/故障率0次0.00%  <-- 从 Confluence 表格获得(为空)
人工介入次数/介入率0次0.00%