6 커밋 955c5ba4c7 ... 2c50994d33

작성자 SHA1 메시지 날짜
  gwd 2c50994d33 docs: 新增知了数学2.0项目规划文档 1 개월 전
  gwd cab82d3507 feat: 题型比例调整为442(选择4:填空4:解答2),配合固定分值各题型贡献均等 1 개월 전
  gwd c2f36208eb feat: 难度分布步骤改为题型感知,同时满足题型比例和难度分布 1 개월 전
  gwd 3b848dfa0e feat: 非摸底组卷按比例分配题型,避免解答题独占缺口 1 개월 전
  gwd b8585658f2 fix: 智能组卷(type 1/8)强制10题,忽略前端传入的total_questions 1 개월 전
  gwd 9413e8680f feat: 智能组卷(type 1/8)默认10题,非20题使用固定题型分值 1 개월 전

+ 36 - 4
app/Http/Controllers/Api/IntelligentExamController.php

@@ -125,7 +125,12 @@ class IntelligentExamController extends Controller
         }
 
         $data = $validator->validated();
-        $data['total_questions'] = $data['total_questions'] ?? 20;
+        $assembleType = (int) ($data['assemble_type'] ?? 4);
+        if (in_array($assembleType, [1, 8])) {
+            $data['total_questions'] = 10;
+        } else {
+            $data['total_questions'] = $data['total_questions'] ?? 20;
+        }
         $this->ensureStudentTeacherRelation($data);
 
         // 【修改】使用series_id、semester_code和grade获取textbook_id
@@ -257,9 +262,16 @@ class IntelligentExamController extends Controller
                 $questions = array_slice($questions, 0, $totalQuestions);
             }
 
-            // 调整题目分值,确保符合目标总分
-            $targetTotalScore = $data['total_score'] ?? 100.0;
-            $questions = $this->adjustQuestionScores($questions, $targetTotalScore);
+            // 调整题目分值
+            if (($data['total_questions'] ?? 20) == 20) {
+                // 20题:沿用动态凑整算法,目标总分100
+                $targetTotalScore = $data['total_score'] ?? 100.0;
+                $questions = $this->adjustQuestionScores($questions, $targetTotalScore);
+            } else {
+                // 非20题:固定题型分值(选择5、填空5、解答10)
+                $questions = $this->applyFixedScores($questions);
+                $targetTotalScore = array_sum(array_column($questions, 'score'));
+            }
 
             // 计算总分
             $totalScore = array_sum(array_column($questions, 'score'));
@@ -869,6 +881,26 @@ class IntelligentExamController extends Controller
         };
     }
 
