---
name: apple-icp-exemption
description: 生成 Apple 国区 ICP 豁免申请附件 PDF。当用户提到 ICP 备案、Apple 国区下架、ICP 豁免申请、App Store 中国区合规、申请例外批准等相关内容时，立即触发此 skill。收集用户的 Team ID、账户持有人姓名、App ID 等信息，调用脚本生成符合 Apple 要求的正式申请附件 PDF 文件。
---

# Apple 国区 ICP 豁免申请附件生成器

## 概述

此 skill 用于生成 Apple App Store 中国大陆地区 ICP 备案豁免申请所需的正式附件 PDF。

## 触发场景

- 用户提到 ICP 备案/豁免/例外
- 用户的 App 在国区被下架，需要申请豁免
- 用户需要准备 Apple 中国区合规材料
- 用户提到 "申请例外批准"、"ICP 相关申诉" 等

## 信息收集

在生成 PDF 前，需要向用户收集以下信息：

### 必填信息
1. **Team ID**（团队 ID）— 在 App Store Connect → 账户 → 会员资格 中查看
2. **账户持有人法定姓名**（中文全名，与证件一致）
3. **App ID**（应用 ID）— 在 App Store Connect 的 App 详情页中查看
4. **申请日期**（默认今天，用户可更改）

### 信息收集方式

直接在对话中逐一询问，或一次性询问所有信息：

```
请提供以下信息来生成 ICP 豁免申请附件：
1. Team ID（例如：ABCD123456）
2. 账户持有人法定姓名
3. App ID（例如：1234567890）
4. 申请日期（格式：YYYY年MM月DD日，留空则使用今天）
```

## PDF 生成

收集好所有信息后，运行以下 Python 脚本生成 PDF：

