第02课:需求分析

SaaS全栈开发实战 · 从零到上线

📖 课程概述

需求分析是SaaS产品成功的基石。据统计,超过60%的软件项目失败源于需求不明确或理解偏差。本课将系统讲解如何从用户痛点出发,通过科学的方法论完成SaaS产品的需求定义、优先级排序和文档化。

🎯 需求分析的黄金法则

好的需求分析不是列功能清单,而是深入理解用户要完成的任务(Jobs to Be Done)。

用户需求分析框架 ┌─────────────────────────────────────────────┐ │ Jobs to Be Done (JTBD) │ │ "当____时,我想要____,以便____" │ ├─────────────────────────────────────────────┤ │ 功能性需求 ← 用户故事 ← JTBD │ │ 非功能性需求 ← 技术约束 ← 架构决策 │ │ 约束条件 ← 业务规则 ← 合规要求 │ └─────────────────────────────────────────────┘

📝 用户故事地图

用户故事地图(User Story Map)是Jeff Patton提出的需求组织方法,它按用户旅程的时间线和任务层级组织需求:

# 用户故事地图生成器
from dataclasses import dataclass, field
from typing import List

@dataclass
class UserStory:
    """用户故事"""
    role: str          # 作为...
    action: str        # 我想要...
    benefit: str       # 以便...
    priority: int      # 1=Must, 2=Should, 3=Could, 4=Won't
    estimate: float    # 故事点
    acceptance: List[str] = field(default_factory=list)
    epic: str = ""
    
    @property
    def text(self) -> str:
        return f"作为{self.role},我想要{self.action},以便{self.benefit}"
    
    @property
    def priority_label(self) -> str:
        labels = {1: "Must Have", 2: "Should Have", 
                  3: "Could Have", 4: "Won't Have"}
        return labels.get(self.priority, "Unknown")

@dataclass
class StoryMap:
    """用户故事地图"""
    epics: dict = field(default_factory=dict)
    
    def add_story(self, story: UserStory):
        if story.epic not in self.epics:
            self.epics[story.epic] = []
        self.epics[story.epic].append(story)
    
    def get_mvp_stories(self) -> List[UserStory]:
        """获取MVP必需的故事(Must Have)"""
        mvp = []
        for epic, stories in self.epics.items():
            mvp.extend([s for s in stories if s.priority == 1])
        return sorted(mvp, key=lambda s: s.estimate)
    
    def total_effort(self, priority: int = None) -> float:
        total = 0
        for stories in self.epics.values():
            for s in stories:
                if priority is None or s.priority == priority:
                    total += s.estimate
        return total

# ✅ 验证通过 - 构建SaaS需求故事地图
story_map = StoryMap()
story_map.add_story(UserStory(
    role="新用户", action="注册账号", benefit="开始使用产品",
    priority=1, estimate=3, epic="用户管理",
    acceptance=["邮箱注册成功", "收到验证邮件", "可以登录"]
))
story_map.add_story(UserStory(
    role="用户", action="登录系统", benefit="访问我的数据",
    priority=1, estimate=2, epic="用户管理",
    acceptance=["邮箱密码登录", "记住我功能", "登录失败提示"]
))
story_map.add_story(UserStory(
    role="用户", action="重置密码", benefit="恢复账号访问",
    priority=2, estimate=2, epic="用户管理",
    acceptance=["发送重置邮件", "链接24h有效", "新密码生效"]
))

mvp = story_map.get_mvp_stories()
print(f"MVP故事数: {len(mvp)}, 工作量: {story_map.total_effort(1)}SP")

🔍 需求优先级:RICE评分模型

# RICE优先级评分系统
class RICEScorer:
    """RICE = (Reach × Impact × Confidence) / Effort"""
    
    @staticmethod
    def score(reach: int, impact: float, confidence: float, effort: float) -> float:
        if effort <= 0:
            return 0
        return (reach * impact * confidence) / effort
    
    @staticmethod
    def rank_features(features: list) -> list:
        scored = []
        for f in features:
            rice = RICEScorer.score(
                f['reach'], f['impact'], f['confidence'], f['effort']
            )
            scored.append({**f, 'rice_score': rice})
        return sorted(scored, key=lambda x: x['rice_score'], reverse=True)

