零碎知识 一键创建 Confluence Matirx 目录层级,不需要手动执行

大海 · June 01, 2025 · 977 hits

需求背景

公司要求各个项目组内,需要使用统一的 Confluence Matrix 目录构建,由于项目太多,手动复制各个层级的目录到自己的项目下是不现实的,最好是需要使用接口实现。因此我自己写了一个脚本,可以一键创建 Confluence Matirx 目录层级,避免了大家手动执行迁移操作。

自己写的代码实现

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
@Author  : XXXXXX
@Contact : XXXXXX
@File    : ConfluenceMigrationPageTool.py
@Create Time:  2025/5/30 18:01
@Description: 一键创建Confluence Matirx目录层级,不需要手动执行
"""

from atlassian import Confluence

# 配置信息
CONFLUENCE_URL = 'https://kone.atlassian.net/wiki'
SOURCE_SPACE_KEY = 'CTF'
DESTINATION_SPACE_KEY = 'GAPP'  # 目标空间键更改为 GAPP
USERNAME = 'XXXXXX'  # 替换为你的个人邮箱
API_TOKEN = 'XXXXXX  # 替换为你的个人API令牌

# 创建 Confluence 实例
confluence = Confluence(
    url=CONFLUENCE_URL,
    username=USERNAME,
    password=API_TOKEN
)

def escape_cql_query(query):
    """Escape special characters in CQL query."""
    return query.replace('&', '&').replace('+', '%2B')

def get_page_id_by_title(space_key, title, parent_id=None):
    cql_query = f"space={space_key} and title='{escape_cql_query(title)}'"
    if parent_id:
        cql_query += f" and ancestor={parent_id}"
    try:
        results = confluence.cql(cql_query)
        if results.get('results'):
            return results['results'][0]['content']['id']
    except Exception as e:
        print(f"Failed to execute CQL query '{cql_query}': {e}")
    return None

def create_or_get_page(title, space_key, parent_id=None):
    page_id = get_page_id_by_title(space_key, title, parent_id)
    if page_id:
        print(f"Page '{title}' already exists with ID: {page_id}")
        return page_id
    else:
        page_body = '<p>This is a placeholder page.</p>'
        try:
            new_page = confluence.create_page(
                space=space_key,
                title=title,
                body=page_body,
                parent_id=parent_id
            )
            print(f"Created page: {new_page['title']} with ID: {new_page['id']}")
            return new_page['id']
        except Exception as e:
            print(f"Failed to create page '{title}': {e}")
            return None

def process_pages(pages, space_key, parent_id):
    for page_dict in pages:
        for page, child_pages in page_dict.items():
            page_title = page.replace('+', ' ')
            page_id = create_or_get_page(page_title, space_key, parent_id)
            if page_id is not None:
                process_pages(child_pages, space_key, page_id)

def main():

    # 如果你正在迁移到一个全新的 Confluence 空间,并且该空间目前没有任何页面,那么 destination_parent_id 就不再需要了。
    # 在这种情况下,你可以直接在根目录下创建新的页面。
    # # 目标父页面ID(GOV+API)
    # destination_parent_id = 95851789

    # 源目录结构
    source_structure = {
        "00.+Product+Overview": [
            {"Introduction": []},
            {"Product+Roadmap": [
                {"Overall": []},
                {"Quarterly+planning": []}
            ]},
            {"Project+key+member+responsibility": []}
        ],
        "01.+Requirement": [
            {"Business+Requirements": [
                {"Policy&Code": []},
                {"Marketing+Analyzation": []},
                {"Competitors+investigation": []},
                {"Business+case": []}
            ]},
            {"Functional+Requirements": [
                {"Module-XXX": [
                    {"Features-XXX+PRD": [
                        {"Business+Background": []},
                        {"Role+Authoritarian": []},
                        {"Requirement+description": []},
                        {"Business+Workflow": []},
                        {"Prototype+design": []},
                        {"Page+elements+definition": []},
                        {"Log": []},
                        {"Requirement+Review+Meeting+Summary": []}
                    ]}
                ]}
            ]},
            {"Non-Functional+Requirements": [
                {"Role+Setting": []},
                {"Product+Performance": []}
            ]},
            {"Business+value+Review": []}
        ],
        "02.+Engineering": [
            {"01.+Architecture": [
                {"Tech-arch": []},
                {"Business-arch": []},
                {"Data-arch": []},
                {"Feature-xxx": []}
            ]},
            {"02.+Development": [
                {"Frontend-App": []},
                {"Frontend-Web": []},
                {"Frontend-Mini": []},
                {"domain+name": [
                    {"domain+arch": [
                        {"features-xxx": []}
                    ]},
                    {"app+name": [
                        {"app-name-api": []},
                        {"design+for+key+feature+1": []}
                    ]}
                ]}
            ]},
            {"03.+Data+Intelligence": []},
            {"04.+Validation+Quality": [
                {"Test+Specifications": [
                    {"Test+ENV": []},
                    {"Test+Strategy": []},
                    {"Test+Spec+Documents": []}
                ]},
                {"Test+Cases": []},
                {"Test+Reports": []},
                {"Automation": [
                    {"Automation+Strategy": []},
                    {"Automation+Test+Result": []},
                    {"Automation+Coverage+Track": []}
                ]},
                {"Non-Function+Test": [
                    {"Performance+Test": []},
                    {"Stability+Test": []},
                    {"Compatibility+test": []},
                    {"Usability+Test": []}
                ]},
                {"PRD+Leaking+Bug+Retro": []}
            ]},
            {"05.+Data+Services+Products": [
                {"KCDP": [
                    {"PoC": []},
                    {"Common+Services": []},
                    {"Data+Engineering": []}
                ]},
                {"Digital+enabled+services+247+services": [
                    {"Device+view": []},
                    {"Device+Shadow": []},
                    {"Dynamic+Scheduling": []},
                    {"ISN+CN": []}
                ]}
            ]}
        ],
        "03.+Application+Security": [
            {"Security+Summary": []},
            {"Secure+Design": [
                {"Security+protocol": []},
                {"Common+Reference+design": []}
            ]},
            {"Security+Requirements": []},
            {"Security+guideline": []},
            {"Security+Certificate": [
                {"MLPS+certificate": []},
                {"IEC-62443+certificate": []},
                {"ISO27001+series": []}
            ]},
            {"Security+Manual": []},
            {"Security+Testing": [
                {"Security+requirements+verification": []},
                {"Hot+findings+mitigation+summary": []},
                {"Pen+testing+Summary": []}
            ]}
        ],
        "04.+Releases": [
            {"Release+Calendar": [
                {"2025": []},
                {"2026": []}
            ]},
            {"Release+Version": [
                {"v-x.y.z": [
                    {"v-x.y.z-git-env-map": []},
                    {"v-x.y.z-human-resource": []},
                    {"v-x.y.z-runbook": [
                        {"v-x.y.z-runbook-result": []}
                    ]},
                    {"v-x.y.z-dev-to-test": [
                        {"feature-xxxxxx": []}
                    ]},
                    {"v-x.y.z-test-report": []},
                    {"v-x.y.z-security-report": []},
                    {"v-x.y.z-deploy-approve": []}
                ]},
                {"v-x.y.z.w": []}
            ]}
        ],
        "05.+Deployment+Operations": [
            {"Deployment+Guide": []},
            {"CI+CD+Pipeline": []},
            {"Monitoring": []},
            {"Incident+Management": []},
            {"User+Manual+FAQ": []}
        ],
        "06.+Knowledge": [],
        "07.+Project+Management": [
            {"Process": []},
            {"Team+Contacts": []},
            {"Team+Availability": []},
            {"Team+Member+privilege": [
                {"system-xxx": []}
            ]}
        ],
        "08.+Audit": [],
        "09.+Meeting+minutes": [
            {"Engineering": [
                {"Arch": [
                    {"yyyy-mm-dd-meeting+topic": []}
                ]},
                {"Dev": []},
                {"Algorithm": []},
                {"DevOps": []}
            ]},
            {"Design": []},
            {"Innovation": []},
            {"Cross+team": []}
        ]
    }

    for category, subcategories in source_structure.items():
        category_title = category.replace('+', ' ')
        category_id = create_or_get_page(category_title, DESTINATION_SPACE_KEY)
        if category_id is None:
            continue
        for subcategory_dict in subcategories:
            for subcategory, pages in subcategory_dict.items():
                subcategory_title = subcategory.replace('+', ' ')
                subcategory_id = create_or_get_page(subcategory_title, DESTINATION_SPACE_KEY, category_id)
                if subcategory_id is None:
                    continue
                process_pages(pages, DESTINATION_SPACE_KEY, subcategory_id)

if __name__ == "__main__":
    main()



利用 AI 模型进行了代码优化

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
@Author  : XXXXXX
@Contact : XXXXXX
@File    : ConfluenceMigrationPageTool.py
@Create Time:  2025/5/30 18:01
@Description: 一键创建Confluence Matrix目录层级,不需要手动执行
"""

