feat: 全量功能模块开发与集成测试修复
- 新增后端模块:Alert、APIAsset、Compliance、Lineage、Masking、Risk、SchemaChange、Unstructured、Watermark - 新增前端模块页面与API接口 - 新增Alembic迁移脚本(002-014)覆盖全量业务表 - 新增测试数据生成脚本与集成测试脚本 - 修复metadata模型JSON类型导入缺失导致启动失败的问题 - 修复前端Alert/APIAsset页面request模块路径错误 - 更新docker-compose与开发计划文档
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 378 KiB |
@@ -0,0 +1,44 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
|
||||
(async () => {
|
||||
const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'] });
|
||||
const page = await browser.newPage();
|
||||
await page.setViewport({ width: 1920, height: 1080 });
|
||||
|
||||
const BASE = 'http://localhost:5173';
|
||||
const outDir = '/Users/nathan/Work/DataPointer/prop-data-guard/screenshots';
|
||||
|
||||
// 1. Login page
|
||||
console.log('Capturing login...');
|
||||
await page.goto(`${BASE}/login`, { waitUntil: 'networkidle2' });
|
||||
await new Promise(r => setTimeout(r, 1000));
|
||||
await page.screenshot({ path: `${outDir}/01_login.png`, fullPage: false });
|
||||
|
||||
// Login
|
||||
await page.type('input[placeholder="用户名"]', 'admin');
|
||||
await page.type('input[type="password"]', 'admin123');
|
||||
await page.click('.login-btn');
|
||||
await page.waitForNavigation({ waitUntil: 'networkidle2' });
|
||||
await new Promise(r => setTimeout(r, 2000));
|
||||
|
||||
// Helper to capture a page
|
||||
async function capture(path, filename, waitMs = 3000) {
|
||||
console.log(`Capturing ${path}...`);
|
||||
await page.goto(`${BASE}${path}`, { waitUntil: 'networkidle2' });
|
||||
await new Promise(r => setTimeout(r, waitMs));
|
||||
await page.screenshot({ path: `${outDir}/${filename}`, fullPage: false });
|
||||
}
|
||||
|
||||
await capture('/dashboard', '02_dashboard.png');
|
||||
await capture('/datasource', '03_datasource.png');
|
||||
await capture('/metadata', '04_metadata.png');
|
||||
await capture('/category', '05_category.png');
|
||||
await capture('/project', '06_project.png');
|
||||
await capture('/task', '07_task.png');
|
||||
await capture('/classification', '08_classification.png');
|
||||
await capture('/report', '09_report.png');
|
||||
await capture('/system', '10_system.png');
|
||||
|
||||
await browser.close();
|
||||
console.log('All screenshots captured!');
|
||||
})();
|
||||
@@ -0,0 +1,418 @@
|
||||
#!/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()
|
||||