# ✅ 验证通过 - RICE评分
features = [
    {'name': '用户注册登录', 'reach': 5000, 'impact': 3, 'confidence': 1.0, 'effort': 2},
    {'name': '订阅支付', 'reach': 3000, 'impact': 3, 'confidence': 0.8, 'effort': 3},
    {'name': '数据导出', 'reach': 800, 'impact': 1, 'confidence': 0.8, 'effort': 1},
    {'name': '团队协作', 'reach': 2000, 'impact': 2, 'confidence': 0.5, 'effort': 5},
    {'name': 'API接口', 'reach': 500, 'impact': 2, 'confidence': 0.8, 'effort': 2},
]
ranked = RICEScorer.rank_features(features)
for i, f in enumerate(ranked, 1):
    print(f"{i}. {f['name']}: RICE={f['rice_score']:.0f}")

📊 竞品分析框架

# 竞品分析器
class CompetitiveAnalyzer:
    def __init__(self):
        self.competitors = []
    
    def add(self, name: str, scores: dict):
        self.competitors.append({'name': name, 'scores': scores})
    
    def differentiation_opportunities(self) -> list:
        """找出差异化机会 - 竞品弱项=你的机会"""
        opps = []
        for comp in self.competitors:
            weak = [k for k, v in comp['scores'].items() if v < 5]
            if weak:
                opps.append({
                    'competitor': comp['name'],
                    'weaknesses': weak,
                    'opportunity': f"在{', '.join(weak)}上超越{comp['name']}"
                })
        return opps

# ✅ 验证通过
analyzer = CompetitiveAnalyzer()
analyzer.add("CompetitorA", {'ux': 4, 'support': 3, 'pricing': 6})
analyzer.add("CompetitorB", {'ux': 8, 'support': 7, 'pricing': 5})
for o in analyzer.differentiation_opportunities():
    print(f"💡 {o['opportunity']}")

📋 PRD文档模板

# SaaS产品需求文档(PRD)模板
PRD_TEMPLATE = """
# 产品需求文档 (PRD)

## 1. 产品概述
### 1.1 产品愿景
[一句话描述产品将成为什么]

### 1.2 目标用户
| 用户角色 | 描述 | 核心需求 |
|---------|------|---------|
| 管理员   | ...  | ...     |
| 普通用户 | ...  | ...     |

## 2. 功能需求
### Epic 1: 用户管理
- US-001: 用户注册 (Must, 3SP)
- US-002: 用户登录 (Must, 2SP)
- US-003: 密码重置 (Should, 2SP)

## 3. 非功能性需求
| 类别 | 需求 | 指标 |
|------|------|------|
| 性能 | API响应时间 | P99 < 200ms |
| 可用性 | 系统可用率 | 99.9% |
| 安全 | 数据加密 | AES-256 |

## 4. 里程碑
| 阶段 | 时间 | 交付物 |
|------|------|--------|
| MVP  | W1-W4 | 核心功能 |
| V1.0 | W5-W8 | 完整功能 |

## 5. 成功指标
- MRR目标: $10K/月
- 用户留存: D7 > 40%
- NPS: > 50
"""
print(PRD_TEMPLATE)

🚀 实战步骤

1 创建需求目录结构
mkdir -p saas-project/docs/{requirements,research,design}
cd saas-project/docs/requirements

# 创建需求追踪文件
touch user-stories.md prd.md non-functional-requirements.md
2 需求追踪系统
# 需求追踪器
import json
from datetime import datetime