import os
import logging
from atlassian import Confluence

# 日志配置
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 配置信息
CONFLUENCE_URL = os.getenv('CONFLUENCE_URL') or 'https://kone.atlassian.net/wiki'
SOURCE_SPACE_KEY = os.getenv('SOURCE_SPACE_KEY') or 'CTF'
DESTINATION_SPACE_KEY = os.getenv('DESTINATION_SPACE_KEY') or 'GAPP'  # 目标空间键更改为 GAPP
USERNAME = os.getenv('CONFLUENCE_USERNAME') or 'XXXXXX'  # 替换为个人邮箱
API_TOKEN = os.getenv('CONFLUENCE_API_TOKEN') or 'XXXXXX'  # 替换为个人API令牌

def validate_config():
    if not CONFLUENCE_URL or not USERNAME or not API_TOKEN or not SOURCE_SPACE_KEY or not DESTINATION_SPACE_KEY:
        raise ValueError("Configuration parameters are missing or invalid.")

# 创建 Confluence 实例
confluence = Confluence(
    url=CONFLUENCE_URL,
    username=USERNAME,
    password=API_TOKEN
)

def escape_cql_query(query):
    """Escape special characters in CQL query."""
    return query.replace('&', '&amp;').replace('+', '%2B')

def get_page_id_by_title(space_key, title, parent_id=None):
    cql_query = f"space={space_key} and title='{escape_cql_query(title)}'"
    if parent_id:
        cql_query += f" and ancestor={parent_id}"
    try:
        results = confluence.cql(cql_query)
        if results.get('results'):
            return results['results'][0]['content']['id']
    except Exception as e:
        logger.error(f"Failed to execute CQL query '{cql_query}': {e}")
    return None

