Browse Source

📝 docs(story): 创建故事018.001 - 修复残疾人照片上传保存功能

- 创建新的018.001故事:修复残疾人照片上传保存功能
- 包含完整的技术上下文、数据模型、组件结构
- 提供详细的任务分解和排查检查清单
- 状态设置为Approved,准备开始开发

🤖 Generated with [Claude Code](https://claude.com/claude-code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
yourname 2 weeks ago
parent
commit
df0450b59b

+ 0 - 256
docs/stories/018.001.refactor-mini-charts-components.story.md

@@ -1,256 +0,0 @@
-# 故事 018.001 - 规整 mini-charts 组件
-
-## 元数据
-| 字段 | 值 |
-|------|-----|
-| **史诗** | 018 |
-| **故事ID** | 018.001 |
-| **标题** | 规整 mini-charts 组件 |
-| **状态** | In Progress |
-| **优先级** | 高 |
-| **故事点** | 5 |
-| **项目负责人** | James (Claude Code) |
-
-## 故事描述
-规整 `mini-ui-packages/mini-charts/src/components` 中的图表组件,统一使用 `BaseChartOriginal2D` 作为基础组件,删除冗余的旧组件,并在 `yongren-statistics-ui` 中更新使用新组件。
-
-## 背景
-当前 `mini-charts` 包中存在多个版本的图表组件:
-- **BaseChartOriginal2D** - 基于原始 u-charts.js + Canvas 2D API 的最新实现
-- **BaseChart** - 模块化TypeScript版本
-- **BaseChartLegacy** - 旧Canvas API版本
-- **ColumnChartOriginal2D** - 基于 BaseChartOriginal2D 的柱状图(参考实现)
-- **ColumnChart** - 基于 BaseChart 的柱状图
-- **ColumnChartLegacy** - 基于 BaseChartLegacy 的柱状图
-- **ColumnChartFCExample** - 文档示例组件
-- **BarChart、PieChart、LineChart** - 基于 BaseChart 的其他图表
-
-这导致代码冗余、维护困难,需要统一技术栈。
-
-## 目标
-1. 统一所有图表组件使用 `BaseChart` 作为基础(原 BaseChartOriginal2D 重命名)
-2. 删除冗余的旧组件(BaseChart、BaseChartLegacy、ColumnChart、ColumnChartLegacy等)
-3. 更新 `yongren-statistics-ui` 使用新组件
-4. 保持API一致性,方便使用
-
-## 重构策略
-1. **BaseChartOriginal2D → BaseChart**: 删除旧的 BaseChart.tsx 后,将 BaseChartOriginal2D.tsx 重命名为 BaseChart.tsx
-2. **其他组件**: 创建 ColumnChart、BarChart、PieChart、LineChart,基于新的 BaseChart
-3. **保持内部一致性**: 所有组件都使用统一的 BaseChart 作为基础
-
-## 验收标准
-### 功能性验收
-- [ ] BaseChartOriginal2D.tsx 重命名为 BaseChart.tsx(替换旧的 BaseChart.tsx)
-- [ ] 所有图表组件(ColumnChart、BarChart、PieChart、LineChart)使用新的 BaseChart
-- [ ] 删除 BaseChartLegacy.tsx、ColumnChart.tsx、ColumnChartLegacy.tsx
-- [ ] yongren-statistics-ui 的 Statistics.tsx 使用新组件且功能正常
-- [ ] 所有图表渲染正常,无报错
-
-### 技术性验收
-- [ ] 代码符合 Mini UI包开发规范
-- [ ] 组件结构简洁清晰
-- [ ] 组件Props类型定义完整
-- [ ] 所有默认配置保持一致
-
-### 测试验收
-- [ ] yongren-statistics-ui 包的测试通过
-- [ ] 图表能够正常渲染和交互
-- [ ] 无 TypeScript 类型错误
-
-## 技术实现细节
-
-### 重构步骤
-
-#### 步骤1: 删除旧组件,重命名 BaseChartOriginal2D
-1. 删除 `BaseChart.tsx`(旧版本)
-2. 将 `BaseChartOriginal2D.tsx` 重命名为 `BaseChart.tsx`
-3. 更新内部导入引用
-
-#### 步骤2: 创建/更新图表组件
-所有图表组件(ColumnChart、BarChart、PieChart、LineChart)基于新的 BaseChart 创建:
-
-**参考模板**(以 ColumnChart 为例):
-```typescript
-import React from 'react';
-import { BaseChart } from './BaseChart';
-import type { ChartsConfig } from '../lib/u-charts-original';
-
-export interface ColumnChartProps {
-  canvasId: string;
-  width?: number;
-  height?: number;
-  categories: string[];
-  series: ChartsConfig['series'];
-  config?: Partial<ChartsConfig>;
-}
-
-export const ColumnChart: React.FC<ColumnChartProps> = (props) => {
-  const { canvasId, width, height, categories, series, config = {} } = props;
-
-  const mergedConfig: Partial<ChartsConfig> = {
-    animation: true,
-    background: '#FFFFFF',
-    color: ['#3b82f6', '#10b981', '#f59e0b', '#8b5cf6', '#ef4444'],
-    padding: [15, 15, 0, 5],
-    enableScroll: false,
-    legend: {},
-    xAxis: { disableGrid: true, ...config.xAxis },
-    yAxis: { data: [{ min: 0 }], ...config.yAxis },
-    extra: {
-      column: {
-        type: 'group',
-        width: 30,
-        activeBgColor: '#000000',
-        activeBgOpacity: 0.08,
-        ...config.extra?.column,
-      }
-    },
-    ...config,
-  };
-
-  return (
-    <BaseChart
-      canvasId={canvasId}
-      width={width}
-      height={height}
-      type="column"
-      categories={categories}
-      series={series}
-      config={mergedConfig}
-    />
-  );
-};
-```
-
-**组件列表**:
-- **ColumnChart**: 柱状图(type="column")
-- **BarChart**: 横向柱状图(type="bar")
-- **PieChart**: 饼图/环形图(type="pie" 或 "ring")
-- **LineChart**: 折线图(type="line")
-
-### 需要删除的文件
-- `BaseChart.tsx`(旧版本,删除后由 BaseChartOriginal2D 替代)
-- `BaseChartLegacy.tsx`
-- `ColumnChart.tsx`(旧版本)
-- `ColumnChartLegacy.tsx`
-- `CandleChart.tsx`(如果不使用)
-- `RadarChart.tsx`(如果不使用)
-- `BarChart.tsx`(旧版本)
-- `PieChart.tsx`(旧版本)
-- `LineChart.tsx`(旧版本)
-
-**保留文件**(用于调试对比):
-- `ColumnChartFCExample.tsx` - 保留作为文档参考实现
-
-### 更新 Statistics.tsx
-替换导入:
-```typescript
-// 旧导入
-import { ColumnChart } from '@d8d/mini-charts/components/ColumnChart'
-import { BarChart } from '@d8d/mini-charts/components/BarChart'
-import { PieChart } from '@d8d/mini-charts/components/PieChart'
-
-// 新导入 - 直接从组件文件路径导入
-import { ColumnChart } from '@d8d/mini-charts/src/components/ColumnChart'
-import { BarChart } from '@d8d/mini-charts/src/components/BarChart'
-import { PieChart } from '@d8d/mini-charts/src/components/PieChart'
-import { LineChart } from '@d8d/mini-charts/src/components/LineChart'
-```
-
-## 任务分解
-
-### Task 1: 删除旧组件,重命名 BaseChartOriginal2D
-- [ ] 删除旧版 BaseChart.tsx
-- [ ] 将 BaseChartOriginal2D.tsx 重命名为 BaseChart.tsx
-- [ ] 删除 BaseChartLegacy.tsx
-- [ ] 删除旧版 ColumnChart.tsx、BarChart.tsx、PieChart.tsx、LineChart.tsx
-- [ ] 删除 ColumnChartLegacy.tsx
-
-### Task 2: 创建 ColumnChart
-- [ ] 基于新的 BaseChart 创建 ColumnChart.tsx
-- [ ] 使用 type="column"
-- [ ] 合并默认配置
-- [ ] 导出组件
-
-### Task 3: 创建 BarChart
-- [ ] 基于新的 BaseChart 创建 BarChart.tsx
-- [ ] 使用 type="bar"
-- [ ] 合并默认配置
-- [ ] 导出组件
-
-### Task 4: 创建 PieChart
-- [ ] 基于新的 BaseChart 创建 PieChart.tsx
-- [ ] 支持 type="pie" 和 "ring"
-- [ ] 合并默认配置
-- [ ] 导出组件
-
-### Task 5: 创建 LineChart
-- [ ] 基于新的 BaseChart 创建 LineChart.tsx
-- [ ] 使用 type="line"
-- [ ] 合并默认配置
-- [ ] 导出组件
-
-### Task 6: 更新 Statistics.tsx
-- [ ] 更新导入路径(使用 @d8d/mini-charts/src/components/...)
-- [ ] 验证所有图表正常渲染
-- [ ] 移除调试用的条件渲染代码
-
-### Task 7: 测试验证
-- [ ] 构建 mini-charts 包
-- [ ] 构建 yongren-statistics-ui 包
-- [ ] 运行测试确认无错误
-
-## Dev Notes
-
-### 关键点
-1. **类型导入**: 使用 `import type { ChartsConfig } from '../lib/u-charts-original'`
-2. **配置合并**: 使用展开运算符合并用户配置和默认配置
-3. **组件命名**: 使用简洁的名称(ColumnChart、BarChart、PieChart、LineChart),不再使用 Original2D 后缀
-4. **默认配置**: 保持与原 ColumnChartOriginal2D 的默认配置结构一致
-5. **BaseChart**: 原来的 BaseChartOriginal2D 重命名为 BaseChart,作为所有图表的基础组件
-
-### 注意事项
-- 重命名时更新 BaseChart 内部的导出名称
-- 不要修改 `u-charts-original.js`
-- 保持与原组件的API兼容性
-- 组件应该是纯展示组件,不需要内部状态
-- index.ts 保持为空,不导出任何组件
-
-## Agent Model Used
-Claude Sonnet 4.5
-
-## Debug Log References
-无
-
-## Completion Notes
-待完成...
-
-## File List
-### 新建文件
-- `docs/stories/018.001.refactor-mini-charts-components.story.md`
-
-### 修改文件
-- `mini-ui-packages/mini-charts/src/components/BaseChart.tsx`(重命名自 BaseChartOriginal2D.tsx)
-- `mini-ui-packages/mini-charts/src/components/ColumnChart.tsx`(新建,基于 BaseChart)
-- `mini-ui-packages/mini-charts/src/components/BarChart.tsx`(新建,基于 BaseChart)
-- `mini-ui-packages/mini-charts/src/components/PieChart.tsx`(新建,基于 BaseChart)
-- `mini-ui-packages/mini-charts/src/components/LineChart.tsx`(新建,基于 BaseChart)
-- `mini-ui-packages/mini-charts/src/components/index.ts`(保持为空)
-- `mini-ui-packages/yongren-statistics-ui/src/pages/Statistics/Statistics.tsx`(更新导入路径)
-
-### 删除文件
-- `mini-ui-packages/mini-charts/src/components/BaseChart.tsx`(旧版本,删除前由 BaseChartOriginal2D 替代)
-- `mini-ui-packages/mini-charts/src/components/BaseChartOriginal2D.tsx`(重命名为 BaseChart.tsx 后删除)
-- `mini-ui-packages/mini-charts/src/components/BaseChartLegacy.tsx`
-- `mini-ui-packages/mini-charts/src/components/ColumnChart.tsx`(旧版本)
-- `mini-ui-packages/mini-charts/src/components/ColumnChartOriginal2D.tsx`(保留参考)
-- `mini-ui-packages/mini-charts/src/components/ColumnChartLegacy.tsx`
-- `mini-ui-packages/mini-charts/src/components/BarChart.tsx`(旧版本)
-- `mini-ui-packages/mini-charts/src/components/PieChart.tsx`(旧版本)
-- `mini-ui-packages/mini-charts/src/components/LineChart.tsx`(旧版本)
-- 可能删除 `mini-ui-packages/mini-charts/src/components/CandleChart.tsx`
-- 可能删除 `mini-ui-packages/mini-charts/src/components/RadarChart.tsx`
-
-**保留文件**:
-- `ColumnChartOriginal2D.tsx` - 作为参考实现保留
-- `ColumnChartFCExample.tsx` - 调试用参考实现

+ 346 - 0
docs/stories/018.001.story.md

@@ -0,0 +1,346 @@
+# 故事 018.001 - 修复残疾人照片上传保存功能
+
+## Status
+Approved
+
+## Story
+**作为** 残疾人信息管理员
+**我希望** 上传的残疾人照片能够成功保存
+**以便** 下次查看时照片仍然存在,无需重复上传
+
+## Acceptance Criteria
+1. 首次上传残疾人照片后提交,照片能够成功保存到数据库
+2. 再次编辑残疾人信息时,已上传的照片能够正常显示
+3. 重新上传照片后能够成功保存,并正确更新数据库记录
+4. 照片上传失败时能够显示明确的错误提示信息
+5. 支持常见的图片格式(JPG、PNG、GIF、BMP、WebP等)
+6. 照片文件大小限制合理,不超过500MB
+7. 照片信息正确关联到残疾人记录(person_id)
+8. 照片文件信息正确保存到files表(file_id关联)
+
+## Tasks / Subtasks
+
+### Task 1: 排查照片上传保存问题根因 (AC: 1, 3, 7, 8)
+- [ ] 检查 `DisabilityPersonManagement.tsx` 中照片数据提交逻辑
+- [ ] 验证前端表单提交时 `photos` 字段是否正确传递给后端API
+- [ ] 检查 `createPhotos` 和 `updatePhotos` 状态在表单提交时的处理
+- [ ] 查看后端API接收和处理照片数据的代码
+- [ ] 检查 `disabled-person-crud.routes.ts` 中创建和更新残疾人时的照片处理逻辑
+- [ ] 验证数据库事务是否正确提交(包括残疾人信息和照片信息)
+
+### Task 2: 检查照片回显逻辑 (AC: 2)
+- [ ] 检查编辑残疾人信息时,后端API是否返回照片数据
+- [ ] 验证 `disabled-person-custom.routes.ts` 中获取残疾人详情时的照片关联查询
+- [ ] 检查前端 `updatePhotos` 状态是否正确填充后端返回的照片数据
+- [ ] 验证 `PhotoUploadField` 组件回显已上传照片的功能
+- [ ] 检查照片文件URL是否正确生成(通过 `file-module` API)
+
+### Task 3: 添加照片上传错误处理 (AC: 4)
+- [ ] 在前端添加照片上传失败时的错误捕获和提示
+- [ ] 在后端添加照片数据验证,确保必要字段(personId, fileId, photoType)存在
+- [ ] 添加文件ID有效性验证(检查file是否存在)
+- [ ] 添加重复照片检测(同一personId + photoType组合)
+- [ ] 在Schema中添加照片数据的验证规则
+
+### Task 4: 验证照片文件类型和大小限制 (AC: 5, 6)
+- [ ] 检查 `PhotoUploadField` 组件的 `accept` 属性配置
+- [ ] 验证 `FileSelector` 组件的文件类型验证
+- [ ] 确认文件大小限制配置(建议不超过500MB)
+- [ ] 添加超出限制时的友好错误提示
+
+### Task 5: 修复发现的问题 (AC: 1, 2, 3)
+- [ ] 根据排查结果,修复前端或后端代码中导致照片保存失败的问题
+- [ ] 确保照片数据正确传递到后端(检查API请求体结构)
+- [ ] 确保后端正确处理和保存照片数据(检查数据库操作)
+- [ ] 确保照片回显时正确加载和显示(检查数据获取和组件渲染)
+
+### Task 6: 单元测试 (AC: 所有)
+- [ ] 为照片上传保存逻辑添加单元测试
+- [ ] 测试照片数据正确提交到后端API
+- [ ] 测试照片回显正确加载已保存的照片
+- [ ] 测试错误处理和用户提示
+
+### Task 7: 集成测试 (AC: 1, 2, 3)
+- [ ] 编写端到端集成测试:创建残疾人并上传照片
+- [ ] 编写端到端集成测试:编辑残疾人并查看已上传照片
+- [ ] 编写端到端集成测试:重新上传照片并验证更新
+- [ ] 验证数据库中 `disabled_photo` 表记录正确
+- [ ] 验证照片与残疾人的关联关系正确
+
+### Task 8: 手动测试验证
+- [ ] 手动测试:创建残疾人,上传多张不同类型照片,提交后验证保存成功
+- [ ] 手动测试:重新进入编辑页面,验证照片正常显示
+- [ ] 手动测试:删除已有照片,重新上传,验证更新成功
+- [ ] 手动测试:上传超大文件,验证错误提示
+- [ ] 手动测试:上传不支持的文件类型,验证错误提示
+
+## Dev Notes
+
+### 数据模型信息
+[Source: allin-packages/disability-module/src/entities/disabled-photo.entity.ts]
+
+**DisabledPhoto Entity**:
+- `id` (photo_id): 主键,自增整数
+- `personId` (person_id): 残疾人ID,外键关联 `disabled_person.person_id`,`onDelete: CASCADE`
+- `photoType` (photo_type): 照片类型,varchar(50),如:身份证照片、残疾证照片、个人照片、其他照片
+- `fileId` (file_id): 文件ID,外键关联 `files.file_id`,`onDelete: CASCADE`
+- `uploadTime` (upload_time): 上传时间,timestamp,默认 `CURRENT_TIMESTAMP`
+- `canDownload` (can_download): 是否可下载,smallint,默认1(是)
+
+**关系**:
+- 多对一关联 `DisabledPerson` (person → person.photos)
+- 多对一关联 `File` (file → file模块的File实体)
+
+**DisabledPerson Entity** (相关字段):
+[Source: allin-packages/disability-module/src/entities/disabled-person.entity.ts]
+- 包含 `@OneToMany` 关系:`photos!: DisabledPhoto[]`
+
+### 前端组件结构
+[Source: allin-packages/disability-person-management-ui/src/components/]
+
+**PhotoUploadField 组件**:
+- 文件位置: `PhotoUploadField.tsx`
+- Props接口:
+  ```typescript
+  export interface PhotoItem {
+    photoType: string;
+    fileId: number | null;
+    canDownload: number;
+    tempId?: string; // 临时ID用于React key
+  }
+  export interface PhotoUploadFieldProps {
+    value?: PhotoItem[];
+    onChange?: (photos: PhotoItem[]) => void;
+    photoTypes?: string[];
+    maxPhotos?: number;
+  }
+  ```
+- 支持的功能: 添加照片、删除照片、选择照片类型、选择文件、设置可下载权限
+- 使用 `FileSelector` 组件选择文件(来自 `@d8d/file-management-ui`)
+
+**DisabilityPersonManagement 组件**:
+- 文件位置: `DisabilityPersonManagement.tsx`
+- 状态管理:
+  ```typescript
+  const [createPhotos, setCreatePhotos] = useState<PhotoItem[]>([]);
+  const [updatePhotos, setUpdatePhotos] = useState<PhotoItem[]>([]);
+  ```
+- 使用 `react-hook-form` 管理表单
+- 使用 `@tanstack/react-query` 进行数据获取和提交
+
+### API客户端
+[Source: allin-packages/disability-person-management-ui/src/api/disabilityClient.ts]
+
+**disabilityClientManager**:
+- 提供创建和更新残疾人的API方法
+- 需要验证请求体中是否正确包含 `photos` 字段
+
+### 后端路由
+[Source: allin-packages/disability-module/src/routes/]
+
+**disabled-person-crud.routes.ts**:
+- 通用CRUD路由,继承自 `GenericCrudService`
+- 创建和更新操作需要处理 `photos` 关联数据
+
+**disabled-person-custom.routes.ts**:
+- 自定义路由,可能包含获取残疾人详情的接口
+- 需要验证是否正确查询并返回照片关联数据
+
+### Schema验证
+[Source: allin-packages/disability-module/src/schemas/]
+
+**CreateDisabledPersonSchema / UpdateDisabledPersonSchema**:
+- 需要验证Schema中是否包含 `photos` 字段定义
+- 照片数据应该是一个数组,包含 `photoType`, `fileId`, `canDownload`
+
+### 文件模块集成
+[Source: packages/file-module/]
+
+**File Entity**:
+- `fileId`: 文件ID
+- `name`: 文件名
+- `path`: 存储路径
+- `size`: 文件大小
+- `type`: MIME类型
+
+**MinIO Service**:
+- 文件上传策略生成
+- 预签名URL获取
+- 文件下载
+
+**前端集成**:
+- `FileSelector` 组件来自 `@d8d/file-management-ui`
+- 支持文件预览、文件选择和上传
+
+### 地区选择组件
+[Source: packages/area-management-ui/]
+
+**ProvinceSelect / AreaSelectForm**:
+- 省市区级联选择器
+- 可能与地区选择性能问题相关(018-03故事)
+
+### 测试要求
+
+#### 单元测试
+- [Source: docs/architecture/testing-strategy.md]
+- 使用 Vitest 测试框架
+- 测试文件位置: `allin-packages/disability-person-management-ui/tests/unit/`
+- 已有测试: `PhotoUploadField.test.tsx`
+- 覆盖率目标: ≥ 80%
+
+#### 集成测试
+- [Source: docs/architecture/testing-strategy.md]
+- 使用 Vitest + Testing Library
+- 测试文件位置: `allin-packages/disability-person-management-ui/tests/integration/`
+- 已有测试: `disability-person.integration.test.tsx`
+- 需要测试照片上传、保存、回显的完整流程
+- 覆盖率目标: ≥ 60%
+
+#### E2E测试
+- [Source: docs/architecture/testing-strategy.md]
+- 使用 Playwright
+- 测试文件位置: `web/tests/e2e/`
+- 需要测试完整的用户操作流程
+
+### 调试技巧
+[Source: docs/architecture/testing-strategy.md]
+
+**查看测试详情**:
+```bash
+# 运行特定测试查看详细信息
+pnpm test --testNamePattern "照片上传"
+
+# 在disability-person-management-ui目录下
+cd allin-packages/disability-person-management-ui
+pnpm test --testNamePattern "照片"
+```
+
+**表单调试**:
+- 在表单 `form.handleSubmit` 的第二个参数中加 `console.debug` 查看表单验证错误:
+  ```typescript
+  form.handleSubmit(handleSubmit, (errors) => console.debug('表单验证错误:', errors))
+  ```
+
+**E2E测试失败调试**:
+- 查看页面结构: `cat test-results/**/error-context.md`
+- 使用调试模式: `pnpm test:e2e:chromium --debug`
+
+### 编码标准
+[Source: docs/architecture/coding-standards.md]
+
+**TypeScript严格模式**: 所有代码必须启用严格类型检查
+**命名约定**:
+- 文件名: kebab-case (如: `photo-upload-field.tsx`)
+- 类名: PascalCase (如: `PhotoUploadField`)
+- 函数/变量: camelCase (如: `handlePhotoUpload`)
+- 接口: PascalCase,无I前缀 (如: `PhotoItem`)
+
+**UI包开发规范**:
+- [Source: docs/architecture/ui-package-standards.md]
+- API路径映射验证: 确保故事中的API路径与实际后端路由一致
+- 类型推断最佳实践: 使用RPC推断类型,而不是直接导入schema类型
+- 测试选择器优化: 为关键交互元素添加 `data-testid` 属性
+- 表单组件模式: 使用条件渲染两个独立的Form组件(创建/编辑)
+
+**参考实现**:
+- 残疾人管理UI包: `allin-packages/disability-person-management-ui`
+- 文件管理UI包: `packages/file-management-ui`
+
+### 项目结构
+[Source: docs/architecture/source-tree.md]
+
+**残疾人相关包**:
+```
+allin-packages/
+├── disability-module/                    # 残疾人后端模块
+│   ├── src/
+│   │   ├── entities/                     # 数据实体
+│   │   │   ├── disabled-person.entity.ts
+│   │   │   ├── disabled-photo.entity.ts
+│   │   │   └── ...
+│   │   ├── routes/                       # API路由
+│   │   │   ├── disabled-person-crud.routes.ts
+│   │   │   └── disabled-person-custom.routes.ts
+│   │   ├── schemas/                      # 验证Schema
+│   │   └── services/                     # 业务逻辑
+│   └── tests/
+└── disability-person-management-ui/      # 残疾人前端UI
+    ├── src/
+    │   ├── components/                   # UI组件
+    │   │   ├── DisabilityPersonManagement.tsx
+    │   │   ├── PhotoUploadField.tsx
+    │   │   └── ...
+    │   └── api/                          # API客户端
+    └── tests/                            # 测试文件
+        ├── unit/
+        └── integration/
+```
+
+**共享包**:
+- `packages/file-module`: 文件管理模块
+- `packages/file-management-ui`: 文件管理UI
+- `packages/geo-areas`: 地理区域模块
+
+### 技术栈
+[Source: docs/architecture/tech-stack.md]
+
+- **运行时**: Node.js 20.18.3
+- **前端框架**: React 19.1.0
+- **构建工具**: Vite 7.0.0
+- **数据库**: PostgreSQL 17 + TypeORM 0.3.25
+- **状态管理**: React Query 5.83.0
+- **表单管理**: react-hook-form + zod
+- **测试框架**: Vitest 3.2.4
+- **文件存储**: MinIO
+
+### 可能的问题点
+
+#### 前端问题
+1. **表单提交时照片数据未正确传递**: 检查 `createForm.handleSubmit` 和 `updateForm.handleSubmit` 中的数据组装
+2. **状态管理问题**: `createPhotos` 和 `updatePhotos` 可能未正确同步到表单值
+3. **组件值绑定问题**: `PhotoUploadField` 的 `value` 和 `onChange` 可能未正确连接
+
+#### 后端问题
+1. **Schema验证问题**: Schema可能未定义 `photos` 字段,导致数据被过滤
+2. **Service层处理问题**: 创建/更新残疾人时可能未正确处理关联的照片数据
+3. **数据库事务问题**: 照片保存可能在独立事务中,导致残疾人保存失败时照片已保存
+4. **关联查询缺失**: 获取残疾人详情时可能未使用 `relations` 加载照片数据
+
+#### 数据库问题
+1. **外键约束**: `person_id` 或 `file_id` 可能在残疾人保存前不存在
+2. **级联删除配置**: `onDelete: CASCADE` 可能导致意外的数据删除
+
+### 修复检查清单
+
+在修复过程中,请按以下顺序检查:
+
+1. [ ] 前端表单提交时的请求体是否包含完整的 `photos` 数据
+2. [ ] 后端API是否正确接收和解析 `photos` 数据
+3. [ ] Schema是否包含 `photos` 字段的验证规则
+4. [ ] Service层是否正确处理照片数据的保存(使用 `DisabledPhoto` Entity)
+5. [ ] 数据库操作是否在事务中执行(确保原子性)
+6. [ ] 获取残疾人详情时是否使用 `relations: ['photos']` 加载照片
+7. [ ] 前端回显时是否正确使用后端返回的照片数据
+8. [ ] 照片文件URL是否正确生成(通过 `file-module` API)
+
+## Change Log
+| Date | Version | Description | Author |
+|------|---------|-------------|--------|
+| 2025-12-31 | 1.0 | 创建故事文档 | Bob (Scrum Master) |
+
+## Dev Agent Record
+
+### Agent Model Used
+待开发时填写
+
+### Debug Log References
+待开发时填写
+
+### Completion Notes List
+待开发时填写
+
+### File List
+待开发时填写
+
+## QA Results
+待QA测试时填写