class RequirementTracker:
    STATUS = ['draft', 'reviewed', 'approved', 'in_progress', 'done', 'dropped']
    
    def __init__(self):
        self.requirements = {}
    
    def add(self, req_id: str, title: str, priority: str, epic: str):
        self.requirements[req_id] = {
            'title': title, 'priority': priority, 'epic': epic,
            'status': 'draft',
            'history': [{'status': 'draft', 'date': datetime.now().isoformat()}]
        }
    
    def update_status(self, req_id: str, new_status: str):
        if new_status not in self.STATUS:
            raise ValueError(f"Invalid status: {new_status}")
        req = self.requirements[req_id]
        req['status'] = new_status
        req['history'].append({'status': new_status, 'date': datetime.now().isoformat()})
    
    def summary(self) -> dict:
        counts = {}
        for req in self.requirements.values():
            s = req['status']
            counts[s] = counts.get(s, 0) + 1
        return counts

# ✅ 验证通过
tracker = RequirementTracker()
tracker.add('US-001', '用户注册', 'Must', '用户管理')
tracker.add('US-002', '用户登录', 'Must', '用户管理')
tracker.update_status('US-001', 'approved')
print(json.dumps(tracker.summary(), indent=2))

⚠️ 需求分析常见错误

🔬 用户调研实战技巧

需求分析不只是坐在办公室想,更要走到用户中间去。以下是经过验证的调研方法:

5Why分析法

连续问5个"为什么",深挖用户需求背后的真正动机:

# 5Why分析示例
five_why_analysis = {
    "表面问题": "用户说想要导出功能",
    "Why 1": "为什么需要导出? → 需要在Excel里做分析",
    "Why 2": "为什么在Excel做? → 产品内分析不够灵活",
    "Why 3": "为什么不够灵活? → 自定义报表功能缺失",
    "Why 4": "为什么缺失? → 优先级排在后面",
    "Why 5": "为什么排在后面? → 低估了分析需求的价值",
    "真实需求": "不是导出功能,而是灵活的自定义分析能力",
    "解决方案": "构建自定义报表引擎,而非简单的CSV导出",
}

# ✅ 验证通过 - 5Why分析

用户画像(Persona)构建

# 用户画像模板
PERSONAS = {
    "sam SMB老板": {
        "角色": "小企业老板(5-20人)",
        "年龄": "30-45岁",
        "技术水平": "一般,会用基础办公软件",
        "核心痛点": "团队协作混乱,信息散落各处",
        "使用场景": "每天登录1-2次,主要看进度和审批",
        "付费意愿": "中等,$20-50/月可接受",
        "决策方式": "自己决定,但需要团队配合使用",
        "关键功能": "简单易用 > 功能强大",
    },
    "alice 产品经理": {
        "角色": "中型公司产品经理",
        "年龄": "25-35岁",
        "技术水平": "较高,熟练使用各种SaaS工具",
        "核心痛点": "跨部门协作低效,需求追踪混乱",
        "使用场景": "每天使用3-5次,重度用户",
        "付费意愿": "高,$50-100/月没问题",
        "决策方式": "推荐给团队,但需要IT审批",
        "关键功能": "集成和API能力 > 价格",
    },
}

# ✅ 验证通过 - 用户画像构建

💡 MVP验证原则:如果你的MVP不能在1周内用无代码工具搭建出来验证核心假设,说明MVP还不够小。真正的MVP只需要验证一个核心假设:用户是否愿意为解决这个痛点付费。

\n

🔬 用户调研方法详解

需求分析的核心是理解用户真实需求,而非表面需求。以下是经过实战验证的深度调研方法:

5Why分析法

连续追问5个"为什么",挖掘需求根因:

# 5Why深度分析
five_why = {
    "表面需求": "用户想要数据导出功能",
    "Why1": "为什么需要导出?→ 需要在Excel做数据分析",
    "Why2": "为什么在Excel?→ 产品内分析功能不够灵活",
    "Why3": "为什么不灵活?→ 自定义报表功能缺失",
    "Why4": "为什么缺失?→ 开发优先级排在后面",
    "Why5": "为什么排在后面?→ 低估了分析需求的价值",
    "根因": "不是需要导出,而是需要灵活的自定义分析能力",
    "解决方案": "构建可配置报表引擎,而非简单CSV导出"
}
print("5Why分析完成,识别根因:", five_why["根因"])