def create_or_get_page(title, space_key, parent_id=None):
    """
    Creates a new page in the specified Confluence space with the given title and parent.
    If the page already exists, returns its ID.

    Args:
        title (str): The title of the page to create.
        space_key (str): The key of the Confluence space where the page will be created.
        parent_id (int, optional): The ID of the parent page under which the new page will be created.

    Returns:
        int: The ID of the created or existing page.
    """
    page_id = get_page_id_by_title(space_key, title, parent_id)
    if page_id:
        logger.info(f"Page '{title}' already exists with ID: {page_id}")
        return page_id
    else:
        page_body = '<p>This is a placeholder page.</p>'
        try:
            new_page = confluence.create_page(
                space=space_key,
                title=title,
                body=page_body,
                parent_id=parent_id
            )
            logger.info(f"Created page: {new_page['title']} with ID: {new_page['id']}")
            return new_page['id']
        except Exception as e:
            logger.error(f"Failed to create page '{title}': {e}")
            return None

def replace_plus_with_space(text):
    """
    Replaces '+' with a space in the given text.

    Args:
        text (str): The input string containing '+' characters.

    Returns:
        str: The modified string with spaces instead of '+'.
    """
    return text.replace('+', ' ')

def process_pages(pages, space_key, parent_id):
    """
    Recursively processes and creates pages based on the provided structure.

    Args:
        pages (list): A list of dictionaries representing the page structure.
        space_key (str): The key of the Confluence space where the pages will be created.
        parent_id (int, optional): The ID of the parent page under which the new pages will be created.
    """
    for page_dict in pages:
        for page, child_pages in page_dict.items():
            page_title = replace_plus_with_space(page)
            page_id = create_or_get_page(page_title, space_key, parent_id)
            if page_id is not None:
                process_pages(child_pages, space_key, page_id)