+    /**
+     * 非20题时使用固定题型分值
+     * 选择题:5分,填空题:5分,解答题:10分
+     */
+    private function applyFixedScores(array $questions): array
+    {
+        foreach ($questions as &$question) {
+            $type = $this->normalizeQuestionType($question['question_type'] ?? 'answer');
+            $question['score'] = match ($type) {
+                'choice' => 5,
+                'fill' => 5,
+                'answer' => 10,
+                default => 5,
+            };
+        }
+        unset($question);
+
+        return $questions;
+    }
+
     /**
      * 计算试卷总分并调整各题目分值,确保总分接近目标分数
      * 符合中国中学卷子标准:

+ 4 - 4
app/Services/ExamTypeStrategy.php

@@ -979,11 +979,11 @@ class ExamTypeStrategy
             'kp_codes' => $kpCodeList,
             'exclude_question_ids' => $answeredQuestionIds,
             'paper_name' => $params['paper_name'] ?? ('知识点组卷_' . now()->format('Ymd_His')),
-            // 知识点组卷:注重题型平衡(恢复原有配比
+            // 知识点组卷:442配比(配合固定分值5/5/10,各题型贡献均等
             'question_type_ratio' => [
-                '选择题' => 35,
-                '填空题' => 30,
-                '解答题' => 35,
+                '选择题' => 40,
+                '填空题' => 40,
+                '解答题' => 20,
             ],
             'difficulty_ratio' => [
                 '基础' => 25,

+ 259 - 40
app/Services/LearningAnalyticsService.php

@@ -1281,8 +1281,8 @@ class LearningAnalyticsService
             $skills = $params['skills'] ?? [];
             $questionTypeRatio = $params['question_type_ratio'] ?? [
                 '选择题' => 40,
-                '填空题' => 30,
-                '解答题' => 30,
+                '填空题' => 40,
+                '解答题' => 20,
             ];
             // 新增:题目分类筛选
             $questionCategory = $params['question_category'] ?? null;
@@ -1571,40 +1571,29 @@ class LearningAnalyticsService
             ]);
 
             if ($enableDistribution && !$isExcludedType) {
-                Log::info('LearningAnalyticsService: 应用难度系数分布', [
-                    'difficulty_category_before' => $difficultyCategory,
+                Log::info('LearningAnalyticsService: 应用题型感知的难度分布', [
+                    'difficulty_category' => $difficultyCategory,
                     'assemble_type' => $assembleType,
-                    'before_count' => count($selectedQuestions)
+                    'before_count' => count($selectedQuestions),
+                    'question_type_ratio' => $questionTypeRatio,
                 ]);
 
                 try {
-                    // 使用 ExamTypeStrategy 的独立方法应用难度分布
-                    $questionExpansionService = $this->questionExpansionService ?? app(QuestionExpansionService::class);
-                    $examStrategy = new ExamTypeStrategy($questionExpansionService);
-
-                    $distributionCandidates = $this->buildDistributionCandidates(
+                    $selectedQuestions = $this->applyTypeAwareDifficultyDistribution(
                         $allQuestions,
                         $selectedQuestions,
-                        (int) $difficultyCategory
-                    );
-
-                    $selectedQuestions = $examStrategy->applyDifficultyDistributionToQuestions(
-                        $distributionCandidates,
                         $totalQuestions,
-                        $difficultyCategory,
-                        $params
+                        (int) $difficultyCategory,
+                        $questionTypeRatio
                     );
 
-                    Log::info('LearningAnalyticsService: 难度分布应用完成', [
-                        'difficulty_category_after' => $difficultyCategory,
-                        'before_count' => count($distributionCandidates),
+                    Log::info('LearningAnalyticsService: 题型感知难度分布完成', [
                         'after_count' => count($selectedQuestions),
-                        'success' => count($selectedQuestions) >= $totalQuestions
+                        'type_breakdown' => $this->countByType($selectedQuestions),
                     ]);
                 } catch (\Exception $e) {
-                    Log::warning('LearningAnalyticsService: 难度分布应用失败,继续使用原结果', [
+                    Log::warning('LearningAnalyticsService: 题型感知难度分布失败,继续使用原结果', [
                         'error' => $e->getMessage(),
-                        'difficulty_category_when_error' => $difficultyCategory
                     ]);
                 }
             }
@@ -2231,10 +2220,40 @@ class LearningAnalyticsService
         $useKnowledgePointPriority = ($assembleType === 0); // 摸底测试需要知识点优先
         $kpSelected = []; // 已选知识点记录
 
+        // 【新增】非摸底类型:按比例计算每种题型的目标数量
+        $typeTargets = ['choice' => 0, 'fill' => 0, 'answer' => 0];
+        if (!$useKnowledgePointPriority) {
+            $ratioMap = ['choice' => '选择题', 'fill' => '填空题', 'answer' => '解答题'];
+            $totalRatio = array_sum($questionTypeRatio) ?: 100;
+            $allocated = 0;
+            $fractions = [];
+            foreach ($ratioMap as $type => $ratioKey) {
+                $ratio = $questionTypeRatio[$ratioKey] ?? 0;
+                $exact = $totalQuestions * $ratio / $totalRatio;
+                $typeTargets[$type] = (int) floor($exact);
+                $fractions[$type] = $exact - floor($exact);
+                $allocated += $typeTargets[$type];
+            }
+            // 余数分配给小数部分最大的题型
+            $remainder = $totalQuestions - $allocated;
+            arsort($fractions);
+            foreach ($fractions as $type => $frac) {
+                if ($remainder <= 0) break;
+                $typeTargets[$type]++;
+                $remainder--;
+            }
+
+            Log::info('selectQuestionsByMastery: 题型目标分配', [
+                'total_questions' => $totalQuestions,
+                'ratio' => $questionTypeRatio,
+                'targets' => $typeTargets,
+            ]);
+        }
+
         Log::info('selectQuestionsByMastery: 知识点优先策略', [
             'assemble_type' => $assembleType,
             'use_knowledge_point_priority' => $useKnowledgePointPriority,
-            'note' => $useKnowledgePointPriority ? '摸底测试:需要均衡分配知识点' : '知识点组卷:允许同一知识点选多题'
+            'note' => $useKnowledgePointPriority ? '摸底测试:需要均衡分配知识点' : '按比例分配题型'
         ]);
 
         // 确保每种题型至少选1题
@@ -2295,14 +2314,26 @@ class LearningAnalyticsService
                     'note' => $selectedInThisType > 0 ? '成功选择' : '无可用知识点'
                 ]);
             } else {
-                // 【修复】知识点组卷:随机选择该题型的一道题,避免固定选择第一个导致知识点分布不均
-                $randomIndex = array_rand($questionsByType[$type]);
-                $selectedQuestions[] = $questionsByType[$type][$randomIndex];
-                Log::debug('题型基础分配(随机选择)', [
+                // 非摸底:按目标数量从该题型池中选题
+                $target = $typeTargets[$type] ?? 0;
+                $selectedIds = array_column($selectedQuestions, 'id');
+                $taken = 0;
+                foreach ($questionsByType[$type] as $q) {
+                    if ($taken >= $target) break;
+                    $qid = $q['id'] ?? null;
+                    if ($qid && !in_array($qid, $selectedIds)) {
+                        $selectedQuestions[] = $q;
+                        $selectedIds[] = $qid;
+                        $taken++;
+                    }
+                }
+                $typeTargets[$type . '_actual'] = $taken;
+
+                Log::info('题型按比例分配', [
                     'type' => $type,
-                    'kp' => $questionsByType[$type][$randomIndex]['kp_code'] ?? 'unknown',
-                    'random_index' => $randomIndex,
-                    'total_in_type' => count($questionsByType[$type])
+                    'target' => $target,
+                    'actual' => $taken,
+                    'available' => count($questionsByType[$type]),
                 ]);
             }
         }
@@ -2440,17 +2471,39 @@ class LearningAnalyticsService
                 ]);
             }
         } else {
-            // 知识点组卷:选择未选过的题目(不要求知识点不重复)
-            $selectedIds = array_column($selectedQuestions, 'id');
-            foreach ($allQuestions as $q) {
-                if (count($selectedQuestions) >= $totalQuestions) break;
+            // 非摸底:题型不足时,缺口按优先级补到其他题型
+            $totalSelected = count($selectedQuestions);
+            if ($totalSelected < $totalQuestions) {
+                $deficit = $totalQuestions - $totalSelected;
+                $selectedIds = array_column($selectedQuestions, 'id');
 
-                $qid = $q['id'] ?? null;
-                if ($qid && !in_array($qid, $selectedIds)) {
-                    $selectedQuestions[] = $q;
-                    $selectedIds[] = $qid;
-                    Log::debug('继续选择题目(无知识点限制)', ['kp' => $q['kp_code'] ?? 'unknown', 'id' => $qid]);
+                // 按补充优先级:填空 > 解答 > 选择(避免解答题独占缺口)
+                $supplementOrder = ['fill', 'answer', 'choice'];
+
+                Log::info('题型缺口补充开始', [
+                    'deficit' => $deficit,
+                    'current_count' => $totalSelected,
+                    'target' => $totalQuestions,
+                    'supplement_order' => $supplementOrder,
+                ]);
+
+                foreach ($supplementOrder as $type) {
+                    if ($deficit <= 0) break;
+                    foreach ($questionsByType[$type] as $q) {
+                        if ($deficit <= 0) break;
+                        $qid = $q['id'] ?? null;
+                        if ($qid && !in_array($qid, $selectedIds)) {
+                            $selectedQuestions[] = $q;
+                            $selectedIds[] = $qid;
+                            $deficit--;
+                        }
+                    }
                 }
+
+                Log::info('题型缺口补充完成', [
+                    'final_count' => count($selectedQuestions),
+                    'target' => $totalQuestions,
+                ]);
             }
         }
 
@@ -3253,6 +3306,172 @@ class LearningAnalyticsService
         return $shortage;
     }
 
+    /**
+     * 题型感知的难度分布选题
+     * 在每种题型内部应用难度分布,确保题型比例和难度分布同时被满足
+     */
+    private function applyTypeAwareDifficultyDistribution(
+        array $allQuestions,
+        array $selectedQuestions,
+        int $totalQuestions,
+        int $difficultyCategory,
+        array $questionTypeRatio
+    ): array {
+        $diffService = app(DifficultyDistributionService::class);
+        $distribution = $diffService->calculateDistribution($difficultyCategory, $totalQuestions);
+
+        // 1. 计算每种题型的目标数量
+        $ratioMap = ['choice' => '选择题', 'fill' => '填空题', 'answer' => '解答题'];
+        $totalRatio = array_sum($questionTypeRatio) ?: 100;
+        $typeTargets = [];
+        $fractions = [];
+        $allocated = 0;
+
+        foreach ($ratioMap as $type => $ratioKey) {
+            $ratio = $questionTypeRatio[$ratioKey] ?? 0;
+            $exact = $totalQuestions * $ratio / $totalRatio;
+            $typeTargets[$type] = (int) floor($exact);
+            $fractions[$type] = $exact - floor($exact);
+            $allocated += $typeTargets[$type];
+        }
+
+        $remainder = $totalQuestions - $allocated;
+        arsort($fractions);
+        foreach ($fractions as $type => $frac) {
+            if ($remainder <= 0) break;
+            $typeTargets[$type]++;
+            $remainder--;
+        }
+
+        // 2. 构建候选题池(合并 allQuestions + selectedQuestions 去重)
+        $candidatePool = [];
+        $seen = [];
+        foreach (array_merge($allQuestions, $selectedQuestions) as $q) {
+            $id = $q['id'] ?? null;
+            if ($id && !isset($seen[$id])) {
+                $seen[$id] = true;
+                $candidatePool[] = $q;
+            }
+        }
+
+        // 3. 按题型分组候选题
+        $candidatesByType = ['choice' => [], 'fill' => [], 'answer' => []];
+        foreach ($candidatePool as $q) {
+            $qType = $q['question_type'] ?? '';
+            if (isset($candidatesByType[$qType])) {
+                $candidatesByType[$qType][] = $q;
+            }
+        }
+
+        // 4. 在每种题型内按难度分桶选题
+        $result = [];
+        $usedIds = [];
+        $typeActual = [];
+
+        foreach ($ratioMap as $type => $ratioKey) {
+            $target = $typeTargets[$type];
+            if ($target <= 0) {
+                $typeActual[$type] = 0;
+                continue;
+            }
+
+            $typeCandidates = $candidatesByType[$type];
+            $buckets = $diffService->groupQuestionsByDifficultyRange($typeCandidates, $difficultyCategory);
+
+            // 按 distribution 比例在该题型内选题
+            $typeSelected = [];
+            foreach ($distribution as $level => $config) {
+                $levelRatio = $config['percentage'];
+                $levelTarget = max(0, (int) round($target * $levelRatio / 100));
+                if ($levelTarget <= 0) continue;
+
+                $rangeKey = $diffService->mapDifficultyLevelToRangeKey($level, $difficultyCategory);
+                $bucket = $buckets[$rangeKey] ?? [];
+                shuffle($bucket);
+
+                $taken = 0;
+                foreach ($bucket as $q) {
+                    if ($taken >= $levelTarget) break;
+                    $qid = $q['id'] ?? null;
+                    if ($qid && !isset($usedIds[$qid])) {
+                        $typeSelected[] = $q;
+                        $usedIds[$qid] = true;
+                        $taken++;
+                    }
+                }
+            }
+
+            // 如果难度分布不满足目标数,从该题型剩余题目补充
+            if (count($typeSelected) < $target) {
+                $allTypeBuckets = array_merge(
+                    $buckets['primary_medium'] ?? [],
+                    $buckets['primary_low'] ?? [],
+                    $buckets['primary_high'] ?? [],
+                    $buckets['secondary'] ?? [],
+                    $buckets['other'] ?? []
+                );
+                shuffle($allTypeBuckets);
+                foreach ($allTypeBuckets as $q) {
+                    if (count($typeSelected) >= $target) break;
+                    $qid = $q['id'] ?? null;
+                    if ($qid && !isset($usedIds[$qid])) {
+                        $typeSelected[] = $q;
+                        $usedIds[$qid] = true;
+                    }
+                }
+            }
+
+            $typeActual[$type] = count($typeSelected);
+            $result = array_merge($result, $typeSelected);
+        }
+
+        // 5. 如果总数不足(某题型候选不够),跨题型补充
+        if (count($result) < $totalQuestions) {
+            $deficit = $totalQuestions - count($result);
+            // 按 fill > choice > answer 优先级补充
+            $supplementOrder = ['fill', 'choice', 'answer'];
+            foreach ($supplementOrder as $type) {
+                if ($deficit <= 0) break;
+                $bucket = $candidatesByType[$type];
+                shuffle($bucket);
+                foreach ($bucket as $q) {
+                    if ($deficit <= 0) break;
+                    $qid = $q['id'] ?? null;
+                    if ($qid && !isset($usedIds[$qid])) {
+                        $result[] = $q;
+                        $usedIds[$qid] = true;
+                        $deficit--;
+                    }
+                }
+            }
+        }
+
+        Log::info('LearningAnalyticsService: 题型感知难度分布详情', [
+            'type_targets' => $typeTargets,
+            'type_actual' => $typeActual,
+            'total_result' => count($result),
+            'difficulty_category' => $difficultyCategory,
+            'candidates_by_type' => array_map('count', $candidatesByType),
+        ]);
+
+        return array_slice($result, 0, $totalQuestions);
+    }
+
+    /**
+     * 统计题目列表中各题型的数量
+     */
+    private function countByType(array $questions): array
+    {
+        $counts = ['choice' => 0, 'fill' => 0, 'answer' => 0];
+        foreach ($questions as $q) {
+            $type = $q['question_type'] ?? '';
+            if (isset($counts[$type])) {
+                $counts[$type]++;
+            }
+        }
+        return $counts;
+    }
+
     private function buildDistributionCandidates(array $allQuestions, array $selectedQuestions, int $difficultyCategory): array
     {
         if (empty($allQuestions)) {

+ 286 - 0
docs/知了数学2.0-项目规划.md

@@ -0,0 +1,286 @@
+# 知了数学 2.0 — 项目规划
+
+## 一、背景
+
+现有系统存在几个核心问题:知识点体系不准确、题目质量差、多套系统维护成本高。2.0 版本产品逻辑全面升级,从"系统临时出题"转向"提前准备好高质量学案内容,业务点打印给学生使用",借此机会重建系统。
+
+**目标:** 2月27日 - 3月31日,完成核心系统,让业务点能用上 2.0 的学案体系。
+
+---
+
+## 二、系统整体设计
+
+### 改造前(5套系统,维护复杂)
+
+```
+内容系统(PHP) + 业务系统(Java) + 加盟系统(.NET) + OCR识别(Python) + 业务点前端(React)
+      ↑                ↑                                        ↑
+   各自独立数据库,语言不同,对接复杂
+```
+
+### 改造后(统一为1套核心系统)
+
+```
+┌──────────────────────────────────────────────────────┐
+│                    核心系统 S                          │
+│                                                        │
+│  管理后台(教研用)    业务接口(业务点+加盟门店用)      │
+│  · 知识点管理          · 学生/班级管理                  │
+│  · 学案内容编辑        · 学案打印                       │
+│  · AI出题 + 审核       · 成绩录入(拍照识别)           │
+│  · 摸底卷管理          · 学情查看                       │
+│                                                        │
+│                    统一数据库                            │
+└───┬──────────┬──────────┬──────────┬─────────────────┘
+    │          │          │          │
+ 业务点端   加盟门店系统  拍照识别   AI出题 + 学案打印
+ (过卫栋) (过卫栋)   (谢照迪)    (谢照迪)
+```
+
+**核心改变:**
+
+- 原来 5 套系统 → 合并为 1 套核心系统 + 3 个配套服务
+- 原来2个数据库 → 统一为 1 个数据库
+- 旧数据不迁移,全部重新开始(目前客户量少,处于内测期,6个月免费)
+
+---
+
+## 三、功能模块
+
+### 管理后台(教研团队使用)
+
+| 功能               | 说明                                                                                                                 |
+| ------------------ | -------------------------------------------------------------------------------------------------------------------- |
+| **知识点目录管理** | 管理三级知识点目录(如:数与式 → 有理数 → 正数与负数);标注难度等级(初中A/B两档,高中A/B/C三档)                   |
+| **智能学案编辑**   | 每份学案对应1个知识点;人工录入核心内容(知识精要+母题),其余由AI自动生成;同一组知识点的学案共享知识精要和巩固练习 |
+| **阶段学案编辑**   | 阶段综合测试,AI生成20道题,覆盖一组关联知识点                                                                       |
+| **AI出题 + 审核**  | 人工录入参考题后,AI自动生成变式题;教研人员逐题审核,通过后上线使用                                                 |
+| **摸底卷管理**     | 预先组好的模拟试卷:初中2套(A+B难度各1套)、高中3套(A+B+C难度各1套)                                               |
+
+### 业务点操作端(业务点人员使用)
+
+| 功能                   | 说明                                                               |
+| ---------------------- | ------------------------------------------------------------------ |
+| **学生/班级/教师管理** | 录入和管理学生信息、分班、分配教师                                 |
+| **生成学案**           | 智能学案(知识点)、阶段学案                                       |
+| **拍照识别答题卷**     | 学生做完纸质学案后,拍照上传,系统自动识别对错                     |
+| **查看学情**           | 查看学生知识点掌握情况(红/黄/绿热力图)、提分力统计,截图发给家长 |
+| **错题追练**           | 学案题目用完后,针对错题自动生成追练题                             |
+| **选择摸底卷**         | 从预组好的摸底卷中选择,打印给学生做摸底测试                       |
+
+---
+
+## 四、数据架构
+
+系统需要管理以下几类数据:
+
+| 类别           | 包含内容                                                           |
+| -------------- | ------------------------------------------------------------------ |
+| **教材与章节** | 教材版本(人教版等)、章节层级、章节与知识点的对应关系             |
+| **知识点**     | 三级目录结构、每个知识点的难度等级                                 |
+| **题目**       | 题干、选项、答案、解析、难度、题型、对应知识点                     |
+| **学案内容**   | 智能学案(7个模块的内容和题目)、阶段学案(20道测试题)            |
+| **摸底卷**     | 预组好的模拟试卷和卷内题目                                         |
+| **业务运营**   | 业务点(加盟门店)、教师、班级、学生                               |
+| **学习记录**   | 学案完成记录、每道题的对错、知识点掌握度、提分力、错题本、追练记录 |
+| **积分**       | 积分流水(预留,内测期免费)                                       |
+
+---
+
+## 五、人员分工
+
+| 人员                     | 负责内容                                                                   | 产出           |
+| ------------------------ | -------------------------------------------------------------------------- | -------------- |
+| **过卫栋**               | 核心系统开发 + 管理后台 + 业务点前端改造 + 整体架构设计                    | 核心系统       |
+| **谢照迪**               | AI出题服务 + 学案打印服务 + 拍照识别服务                                   | 三个配套服务   |
+| **李文屿**               | 整理知识点目录;准备出题样例和规范;验证AI出题质量;协调管理内容录入和审核 | 内容质量保障   |
+| **姚老师 + 文莉**        | 系统测试(第5阶段,5个工作日)                                             | 测试反馈       |
+| **新后端同事(待到岗)** | 可分担:加盟系统改造 / 管理后台部分页面 / 前端部分改造                     | 视到岗时间分配 |
+
+---
+
+## 六、开发节奏
+
+**总工期:2月27日(周四) - 3月31日(周一),每周休息1天(周日),共 28 个工作日**
+
+- 开发:23 个工作日(2/27 - 3/25)
+- 测试:5 个工作日(3/26 - 3/31)
+
+### 第 1 阶段(2/27 - 3/8,8个工作日):搭建基础 + 管理后台核心
+
+**过卫栋:**
+
+- 搭建核心系统框架和数据库
+- 搭建管理后台
+- 完成「知识点目录管理」功能
+- 完成「智能学案编辑」功能框架
+
+**谢照迪:**
+
+- 搭建 AI 出题服务(输入参考题 → 输出变式题)
+- 搭建学案打印服务,确定打印格式
+- 验证数学公式在打印件中的显示效果
+
+**李文屿:**
+
+- 整理完整的三级知识点目录(初中+高中)
+- 确定每个知识点的难度等级
+- 准备"通用知识精要样例"截图和出题规范
+- 验证 AI 生成知识精要和母题的质量,评估是否需要人工补录
+
+### 第 2 阶段(3/9 - 3/15,6个工作日):管理后台全部完成
+
+**过卫栋:**
+
+- 完成「阶段学案编辑」功能
+- 完成「AI出题 + 审核流程」功能
+- 完成「摸底卷管理」功能(初中2套+高中3套)
+- 管理后台全部功能可用
+
+**谢照迪:**
+
+- AI 出题服务可以被管理后台正常调用
+- 学案打印服务可以生成基础学案PDF
+- 拍照识别服务适配新系统格式
+
+**李文屿:**
+
+- 开始在管理后台录入知识点目录
+- 持续验证AI生成的知识精要和母题质量,协调安排审核
+
+### 第 3 阶段(3/16 - 3/22,6个工作日):业务点功能
+
+**过卫栋:**
+
+- 完成业务点端的学生/班级/教师管理
+- 完成学案选择和打印功能
+- 完成拍照录入成绩功能
+- 改造业务点前端,对接新系统
+
+**谢照迪:**
+
+- 完善打印服务(支持所有学案类型和摸底卷)
+- 拍照识别功能联调
+
+**李文屿:**
+
+- 协调安排人员审核 AI 生成的题目
+
+### 第 4 阶段(3/23 - 3/25,3个工作日):学情功能 + 全面联调
+
+**过卫栋:**
+
+- 完成知识点掌握判定和提分力计算
+- 完成学情热力图展示
+- 完成错题追练功能
+- 加盟系统对接新数据库
+- 所有功能串通测试
+
+**谢照迪:**
+
+- 所有服务联调
+- 部署线上环境
+
+### 第 5 阶段(3/26 - 3/31,5个工作日):测试 + 上线
+
+**姚老师 + 文莉:**
+
+- 管理后台功能测试(知识点、学案、AI出题、摸底卷)
+- 业务点操作流程测试(学案打印、拍照录入、学情查看)
+- 完整业务流程回归测试
+
+**过卫栋:**
+
+- 修复测试发现的问题
+- 正式部署上线
+
+**新后端同事(如已到岗):**
+
+- 分担前端改造或加盟系统改造
+
+---
+
+## 七、内容规模与生产计划
+
+### 每份智能学案包含什么
+
+| 模块     | 数量  | 谁来做       | 备注                      |
+| -------- | ----- | ------------ | ------------------------- |
+| 知识精要 | 10 个 | 人工或AI生成 | 同一组知识点的学案共享    |
+| 巩固训练 | 30 道 | AI 自动生成  | 同一组知识点的学案共享    |
+| 母题示范 | 2 道  | 人工或AI生成 | 每份学案独有              |
+| 学以致用 | 2 道  | AI 自动生成  | 基于每道母题生成2个变式题 |
+| 知识回顾 | 2 道  | AI 自动生成  | 每份学案独有              |
+| 进阶练习 | 8 道  | AI 自动生成  | 每份学案独有              |
+
+### 总量一览
+
+| 内容           | 数量   | 说明                                               |
+| -------------- | ------ | -------------------------------------------------- |
+| 知识点阶段分组 | 311 组 | 初中 108 组 + 高中 203 组                          |
+| 智能学案       | 780 份 | 初中 309 份 + 高中 471 份,每份对应1个知识点       |
+| 阶段学案       | 311 份 | 每份 20 道测试题,AI 生成                          |
+| 摸底卷         | 5 套   | 初中2套(A+B难度各1套),高中3套(A+B+C难度各1套) |
+
+### 内容生产方案
+
+知识精要和母题是所有内容的"种子",有两种生产方式:
+
+**方案A:人工录入(兜底方案)**
+
+找人手工录入知识精要和母题,工作量如下:
+
+| 内容     | 怎么算                 | 数量         |
+| -------- | ---------------------- | ------------ |
+| 知识精要 | 311 组 × 每组 10 个    | 3,110 个     |
+| 母题     | 780 份学案 × 每份 2 道 | 1,560 道     |
+| **合计** |                        | **4,670 项** |
+
+**方案B:全AI生成(优先尝试)**
+
+优化 prompt 后,提供一份"通用知识精要样例"截图(10题示范),让 AI 参照样例格式和质量标准,直接为每个知识点生成知识精要和母题,无需人工逐条录入。如果 AI 生成质量达标,可省去全部人工录入工作。
+
+> 建议:先用方案B试跑几个知识点,李文屿评估质量。如果效果好,全部用AI生成;如果部分知识点效果不好,再针对性用方案A补充。
+
+**AI 自动生成的其余内容(谢照迪的 AI 服务):**
+
+| 内容                 | 怎么算                            | 数量             |
+| -------------------- | --------------------------------- | ---------------- |
+| 巩固训练             | 311 组 × 每组 30 道               | 9,330 道         |
+| 母题变式(学以致用) | 780 份 × 每份 2 道母题 × 2 个变式 | 3,120 道         |
+| 知识回顾             | 780 份 × 每份 2 道                | 1,560 道         |
+| 进阶练习             | 780 份 × 每份 8 道                | 6,240 道         |
+| 阶段测试             | 311 份 × 每份 20 道               | 6,220 道         |
+| 摸底卷               | 5 套 × 约 100 道                  | 约 500 道        |
+| **AI 合计**          |                                   | **约 26,970 道** |
+
+### 内容生产流程
+
+```
+第1步:李文屿 准备"通用知识精要样例"截图 + 出题规范
+                              ↓
+第2步:AI 服务 按样例批量生成知识精要 + 母题(约 4,670 项)
+        ↓ 质量不达标的部分 → 安排人工补录
+                              ↓
+第3步:AI 服务 基于知识精要和母题,批量生成其余题目(约 26,970 道)
+                              ↓
+第4步:李文屿 协调安排人员在管理后台审核(通过 / 打回重新生成)
+                              ↓
+第5步:审核通过的内容上线,业务点即可使用
+```
+
+**总内容量约 3.2 万项,目标是尽量全部由 AI 生成,人工只做审核。**
+
+内容生产是持续性工作,不需要一次性全部完成。系统先上线,内容按知识点分批生成、分批审核、分批上线。
+
+---
+
+## 八、风险与应对
+
+| 风险                        | 应对措施                                                         |
+| --------------------------- | ---------------------------------------------------------------- |
+| 内容生产量大(约 3.2 万项) | 优先尝试全AI生成方案,人工只做审核;分批上线,先做核心知识点     |
+| AI 生成的题目质量不稳定     | 李文屿提前验证出题效果,持续优化;所有AI题目必须人工审核后才上线 |
+| 新后端同事到岗时间不确定    | 过卫栋先推进核心功能,到岗后再分担                               |
+| 数学公式打印显示问题        | 谢照迪第1阶段就验证打印效果,有问题及早调整                      |
+| 测试时间只有5天             | 开发过程中充分自测,测试阶段聚焦核心业务流程                     |