用户画像(Persona)构建

# 用户画像模板
class Persona:
    def __init__(self, name, role, age_range, tech_level, pain_point,
                 usage_pattern, willingness_to_pay, decision_style):
        self.name = name
        self.role = role
        self.age_range = age_range
        self.tech_level = tech_level
        self.pain_point = pain_point
        self.usage_pattern = usage_pattern
        self.willingness_to_pay = willingness_to_pay
        self.decision_style = decision_style

sam = Persona(
    name="Sam", role="小企业老板(5-20人)",
    age_range="30-45", tech_level="一般",
    pain_point="团队协作混乱,信息散落各处",
    usage_pattern="每天1-2次,看进度和审批",
    willingness_to_pay="$20-50/月",
    decision_style="自己决定,需要团队配合使用"
)

alice = Persona(
    name="Alice", role="中型公司产品经理",
    age_range="25-35", tech_level="较高",
    pain_point="跨部门协作低效,需求追踪混乱",
    usage_pattern="每天3-5次,重度用户",
    willingness_to_pay="$50-100/月",
    decision_style="推荐给团队,需要IT审批"
)

print(f"用户画像1: {sam.name} - {sam.pain_point}")
print(f"用户画像2: {alice.name} - {alice.pain_point}")

用户旅程地图

用户旅程: 从认知到付费 认知 → 评估 → 试用 → 付费 → 留存 → 推荐 │ │ │ │ │ │ 搜索 对比 注册 升级 深度 邀请 广告 试用 首次使用 支付 使用 朋友 推荐 看评论 探索功能 订阅 依赖 同事 关键触点: - 认知: SEO/内容营销/社交媒体 - 评估: 定价页/功能对比/案例 - 试用: Onboarding引导/Aha Moment - 付费: 无摩擦升级/多种支付 - 留存: 邮件培育/功能深度引导 - 推荐: 推荐奖励/社区建设

💡 MVP验证原则:如果MVP不能在1周内用无代码工具搭建验证核心假设,说明MVP还不够小。真正的MVP只需验证:用户是否愿意为解决痛点付费。

📐 需求文档自动化

# 需求文档自动生成器
class PRDGenerator:
    """从用户故事自动生成PRD文档"""
    
    def __init__(self, project_name: str, version: str = "1.0"):
        self.project_name = project_name
        self.version = version
        self.epics = {}
        self.nfrs = []
    
    def add_epic(self, name: str, stories: list):
        self.epics[name] = stories
    
    def add_nfr(self, category: str, requirement: str, metric: str):
        self.nfrs.append({
            'category': category,
            'requirement': requirement,
            'metric': metric
        })
    
    def generate(self) -> str:
        doc = f"# {self.project_name} PRD v{self.version}\n\n"
        doc += "## 功能需求\n"
        for epic, stories in self.epics.items():
            doc += f"\n### {epic}\n"
            for s in stories:
                doc += f"- {s}\n"
        
        doc += "\n## 非功能性需求\n"
        doc += "| 类别 | 需求 | 指标 |\n|------|------|------|\n"
        for nfr in self.nfrs:
            doc += f"| {nfr['category']} | {nfr['requirement']} | {nfr['metric']} |\n"
        
        return doc

# ✅ 验证通过
gen = PRDGenerator("SaaS Platform")
gen.add_epic("用户管理", ["US-001: 注册(Must)", "US-002: 登录(Must)"])
gen.add_nfr("性能", "API响应时间", "P99 < 200ms")
print(gen.generate())

🏆 课程成就

完成本课后,你已解锁:

JTBD需求分析 用户故事地图 RICE优先级 竞品分析 PRD文档

✅ 你现在能用系统化方法定义SaaS产品需求了!