```python
#!/usr/bin/env python3
"""
Apple 国区 ICP 豁免申请附件生成器
依赖：pip install reportlab
"""

import argparse
from datetime import datetime
from pathlib import Path


def get_today_chinese():
    """返回今天的中文日期"""
    today = datetime.today()
    return f"{today.year}年{today.month:02d}月{today.day:02d}日"


def generate_pdf(team_id: str, name: str, app_id: str, date: str, output_path: str):
    from reportlab.lib.pagesizes import A4
    from reportlab.lib.units import mm
    from reportlab.lib.styles import ParagraphStyle
    from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY
    from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, HRFlowable, Table, TableStyle
    from reportlab.lib import colors
    from reportlab.pdfbase import pdfmetrics
    from reportlab.pdfbase.ttfonts import TTFont
    import os

    # 注册中文字体
    font_name = "Helvetica"
    bold_font_name = "Helvetica-Bold"
    
    cjk_fonts = [
        ("/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc", "WQYZenHei"),
        ("/usr/share/fonts/truetype/wqy/wqy-microhei.ttc", "WQYMicroHei"),
        ("/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", "NotoSansCJK"),
        ("/usr/share/fonts/truetype/noto/NotoSansCJK-Regular.ttc", "NotoSansCJK"),
        ("/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", "NotoSansCJK"),
    ]
    
    for font_path, font_reg_name in cjk_fonts:
        if os.path.exists(font_path):
            try:
                pdfmetrics.registerFont(TTFont(font_reg_name, font_path))
                font_name = font_reg_name
                bold_font_name = font_reg_name
                break
            except Exception:
                continue

    # 页面布局
    margin = 30 * mm
    doc = SimpleDocTemplate(
        output_path,
        pagesize=A4,
        leftMargin=margin,
        rightMargin=margin,
        topMargin=25 * mm,
        bottomMargin=25 * mm,
        title="Apple 国区 ICP 豁免申请附件",
        author=name,
    )

    # 样式定义
    def style(name_s, **kwargs):
        defaults = dict(fontName=font_name, fontSize=11, leading=18, spaceAfter=0, spaceBefore=0)
        defaults.update(kwargs)
        return ParagraphStyle(name_s, **defaults)

    title_style = style("Title", fontName=bold_font_name, fontSize=16, leading=26,
                        alignment=TA_CENTER, spaceBefore=0, spaceAfter=8)
    subtitle_style = style("Subtitle", fontSize=11, alignment=TA_CENTER, spaceAfter=4)
    section_header_style = style("SectionHeader", fontName=bold_font_name, fontSize=12,
                                  leading=20, spaceBefore=14, spaceAfter=4)
    info_key_style = style("InfoKey", fontName=bold_font_name, fontSize=11, leading=20)
    info_val_style = style("InfoVal", fontSize=11, leading=20)
    declaration_style = style("Declaration", fontSize=11, leading=22, alignment=TA_JUSTIFY)
    sign_label_style = style("SignLabel", fontName=bold_font_name, fontSize=11, leading=22)
    sign_val_style = style("SignVal", fontSize=11, leading=22)
    footer_style = style("Footer", fontSize=9, leading=14, alignment=TA_CENTER,
                          textColor=colors.grey)

    story = []

    # 标题
    story.append(Spacer(1, 6 * mm))
    story.append(Paragraph("Apple 国区 ICP 豁免申请附件", title_style))
    story.append(Paragraph("App Store Connect 中国大陆地区 ICP 备案例外申请", subtitle_style))
    story.append(Spacer(1, 3 * mm))
    story.append(HRFlowable(width="100%", thickness=1.5, color=colors.black))
    story.append(Spacer(1, 5 * mm))

    # 一、账户信息
    story.append(Paragraph("一、账户信息", section_header_style))
    info_data = [
        [Paragraph("Team ID（团队 ID）", info_key_style),
         Paragraph(f"：{team_id}", info_val_style)],
        [Paragraph("账户持有人法定姓名", info_key_style),
         Paragraph(f"：{name}", info_val_style)],
    ]
    info_table = Table(info_data, colWidths=[65 * mm, None])
    info_table.setStyle(TableStyle([
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
        ("LEFTPADDING", (0, 0), (-1, -1), 0),
        ("RIGHTPADDING", (0, 0), (-1, -1), 0),
        ("TOPPADDING", (0, 0), (-1, -1), 2),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 2),
    ]))
    story.append(info_table)

    # 二、App 信息
    story.append(Paragraph("二、App 信息", section_header_style))
    app_data = [
        [Paragraph("App ID（应用 ID）", info_key_style),
         Paragraph(f"：{app_id}", info_val_style)],
    ]
    app_table = Table(app_data, colWidths=[65 * mm, None])
    app_table.setStyle(TableStyle([
        ("VALIGN", (0, 0), (-1, -1), "TOP"),
        ("LEFTPADDING", (0, 0), (-1, -1), 0),
        ("RIGHTPADDING", (0, 0), (-1, -1), 0),
        ("TOPPADDING", (0, 0), (-1, -1), 2),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 2),
    ]))
    story.append(app_table)

    # 三、申请声明
    story.append(Paragraph("三、申请声明", section_header_style))
    declarations = [
        f"本人 {name}，Team ID 为 {team_id}，现就 App ID 为 {app_id} 的独立应用，向 Apple 申请中国大陆地区 ICP 备案豁免例外批准。本人声明如下：",
        "",
        "1. 本人有意就上述独立 App 向 Apple 申请例外批准。",
        "",
        "2. 本人已充分了解并遵守所有相关法律法规及 Apple 的相关政策要求，确认本 App 属于以下豁免情形之一：",
        "    • 完全离线应用，不进行任何网络通信；或",
        "    • 仅通过 iCloud 同步数据，不连接其他任何服务器；或",
        "    • 仅通过 Apple 内购（IAP）进行交易，无自建支付系统及其他联网功能。",
        "",
        "3. 本人确认所提交的所有信息真实、准确、完整，与 App Store Connect 账户信息完全一致。",
        "",
        "4. 如存在任何虚假陈述或误导信息，本人愿意承担由此产生的全部法律责任。",
    ]
    for line in declarations:
        if line == "":
            story.append(Spacer(1, 3 * mm))
        else:
            story.append(Paragraph(line, declaration_style))

    # 四、签署
    story.append(Spacer(1, 8 * mm))
    story.append(HRFlowable(width="100%", thickness=0.8, color=colors.HexColor("#aaaaaa")))
    story.append(Spacer(1, 5 * mm))
    story.append(Paragraph("四、签署", section_header_style))
    story.append(Spacer(1, 2 * mm))

    sig_line = "_" * 20
    sign_data = [
        [Paragraph("手写签名：", sign_label_style),
         Paragraph(sig_line, sign_val_style),
         Paragraph("", sign_val_style)],
        [Paragraph("正楷姓名：", sign_label_style),
         Paragraph(name, sign_val_style),
         Paragraph("", sign_val_style)],
        [Paragraph("日　　期：", sign_label_style),
         Paragraph(date, sign_val_style),
         Paragraph("", sign_val_style)],
    ]
    sign_table = Table(sign_data, colWidths=[30 * mm, 80 * mm, None])
    sign_table.setStyle(TableStyle([
        ("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
        ("LEFTPADDING", (0, 0), (-1, -1), 0),
        ("RIGHTPADDING", (0, 0), (-1, -1), 4),
        ("TOPPADDING", (0, 0), (-1, -1), 5),
        ("BOTTOMPADDING", (0, 0), (-1, -1), 5),
    ]))
    story.append(sign_table)
    story.append(Spacer(1, 10 * mm))

    # 注意事项
    story.append(HRFlowable(width="100%", thickness=0.8, color=colors.HexColor("#aaaaaa")))
    story.append(Spacer(1, 4 * mm))
    notice_style = style("Notice", fontSize=9, leading=15, textColor=colors.HexColor("#555555"),
                          alignment=TA_JUSTIFY)
    story.append(Paragraph(
        "【注意事项】本附件仅供 Apple App Store Connect ICP 豁免申请使用。"
        "请在手写签名后将本文件扫描或拍照，作为附件上传至 App Store Connect 申诉流程中。"
        "如有多个 App 需要申请，请为每个 App 单独准备并提交一份附件。",
        notice_style
    ))
    story.append(Spacer(1, 5 * mm))
    story.append(Paragraph(
        f"本文件由 Apple ICP 豁免申请助手自动生成  ·  生成日期：{date}",
        footer_style
    ))

    doc.build(story)
    print(f"PDF 生成成功：{output_path}")


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="生成 Apple 国区 ICP 豁免申请附件 PDF")
    parser.add_argument("--team-id", required=True, help="Team ID（团队 ID）")
    parser.add_argument("--name", required=True, help="账户持有人法定姓名")
    parser.add_argument("--app-id", required=True, help="App ID")
    parser.add_argument("--date", default="", help="申请日期（留空则使用今天）")
    parser.add_argument("--output", default="ICP豁免申请附件.pdf", help="输出路径")
    args = parser.parse_args()
    
    date = args.date if args.date else get_today_chinese()
    Path(args.output).parent.mkdir(parents=True, exist_ok=True)
    generate_pdf(args.team_id, args.name, args.app_id, date, args.output)
```

### 使用示例

```bash
python generate_pdf.py \
  --team-id "ABCD123456" \
  --name "张三" \
  --app-id "1234567890" \
  --output "ICP豁免申请附件.pdf"
```

## 注意事项

- 生成的 PDF 需要用户**手写签名**后再提交
- 一个 App 对应一份附件，多个 App 需分别生成
- 提醒用户核实所有信息与 App Store Connect 账户完全一致
- PDF 使用中文，符合 Apple 中国区审核团队要求

## 申请流程

生成附件后，告知用户提交流程：

1. 打印或在平板上手写签名
2. 扫描/拍照保存为 PDF
3. 登录 App Store Connect，找到被下架的 App
4. 点击「联系我们」→「ICP 相关问题」
5. 上传签名后的附件，说明 App 不联网或仅使用 Apple 服务
6. 提交等待 3-7 个工作日审核