def main():
    validate_config()

    # 源目录结构
    source_structure = {
        "00.+Product+Overview": [
            {"Introduction": []},
            {"Product+Roadmap": [
                {"Overall": []},
                {"Quarterly+planning": []}
            ]},
            {"Project+key+member+responsibility": []}
        ],
        "01.+Requirement": [
            {"Business+Requirements": [
                {"Policy&Code": []},
                {"Marketing+Analyzation": []},
                {"Competitors+investigation": []},
                {"Business+case": []}
            ]},
            {"Functional+Requirements": [
                {"Module-XXX": [
                    {"Features-XXX+PRD": [
                        {"Business+Background": []},
                        {"Role+Authoritarian": []},
                        {"Requirement+description": []},
                        {"Business+Workflow": []},
                        {"Prototype+design": []},
                        {"Page+elements+definition": []},
                        {"Log": []},
                        {"Requirement+Review+Meeting+Summary": []}
                    ]}
                ]}
            ]},
            {"Non-Functional+Requirements": [
                {"Role+Setting": []},
                {"Product+Performance": []}
            ]},
            {"Business+value+Review": []}
        ],
        "02.+Engineering": [
            {"01.+Architecture": [
                {"Tech-arch": []},
                {"Business-arch": []},
                {"Data-arch": []},
                {"Feature-xxx": []}
            ]},
            {"02.+Development": [
                {"Frontend-App": []},
                {"Frontend-Web": []},
                {"Frontend-Mini": []},
                {"domain+name": [
                    {"domain+arch": [
                        {"features-xxx": []}
                    ]},
                    {"app+name": [
                        {"app-name-api": []},
                        {"design+for+key+feature+1": []}
                    ]}
                ]}
            ]},
            {"03.+Data+Intelligence": []},
            {"04.+Validation+Quality": [
                {"Test+Specifications": [
                    {"Test+ENV": []},
                    {"Test+Strategy": []},
                    {"Test+Spec+Documents": []}
                ]},
                {"Test+Cases": []},
                {"Test+Reports": []},
                {"Automation": [
                    {"Automation+Strategy": []},
                    {"Automation+Test+Result": []},
                    {"Automation+Coverage+Track": []}
                ]},
                {"Non-Function+Test": [
                    {"Performance+Test": []},
                    {"Stability+Test": []},
                    {"Compatibility+test": []},
                    {"Usability+Test": []}
                ]},
                {"PRD+Leaking+Bug+Retro": []}
            ]},
            {"05.+Data+Services+Products": [
                {"KCDP": [
                    {"PoC": []},
                    {"Common+Services": []},
                    {"Data+Engineering": []}
                ]},
                {"Digital+enabled+services+247+services": [
                    {"Device+view": []},
                    {"Device+Shadow": []},
                    {"Dynamic+Scheduling": []},
                    {"ISN+CN": []}
                ]}
            ]}
        ],
        "03.+Application+Security": [
            {"Security+Summary": []},
            {"Secure+Design": [
                {"Security+protocol": []},
                {"Common+Reference+design": []}
            ]},
            {"Security+Requirements": []},
            {"Security+guideline": []},
            {"Security+Certificate": [
                {"MLPS+certificate": []},
                {"IEC-62443+certificate": []},
                {"ISO27001+series": []}
            ]},
            {"Security+Manual": []},
            {"Security+Testing": [
                {"Security+requirements+verification": []},
                {"Hot+findings+mitigation+summary": []},
                {"Pen+testing+Summary": []}
            ]}
        ],
        "04.+Releases": [
            {"Release+Calendar": [
                {"2025": []},
                {"2026": []}
            ]},
            {"Release+Version": [
                {"v-x.y.z": [
                    {"v-x.y.z-git-env-map": []},
                    {"v-x.y.z-human-resource": []},
                    {"v-x.y.z-runbook": [
                        {"v-x.y.z-runbook-result": []}
                    ]},
                    {"v-x.y.z-dev-to-test": [
                        {"feature-xxxxxx": []}
                    ]},
                    {"v-x.y.z-test-report": []},
                    {"v-x.y.z-security-report": []},
                    {"v-x.y.z-deploy-approve": []}
                ]},
                {"v-x.y.z.w": []}
            ]}
        ],
        "05.+Deployment+Operations": [
            {"Deployment+Guide": []},
            {"CI+CD+Pipeline": []},
            {"Monitoring": []},
            {"Incident+Management": []},
            {"User+Manual+FAQ": []}
        ],
        "06.+Knowledge": [],
        "07.+Project+Management": [
            {"Process": []},
            {"Team+Contacts": []},
            {"Team+Availability": []},
            {"Team+Member+privilege": [
                {"system-xxx": []}
            ]}
        ],
        "08.+Audit": [],
        "09.+Meeting+minutes": [
            {"Engineering": [
                {"Arch": [
                    {"yyyy-mm-dd-meeting+topic": []}
                ]},
                {"Dev": []},
                {"Algorithm": []},
                {"DevOps": []}
            ]},
            {"Design": []},
            {"Innovation": []},
            {"Cross+team": []}
        ]
    }

    for category, subcategories in source_structure.items():
        category_title = replace_plus_with_space(category)
        category_id = create_or_get_page(category_title, DESTINATION_SPACE_KEY)
        if category_id is None:
            continue
        for subcategory_dict in subcategories:
            for subcategory, pages in subcategory_dict.items():
                subcategory_title = replace_plus_with_space(subcategory)
                subcategory_id = create_or_get_page(subcategory_title, DESTINATION_SPACE_KEY, category_id)
                if subcategory_id is None:
                    continue
                process_pages(pages, DESTINATION_SPACE_KEY, subcategory_id)

if __name__ == "__main__":
    main()



对比分析

特征 原始脚本 优化后脚本
安全性 敏感信息硬编码 使用环境变量存储敏感信息
日志记录 使用 print 语句 使用 logging 模块,支持不同级别的日志
输入验证 缺少 添加 validate_config 函数验证配置参数
代码复用 字符串替换逻辑分散 提取 replace_plus_with_space 函数
文档字符串 缺少 为每个函数添加详细的文档字符串
性能优化 可以通过批量查询等方式进一步优化(未具体实现)

最终效果


如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
No Reply at the moment.
需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up