6d70520e79
- 新增后端模块:Alert、APIAsset、Compliance、Lineage、Masking、Risk、SchemaChange、Unstructured、Watermark - 新增前端模块页面与API接口 - 新增Alembic迁移脚本(002-014)覆盖全量业务表 - 新增测试数据生成脚本与集成测试脚本 - 修复metadata模型JSON类型导入缺失导致启动失败的问题 - 修复前端Alert/APIAsset页面request模块路径错误 - 更新docker-compose与开发计划文档
419 lines
16 KiB
Python
419 lines
16 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Generate DataPointer White Paper PPT with screenshots."""
|
|
|
|
import os
|
|
from pptx import Presentation
|
|
from pptx.util import Inches, Pt
|
|
from pptx.dml.color import RGBColor
|
|
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
|
|
from pptx.enum.shapes import MSO_SHAPE
|
|
from io import BytesIO
|
|
from PIL import Image
|
|
|
|
# Config
|
|
SCREENSHOT_DIR = '/Users/nathan/Work/DataPointer/prop-data-guard/screenshots'
|
|
OUTPUT_PATH = '/Users/nathan/Work/DataPointer/DataPointer产品介绍白皮书.pptx'
|
|
SLIDE_WIDTH = Inches(13.333)
|
|
SLIDE_HEIGHT = Inches(7.5)
|
|
|
|
# Color theme
|
|
COLOR_PRIMARY = RGBColor(0x1A, 0x56, 0xDB) # Deep blue
|
|
COLOR_SECONDARY = RGBColor(0x10, 0xB9, 0x81) # Green
|
|
COLOR_DARK = RGBColor(0x1E, 0x29, 0x3B) # Dark slate
|
|
COLOR_TEXT = RGBColor(0x37, 0x41, 0x51) # Gray text
|
|
COLOR_LIGHT = RGBColor(0xF8, 0xFA, 0xFC) # Light bg
|
|
COLOR_ACCENT = RGBColor(0xF5, 0x9E, 0x0B) # Orange accent
|
|
|
|
|
|
def add_title_slide(prs, title, subtitle):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6]) # blank
|
|
slide.shapes._spTree.remove(slide.shapes._spTree[0]) if len(slide.shapes._spTree) > 0 else None
|
|
|
|
# Background shape
|
|
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
|
|
bg.fill.solid()
|
|
bg.fill.fore_color.rgb = COLOR_DARK
|
|
bg.line.fill.background()
|
|
|
|
# Accent bar
|
|
bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, Inches(0.8), Inches(3.0), Inches(0.15), Inches(2.2))
|
|
bar.fill.solid()
|
|
bar.fill.fore_color.rgb = COLOR_PRIMARY
|
|
bar.line.fill.background()
|
|
|
|
# Title
|
|
title_box = slide.shapes.add_textbox(Inches(1.2), Inches(2.8), Inches(10), Inches(1.5))
|
|
tf = title_box.text_frame
|
|
tf.word_wrap = True
|
|
p = tf.paragraphs[0]
|
|
p.text = title
|
|
p.font.size = Pt(48)
|
|
p.font.bold = True
|
|
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
|
|
|
|
# Subtitle
|
|
sub_box = slide.shapes.add_textbox(Inches(1.2), Inches(4.4), Inches(10), Inches(0.8))
|
|
tf = sub_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = subtitle
|
|
p.font.size = Pt(22)
|
|
p.font.color.rgb = RGBColor(0xA0, 0xAE, 0xC0)
|
|
|
|
# Bottom info
|
|
info_box = slide.shapes.add_textbox(Inches(1.2), Inches(6.5), Inches(10), Inches(0.5))
|
|
tf = info_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = "Property Insurance Data Classification Platform | 2026"
|
|
p.font.size = Pt(12)
|
|
p.font.color.rgb = RGBColor(0x71, 0x80, 0x96)
|
|
|
|
return slide
|
|
|
|
|
|
def add_section_slide(prs, section_num, section_title):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
|
|
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
|
|
bg.fill.solid()
|
|
bg.fill.fore_color.rgb = COLOR_PRIMARY
|
|
bg.line.fill.background()
|
|
|
|
num_box = slide.shapes.add_textbox(Inches(0.8), Inches(2.5), Inches(2), Inches(1.5))
|
|
tf = num_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = f"0{section_num}" if section_num < 10 else str(section_num)
|
|
p.font.size = Pt(72)
|
|
p.font.bold = True
|
|
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
|
|
p.font.opacity = 0.3
|
|
|
|
title_box = slide.shapes.add_textbox(Inches(0.8), Inches(4.0), Inches(10), Inches(1.0))
|
|
tf = title_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = section_title
|
|
p.font.size = Pt(40)
|
|
p.font.bold = True
|
|
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
|
|
|
|
return slide
|
|
|
|
|
|
def add_content_slide(prs, title, bullets, image_path=None):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
|
|
# Light background
|
|
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
|
|
bg.fill.solid()
|
|
bg.fill.fore_color.rgb = COLOR_LIGHT
|
|
bg.line.fill.background()
|
|
# Send to back
|
|
spTree = slide.shapes._spTree
|
|
spTree.insert(2, bg._element)
|
|
|
|
# Top bar
|
|
top_bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, Inches(0.12))
|
|
top_bar.fill.solid()
|
|
top_bar.fill.fore_color.rgb = COLOR_PRIMARY
|
|
top_bar.line.fill.background()
|
|
|
|
# Title
|
|
title_box = slide.shapes.add_textbox(Inches(0.6), Inches(0.35), Inches(12), Inches(0.6))
|
|
tf = title_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = title
|
|
p.font.size = Pt(28)
|
|
p.font.bold = True
|
|
p.font.color.rgb = COLOR_DARK
|
|
|
|
# Content
|
|
if image_path and os.path.exists(image_path):
|
|
# Image on right, text on left
|
|
content_box = slide.shapes.add_textbox(Inches(0.6), Inches(1.2), Inches(4.5), Inches(5.5))
|
|
tf = content_box.text_frame
|
|
tf.word_wrap = True
|
|
for i, bullet in enumerate(bullets):
|
|
if i == 0:
|
|
p = tf.paragraphs[0]
|
|
else:
|
|
p = tf.add_paragraph()
|
|
p.text = f"• {bullet}"
|
|
p.font.size = Pt(16)
|
|
p.font.color.rgb = COLOR_TEXT
|
|
p.space_after = Pt(12)
|
|
|
|
# Add image
|
|
img = Image.open(image_path)
|
|
orig_w, orig_h = img.size
|
|
max_w = Inches(7.0)
|
|
max_h = Inches(5.5)
|
|
ratio = min(max_w / orig_w, max_h / orig_h)
|
|
img_w = orig_w * ratio
|
|
img_h = orig_h * ratio
|
|
left = SLIDE_WIDTH - img_w - Inches(0.6)
|
|
top = Inches(1.0) + (max_h - img_h) / 2
|
|
slide.shapes.add_picture(image_path, left, top, width=img_w, height=img_h)
|
|
else:
|
|
content_box = slide.shapes.add_textbox(Inches(0.6), Inches(1.2), Inches(12), Inches(5.5))
|
|
tf = content_box.text_frame
|
|
tf.word_wrap = True
|
|
for i, bullet in enumerate(bullets):
|
|
if i == 0:
|
|
p = tf.paragraphs[0]
|
|
else:
|
|
p = tf.add_paragraph()
|
|
p.text = f"• {bullet}"
|
|
p.font.size = Pt(18)
|
|
p.font.color.rgb = COLOR_TEXT
|
|
p.space_after = Pt(14)
|
|
|
|
return slide
|
|
|
|
|
|
def add_image_slide(prs, title, image_path, caption=""):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
|
|
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
|
|
bg.fill.solid()
|
|
bg.fill.fore_color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
|
|
bg.line.fill.background()
|
|
spTree = slide.shapes._spTree
|
|
spTree.insert(2, bg._element)
|
|
|
|
top_bar = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, Inches(0.12))
|
|
top_bar.fill.solid()
|
|
top_bar.fill.fore_color.rgb = COLOR_PRIMARY
|
|
top_bar.line.fill.background()
|
|
|
|
title_box = slide.shapes.add_textbox(Inches(0.6), Inches(0.35), Inches(12), Inches(0.5))
|
|
tf = title_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = title
|
|
p.font.size = Pt(26)
|
|
p.font.bold = True
|
|
p.font.color.rgb = COLOR_DARK
|
|
|
|
if os.path.exists(image_path):
|
|
img = Image.open(image_path)
|
|
orig_w, orig_h = img.size
|
|
max_w = Inches(11.5)
|
|
max_h = Inches(5.8)
|
|
ratio = min(max_w / orig_w, max_h / orig_h)
|
|
img_w = orig_w * ratio
|
|
img_h = orig_h * ratio
|
|
left = (SLIDE_WIDTH - img_w) / 2
|
|
top = Inches(1.0)
|
|
slide.shapes.add_picture(image_path, left, top, width=img_w, height=img_h)
|
|
|
|
if caption:
|
|
cap_box = slide.shapes.add_textbox(Inches(0.6), Inches(6.9), Inches(12), Inches(0.4))
|
|
tf = cap_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = caption
|
|
p.font.size = Pt(12)
|
|
p.font.color.rgb = RGBColor(0x71, 0x80, 0x96)
|
|
p.alignment = PP_ALIGN.CENTER
|
|
|
|
return slide
|
|
|
|
|
|
def add_summary_slide(prs):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
|
|
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
|
|
bg.fill.solid()
|
|
bg.fill.fore_color.rgb = COLOR_DARK
|
|
bg.line.fill.background()
|
|
|
|
title_box = slide.shapes.add_textbox(Inches(0.8), Inches(1.5), Inches(11.5), Inches(1.0))
|
|
tf = title_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = "DataPointer 产品优势"
|
|
p.font.size = Pt(36)
|
|
p.font.bold = True
|
|
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
|
|
|
|
items = [
|
|
("全生命周期数据安全", "从数据采集、分类分级、风险评估到持续监控,覆盖数据安全全生命周期"),
|
|
("智能自动化引擎", "基于规则+AI的智能分类引擎,自动识别敏感数据,效率提升80%"),
|
|
("精细化权限管控", "RBAC 角色权限模型,支持数据隔离,满足多租户场景"),
|
|
("保险行业深度适配", "内置保险行业数据分类分级标准,覆盖客户、保单、理赔等核心领域"),
|
|
("可视化数据资产", "仪表盘+报表多维展示,数据资产状况一目了然"),
|
|
]
|
|
|
|
y = Inches(2.8)
|
|
for title, desc in items:
|
|
box = slide.shapes.add_textbox(Inches(0.8), y, Inches(11.5), Inches(0.8))
|
|
tf = box.text_frame
|
|
tf.word_wrap = True
|
|
p = tf.paragraphs[0]
|
|
p.text = f"▎ {title}"
|
|
p.font.size = Pt(18)
|
|
p.font.bold = True
|
|
p.font.color.rgb = COLOR_SECONDARY
|
|
p = tf.add_paragraph()
|
|
p.text = f" {desc}"
|
|
p.font.size = Pt(14)
|
|
p.font.color.rgb = RGBColor(0xA0, 0xAE, 0xC0)
|
|
y += Inches(0.85)
|
|
|
|
return slide
|
|
|
|
|
|
def add_end_slide(prs):
|
|
slide = prs.slides.add_slide(prs.slide_layouts[6])
|
|
|
|
bg = slide.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SLIDE_WIDTH, SLIDE_HEIGHT)
|
|
bg.fill.solid()
|
|
bg.fill.fore_color.rgb = COLOR_PRIMARY
|
|
bg.line.fill.background()
|
|
|
|
title_box = slide.shapes.add_textbox(Inches(0), Inches(2.8), SLIDE_WIDTH, Inches(1.0))
|
|
tf = title_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = "谢谢观看"
|
|
p.font.size = Pt(52)
|
|
p.font.bold = True
|
|
p.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
|
|
p.alignment = PP_ALIGN.CENTER
|
|
|
|
sub_box = slide.shapes.add_textbox(Inches(0), Inches(4.0), SLIDE_WIDTH, Inches(0.6))
|
|
tf = sub_box.text_frame
|
|
p = tf.paragraphs[0]
|
|
p.text = "DataPointer - 让数据安全触手可及"
|
|
p.font.size = Pt(20)
|
|
p.font.color.rgb = RGBColor(0xA0, 0xAE, 0xC0)
|
|
p.alignment = PP_ALIGN.CENTER
|
|
|
|
return slide
|
|
|
|
|
|
def main():
|
|
prs = Presentation()
|
|
prs.slide_width = SLIDE_WIDTH
|
|
prs.slide_height = SLIDE_HEIGHT
|
|
|
|
# 1. Cover
|
|
add_title_slide(prs, "DataPointer", "数据安全分级及风险管理平台产品介绍白皮书")
|
|
|
|
# 2. TOC
|
|
add_content_slide(prs, "目录", [
|
|
"产品概述与定位",
|
|
"系统架构与技术栈",
|
|
"核心功能模块详解",
|
|
"产品优势与价值",
|
|
"应用场景与最佳实践"
|
|
])
|
|
|
|
# 3. Section: Overview
|
|
add_section_slide(prs, 1, "产品概述")
|
|
|
|
add_content_slide(prs, "DataPointer 产品定位", [
|
|
"DataPointer 是面向保险行业的数据安全分级及风险管理平台",
|
|
"基于国家及行业数据分类分级标准,帮助企业实现数据资产的自动化识别与分级",
|
|
"支持从数据源接入、元数据采集、智能分类、人工复核到报表输出的完整工作流",
|
|
"内置保险行业专属分类标准,覆盖客户、保单、理赔、财务、渠道、监管等核心领域",
|
|
"提供多维度数据资产视图,助力企业满足数据安全合规要求"
|
|
])
|
|
|
|
# 4. Section: Architecture
|
|
add_section_slide(prs, 2, "系统架构")
|
|
|
|
add_content_slide(prs, "技术架构", [
|
|
"前端:Vue 3.4 + Vite + TypeScript + Element Plus 2.7 + ECharts 5.4",
|
|
"后端:FastAPI 0.111 + SQLAlchemy 2.0 + PostgreSQL 16 + Redis 7 + Celery 5.4",
|
|
"部署:容器化部署,支持 Docker Compose / Kubernetes",
|
|
"安全:JWT 认证 + RBAC 权限控制 + 数据隔离,满足等保要求",
|
|
"扩展:模块化设计,支持自定义分类标准与规则引擎扩展"
|
|
])
|
|
|
|
# 5. Section: Features
|
|
add_section_slide(prs, 3, "核心功能")
|
|
|
|
add_image_slide(prs, "安全登录与权限管控", os.path.join(SCREENSHOT_DIR, "01_login.png"),
|
|
"支持用户名密码登录,JWT Token 认证,会话安全")
|
|
|
|
add_image_slide(prs, "仪表盘总览", os.path.join(SCREENSHOT_DIR, "02_dashboard.png"),
|
|
"实时展示数据源、表、字段、分级结果等核心指标,支持敏感数据分布可视化")
|
|
|
|
add_content_slide(prs, "数据源管理", [
|
|
"支持多种数据库类型接入:MySQL、PostgreSQL、Oracle、SQL Server 等",
|
|
"自动扫描数据库元数据,采集表结构与字段信息",
|
|
"支持数据源连接测试与状态监控",
|
|
"批量导入导出数据源配置,提升运维效率"
|
|
], os.path.join(SCREENSHOT_DIR, "03_datasource.png"))
|
|
|
|
add_content_slide(prs, "数据资产管理", [
|
|
"自动生成数据资产目录,展示数据库、表、字段的层级关系",
|
|
"支持字段级元数据查看,包括数据类型、注释、采样数据等",
|
|
"资产变更追踪,记录元数据的增删改历史",
|
|
"与分类分级结果联动,直观展示各资产的安全等级"
|
|
], os.path.join(SCREENSHOT_DIR, "04_metadata.png"))
|
|
|
|
add_content_slide(prs, "分类分级标准", [
|
|
"内置保险行业五级分类标准:公开级(L1)、内部级(L2)、敏感级(L3)、重要级(L4)、核心级(L5)",
|
|
"支持自定义分类标准与分级规则,灵活适配不同业务场景",
|
|
"规则引擎支持正则匹配、关键字匹配、语义识别等多种检测方式",
|
|
"标准模板管理,支持一键导入导出行业标准模板"
|
|
], os.path.join(SCREENSHOT_DIR, "05_category.png"))
|
|
|
|
add_content_slide(prs, "项目管理", [
|
|
"支持创建多个分类分级项目,独立管理与进度跟踪",
|
|
"项目维度统计:自动识别、人工标注、已复核数量",
|
|
"支持批量触发自动分类任务,提升处理效率",
|
|
"项目经理可查看负责项目的全部进度与成果"
|
|
], os.path.join(SCREENSHOT_DIR, "06_project.png"))
|
|
|
|
add_content_slide(prs, "任务管理", [
|
|
"自动拆解项目为标注/复核任务,分配给标注员与审核员",
|
|
"任务状态追踪:待处理、处理中、已完成、已驳回",
|
|
"支持任务领取与批量处理,提升团队协作效率",
|
|
"数据隔离:普通用户仅能查看和处理分配给自己的任务"
|
|
], os.path.join(SCREENSHOT_DIR, "07_task.png"))
|
|
|
|
add_content_slide(prs, "分类分级结果", [
|
|
"展示所有字段的分类分级结果,支持多维筛选与搜索",
|
|
"结果包含:字段名称、所属表/库、分类标准、安全等级、识别方式",
|
|
"支持结果的批量导出,便于合规审计与报告生成",
|
|
"人工复核机制,确保分类分级的准确性与权威性"
|
|
], os.path.join(SCREENSHOT_DIR, "08_classification.png"))
|
|
|
|
add_content_slide(prs, "报表统计", [
|
|
"多维统计报表:按等级分布、类别分布、数据源分布等维度展示",
|
|
"支持 Word 报告一键生成,包含完整的数据资产与分级详情",
|
|
"可视化图表直观展示敏感数据分布与趋势",
|
|
"满足监管报送与内部审计的数据安全报告需求"
|
|
], os.path.join(SCREENSHOT_DIR, "09_report.png"))
|
|
|
|
add_content_slide(prs, "系统管理", [
|
|
"用户管理:支持用户增删改查、角色分配、部门归属",
|
|
"角色权限:基于 RBAC 模型,支持 admin、project_manager、labeler、reviewer、guest 等角色",
|
|
"数据隔离:严格的数据权限控制,确保跨部门数据安全",
|
|
"操作日志:完整记录用户操作行为,支持安全审计"
|
|
], os.path.join(SCREENSHOT_DIR, "10_system.png"))
|
|
|
|
# 6. Section: Advantages
|
|
add_section_slide(prs, 4, "产品优势")
|
|
add_summary_slide(prs)
|
|
|
|
# 7. Section: Scenarios
|
|
add_section_slide(prs, 5, "应用场景")
|
|
|
|
add_content_slide(prs, "典型应用场景", [
|
|
"数据安全合规:帮助企业满足《数据安全法》《个人信息保护法》等法规要求",
|
|
"等保测评:提供完整的数据分类分级证据链,支撑等级保护测评",
|
|
"数据资产盘点:全面梳理企业数据资产,建立统一的数据资产目录",
|
|
"敏感数据发现:自动识别客户身份证号、银行卡号、保单信息等敏感字段",
|
|
"数据分级保护:根据分级结果制定差异化的数据安全保护策略"
|
|
])
|
|
|
|
# 8. End
|
|
add_end_slide(prs)
|
|
|
|
prs.save(OUTPUT_PATH)
|
|
print(f"PPT saved to: {OUTPUT_PATH}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|