---
category: schema
title: YSS项目Parse Server数据范式 - Product表统一空间管理
subtitle: 映三色设计师项目管理系统完整数据表结构（基于Product表的空间管理版本）
name: 'yss-schemas-product-space'
label: database
---

# Parse Server 数据范式 - 映三色项目管理系统（Product表统一空间管理）

## 概述

本文档定义了映三色（YSS）设计师项目管理系统的完整Parse Server数据范式。系统采用**多租户架构**，以Company为核心，支持客服、设计师、组长等多角色协作的全流程项目管理。

**核心特性**：
- 🏢 多租户架构，以Company为核心租户隔离
- 👥 统一员工表（Profile）和客户表（ContactInfo）
- 📋 项目与群聊灵活关联（Project ←→ GroupChat）
- 🏠 **Product表统一空间管理**：通过Product表实现多空间设计产品管理
- 🎨 设计师项目全生命周期管理
- 💰 财务报价与结算流程
- 📊 质量控制与客户反馈

**空间管理优化**：
- 🎯 **Product即空间**：每个Product代表一个空间的设计产品
- 💰 **产品报价**：通过Product的`.quotation`字段管理空间级报价
- 📁 **文件分类**：ProjectFile通过`.category`区分panorama、delivery等类型
- 👥 **设计师分配**：通过Product的`.profile`字段直接关联负责设计师
- 🎯 **产品需求**：通过Product的`.requirements`字段管理空间需求
- ⭐ **产品评价**：通过Product的`.reviews`字段管理空间评价

---

## 数据表关系图


- Department 项目组（部门）
  - name String 项目组名称
  - type "project" 项目组
  - leader Pointer<Profile> 组长
  - company Pointer<Company> 指向当前帐套

```plantuml
@startuml
!define TABLE(name,desc) class name as "desc" << (T,#FFAAAA) >>
!define FIELD(name,type) name : type

skinparam classAttributeIconSize 0
skinparam class {
    BackgroundColor LightYellow
    BorderColor Black
    ArrowColor Black
}

' ============ 核心租户与人员 ============
TABLE(Company, "Company\n企业表") {
    FIELD(objectId, String)
    FIELD(name, String)
    FIELD(corpId, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(Profile, "Profile\n员工档案表") {
    FIELD(objectId, String)
    FIELD(name, String)
    FIELD(mobile, String)
    FIELD(department, Pointer→Department)
    FIELD(company, Pointer→Company)
    FIELD(userId, String)
    FIELD(roleName, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ContactInfo, "ContactInfo\n客户信息表") {
    FIELD(objectId, String)
    FIELD(name, String)
    FIELD(mobile, String)
    FIELD(company, Pointer→Company)
    FIELD(external_userid, String)
    FIELD(source, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 企微集成 ============
TABLE(GroupChat, "GroupChat\n企微群聊表") {
    FIELD(objectId, String)
    FIELD(chat_id, String)
    FIELD(name, String)
    FIELD(company, Pointer→Company)
    FIELD(project, Pointer→Project)
    FIELD(member_list, Array)
    FIELD(joinUrl, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectGroup, "ProjectGroup\n项目群组关联表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(groupChat, Pointer→GroupChat)
    FIELD(isPrimary, Boolean)
    FIELD(createdAt, Date)
}

' ============ 项目模块 ============
TABLE(Project, "Project\n项目表") {
    FIELD(objectId, String)
    FIELD(title, String)
    FIELD(company, Pointer→Company)
    FIELD(customer, Pointer→ContactInfo)
    FIELD(assignee, Pointer→Profile)
    FIELD(status, String)
    FIELD(currentStage, String)
    FIELD(deadline, Date)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 核心创新：Product表统一空间管理 ============
TABLE(Product, "Product\n空间设计产品表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(company, Pointer→Company)
    FIELD(profile, Pointer→Profile)
    FIELD(stage, String)
    FIELD(processType, String)
    FIELD(productName, String)
    FIELD(productType, String)
    FIELD(status, String)
    FIELD(fileUrl, String)
    FIELD(reviewStatus, String)
    FIELD(space, Object)
    FIELD(quotation, Object)
    FIELD(requirements, Object)
    FIELD(reviews, Array)
    FIELD(estimatedBudget, Number)
    FIELD(estimatedDuration, Number)
    FIELD(order, Number)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectRequirement, "ProjectRequirement\n需求信息表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(company, Pointer→Company)
    FIELD(spaces, Array)
    FIELD(designRequirements, Object)
    FIELD(materialAnalysis, Object)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectTeam, "ProjectTeam\n项目团队表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(profile, Pointer→Profile)
    FIELD(role, String)
    FIELD(workload, Number)
    FIELD(isDeleted, Boolean)
}

' ============ 文件管理（简化版） ============
TABLE(ProjectFile, "ProjectFile\n项目文件表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(product, Pointer→Product)
    FIELD(attach, Pointer→Attachment) 
    FIELD(uploadedBy, Pointer→Profile)
    FIELD(stage, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 财务模块 ============
TABLE(ProjectPayment, "ProjectPayment\n项目付款表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(company, Pointer→Company)
    FIELD(type, String)
    FIELD(stage, String)
    FIELD(method, String)
    FIELD(amount, Number)
    FIELD(currency, String)
    FIELD(percentage, Number)
    FIELD(paymentDate, Date)
    FIELD(dueDate, Date)
    FIELD(recordedDate, Date)
    FIELD(status, String)
    FIELD(voucherFile, Pointer→ProjectFile)
    FIELD(voucherUrl, String)
    FIELD(transactionId, String)
    FIELD(paymentReference, String)
    FIELD(paidBy, Pointer→ContactInfo)
    FIELD(recordedBy, Pointer→Profile)
    FIELD(verifiedBy, Pointer→Profile)
    FIELD(description, String)
    FIELD(notes, String)
    FIELD(relatedStage, String)
    FIELD(product, Pointer→Product)
    FIELD(autoReminderSent, Boolean)
    FIELD(reminderCount, Number)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 质量与反馈 ============
TABLE(ProjectFeedback, "ProjectFeedback\n客户反馈表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(customer, Pointer→ContactInfo)
    FIELD(product, Pointer→Product)
    FIELD(stage, String)
    FIELD(feedbackType, String)
    FIELD(content, String)
    FIELD(rating, Number)
    FIELD(status, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectIssue, "ProjectIssue\n项目问题追踪表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(product, Pointer→Product)
    FIELD(creator, Pointer→Profile)                  // 创建人
    FIELD(assignee, Pointer→Profile)                 // 责任人
    FIELD(title, String)                              // 问题标题
    FIELD(description, String)                       // 问题描述
    FIELD(relatedSpace, String)                       // 相关空间
    FIELD(relatedStage, String)                       // 相关阶段
    FIELD(relatedContentType, String)                 // 相关内容类型 (白模/软装/渲染/后期)
    FIELD(relatedFiles, Array)                        // 相关项目文件
    FIELD(priority, String)                           // 优先程度 (低/中/高/紧急)
    FIELD(issueType, String)                          // 问题类型 (投诉/建议/改图)
    FIELD(dueDate, Date)                              // 截止时间
    FIELD(status, String)                             // 状态 (待处理/处理中/已解决/已关闭)
    FIELD(resolution, String)                         // 解决方案
    FIELD(lastReminderAt, Date)                       // 最后催单时间
    FIELD(reminderCount, Number)                      // 催单次数
    FIELD(data, Object)                              // 扩展数据
    FIELD(isDeleted, Boolean)
}

' ============ 跟进记录 ============
TABLE(ContactFollow, "ContactFollow\n跟进记录表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(product, Pointer→Product)
    FIELD(sender, Pointer→Profile/ContactInfo)
    FIELD(content, String)
    FIELD(type, String)
    FIELD(stage, String)
    FIELD(attachments, Array)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 关系连线 ============

' Company 一对多关系
Company "1" --> "n" Profile : 企业员工
Company "1" --> "n" ContactInfo : 企业客户
Company "1" --> "n" Project : 企业项目
Company "1" --> "n" GroupChat : 企业群聊

' 项目核心关系
Project "n" --> "1" Company : 所属企业
Project "n" --> "1" ContactInfo : 客户
Project "n" --> "1" Profile : 负责人(assignee)
Project "1" --> "1" ProjectRequirement : 需求信息
Project "1" <--> "n" GroupChat : ProjectGroup\n群聊关联
Project "1" --> "n" ProjectTeam : 项目团队

' Product表统一空间管理关系
Project "1" --> "n" Product : 空间设计产品
Product "n" --> "1" Profile : 负责设计师
Product "1" --> "n" ProjectFile : 产品文件
Product "1" --> "n" ProjectFeedback : 产品反馈
Product "1" --> "n" ProjectIssue : 产品异常
Product "1" --> "n" ContactFollow : 产品跟进

' 交付与财务
Project "1" --> "n" ProjectFile : 项目文件
Project "1" --> "n" ProjectPayment : 项目付款
ProjectPayment "1" --> "1" ProjectFile : 付款凭证
ProjectPayment "1" --> "1" ContactInfo : 付款人
ProjectPayment "1" --> "1" Profile : 记录人/验证人
Product "1" --> "n" ProjectPayment : 产品级付款

' 质量与沟通
Project "1" --> "n" ProjectFeedback : 客户反馈
Project "1" --> "n" ProjectIssue : 异常记录
Project "1" --> "n" ContactFollow : 跟进记录

' 群聊关系
GroupChat "n" --> "1" Company : 所属企业
GroupChat "n" --> "1" Project : 关联项目(可选)

@enduml
```

---

## 核心数据表详解

### 1. Company（企业表）

**用途**: 多租户系统的核心，所有数据通过 company 字段进行租户隔离。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "cDL6R1hgSi" |
| name | String | 是 | 企业名称 | "映三色设计" |
| corpId | String | 否 | 企业微信CorpID | "ww1234567890" |
| data | Object | 否 | 扩展数据 | { settings: {...}, modules: [...] } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

---

### 2. Profile（员工档案表）

**用途**: 存储企业员工档案信息，统一管理客服、设计师、组长等所有角色。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "prof001" |
| name | String | 是 | 员工姓名 | "张三" |
| mobile | String | 否 | 手机号 | "13800138000" |
| department | Pointer | 是 | 所属小组 | → Department |
| company | Pointer | 是 | 所属企业 | → Company |
| userId | String | 否 | 企微UserID | "zhangsan" |
| roleName | String | 是 | 员工角色 | "客服" / "组员" / "组长" |
| data | Object | 否 | 扩展数据 | { avatar, department, skills, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**role 枚举值**:
- `客服`: 客户服务人员，负责接单、跟进
- `组员`: 设计师，负责具体设计工作
- `组长`: 团队负责人，负责审核、分配
- `财务`: 财务人员
- `人事`: 人事人员
- `管理员`: 系统管理员

---

### 3. ContactInfo（客户信息表）

**用途**: 统一管理所有客户信息，支持企微外部联系人同步。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "contact001" |
| name | String | 是 | 客户姓名 | "李四" |
| mobile | String | 否 | 手机号 | "13900139000" |
| company | Pointer | 是 | 所属企业 | → Company |
| external_userid | String | 否 | 企微外部联系人ID | "wmxxx" |
| source | String | 否 | 来源渠道 | "朋友圈" / "信息流" / "转介绍" |
| data | Object | 否 | 扩展数据 | { avatar, wechat, tags, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

---

### 4. Project（项目表）

**用途**: 项目管理的核心表，记录设计项目的全生命周期信息。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "proj001" |
| title | String | 是 | 项目标题 | "李总现代简约全案" |
| company | Pointer | 是 | 所属企业 | → Company |
| customer | Pointer | 是 | 客户 | → ContactInfo |
| assignee | Pointer | 否 | 负责设计师 | → Profile |
| status | String | 是 | 项目状态 | "进行中" |
| currentStage | String | 是 | 当前阶段 | "建模" |
| deadline | Date | 否 | 截止时间 | 2024-12-31T00:00:00.000Z |
| data | Object | 否 | 扩展数据 | { requirements, stageHistory, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

---

### 5. Product（空间设计产品表）⭐

**功能描述**: **核心创新** - 统一管理空间设计产品，每个Product代表一个空间的设计产品（如"李总主卧设计"、"王总客厅设计"），包含空间信息、报价、需求、评价等全生命周期数据。

**字段说明**:
| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "prod001" |
| project | Pointer | 是 | 所属项目 | → Project |
| company | Pointer | 是 | 所属企业 | → Company |
| **profile** | **Pointer** | **是** | **负责设计师** | **→ Profile** |
| stage | String | 是 | 设计阶段 | "建模" / "渲染" / "后期" |
| processType | String | 否 | 工序类型 | "modeling" / "rendering" |
| **productName** | **String** | **是** | **产品名称** | **"李总主卧设计"** |
| **productType** | **String** | **是** | **空间类型** | **"bedroom"** |
| status | String | 是 | 产品状态 | "进行中" |
| fileUrl | String | 否 | 主要效果图URL | "https://..." |
| reviewStatus | String | 是 | 审核状态 | "pending" / "approved" |
| **space** | **Object** | **否** | **空间信息** | **{name, area, dimensions}** |
| **quotation** | **Object** | **否** | **产品报价** | **{price, breakdown}** |
| **requirements** | **Object** | **否** | **设计需求** | **{color, material}** |
| **reviews** | **Array** | **否** | **产品评价** | **[{rating, comments}]** |
| estimatedBudget | Number | 否 | 预估预算 | 35000 |
| estimatedDuration | Number | 否 | 预估工期（天） | 7 |
| order | Number | 否 | 排序顺序 | 1 |
| data | Object | 否 | 扩展数据 | { version, progress, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |

**productType 枚举值**:
- `living_room`: 客厅
- `bedroom`: 卧室
- `kitchen`: 厨房
- `bathroom`: 卫生间
- `dining_room`: 餐厅
- `study`: 书房
- `balcony`: 阳台
- `corridor`: 走廊
- `storage`: 储物间
- `entrance`: 玄关
- `other`: 其他

**status 枚举值**:
- `not_started`: 未开始
- `in_progress`: 进行中
- `awaiting_review`: 待审核
- `completed`: 已完成
- `blocked`: 已阻塞
- `delayed`: 已延期

**space 字段结构示例**:
```json
{
  "spaceName": "主卧",
  "area": 18.5,
  "dimensions": {
    "length": 4.5,
    "width": 4.1,
    "height": 2.8
  },
  "features": ["朝南", "飘窗", "独立卫浴"],
  "constraints": ["承重墙不可动"],
  "priority": "high",
  "complexity": "medium"
}
```

**quotation 字段结构示例**:
```json
{
  "price": 35000,
  "currency": "CNY",
  "breakdown": {
    "design": 15000,
    "modeling": 10000,
    "rendering": 8000,
    "softDecor": 2000
  },
  "unitPrice": 1891,
  "estimatedDays": 7,
  "status": "approved",
  "approvedBy": {"__type": "Pointer", "className": "Profile", "objectId": "prof001"},
  "validUntil": "2024-12-31T00:00:00.000Z"
}
```

**requirements 字段结构示例**:
```json
{
  "colorRequirement": {
    "primaryHue": 180,
    "saturation": 45,
    "temperature": "暖色调",
    "colorDistribution": [
      {"hex": "#F5F5DC", "percentage": 40, "name": "米白色"},
      {"hex": "#8B4513", "percentage": 30, "name": "原木色"}
    ]
  },
  "materialRequirement": {
    "preferred": ["实木", "环保材料"],
    "avoid": ["塑料", "合成材料"],
    "budget": { "min": 20000, "max": 40000 }
  },
  "lightingRequirement": {
    "naturalLight": "充足",
    "lightColor": "暖白",
    "specialRequirements": ["床头阅读灯", "氛围灯"]
  },
  "specificRequirements": [
    "需要大储物空间",
    "独立卫浴",
    "飘窗设计"
  ],
  "referenceImages": ["https://...", "https://..."]
}
```

**reviews 字段结构示例**:
```json
[
  {
    "reviewId": "review001",
    "satisfactionScore": 4.5,
    "spaceSpecificRatings": {
      "design": 5,
      "functionality": 4,
      "material": 4,
      "lighting": 5
    },
    "usageFeedback": {
      "positive": ["储物空间充足", "光线舒适"],
      "improvements": ["可以增加插座数量"]
    },
    "comments": "整体设计很满意，储物功能强大",
    "afterPhotos": ["https://...", "https://..."],
    "submittedAt": "2024-11-15T10:00:00.000Z",
    "submittedBy": {"__type": "Pointer", "className": "ContactInfo", "objectId": "contact001"}
  }
]
```

**使用场景示例**:
```typescript
// 创建空间设计产品
const Product = Parse.Object.extend("Product");
const product = new Product();
product.set("project", project.toPointer());
product.set("profile", designer.toPointer());
product.set("productName", "李总主卧设计");
product.set("productType", "bedroom");
product.set("stage", "modeling");

// 设置空间信息
product.set("space", {
  spaceName: "主卧",
  area: 18.5,
  dimensions: { length: 4.5, width: 4.1, height: 2.8 },
  features: ["朝南", "飘窗", "独立卫浴"]
});

// 设置产品报价
product.set("quotation", {
  price: 35000,
  currency: "CNY",
  breakdown: { design: 15000, modeling: 10000, rendering: 8000, softDecor: 2000 },
  status: "pending"
});

await product.save();

// 查询项目的所有空间设计产品
const productQuery = new Parse.Query("Product");
productQuery.equalTo("project", projectId);
productQuery.notEqualTo("isDeleted", true);
productQuery.ascending("order");
const products = await productQuery.find();

// 查询设计师负责的空间产品
const designerProductQuery = new Parse.Query("Product");
designerProductQuery.equalTo("profile", designerId);
designerProductQuery.equalTo("status", "in_progress");
const designerProducts = await designerProductQuery.find();
```

---

### 6. ProjectFile（项目文件表）

**用途**: 存储项目相关的所有文件，**简化设计**，通过 `.category` 字段区分文件类型。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "file001" |
| project | Pointer | 是 | 所属项目 | → Project |
| **product** | **Pointer** | **否** | **关联空间产品** | **→ Product** |
| attach | Pointer | 是 | 附件 | → Attachment | NovaFile.id为Attachment.objectId|
| uploadedBy | Pointer | 是 | 上传人 | → Profile |
| stage | String | 否 | 关联阶段 | "需求沟通" |
| category | String | 否 | 关联阶段 | "需求沟通" |
| data | Object | 否 | 扩展数据 | { thumbnailUrl, ... } |
| analysis | Object | 否 | 分析结果 | 详见下方 `analysis.color` 结构 |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**stage 枚举值**
- `订单分配`: 'order'
- `方案深化`: 'requirements'
- `交付执行`: 'delivery'
- `售后归档`: 'aftercare'

**category 枚举值**:
- `quotation`: 财务凭据
- `panorama`: 全景素材
- `delivery`: 交付文件
- `reference`: 参考文件
- `requirement`: 需求文件

**analysis.color - 色彩分析结果结构**

用于存放“色彩分析插件（color-get）”生成的分析报告，并支持重新分析覆盖。该结构与组件内 `ColorAnalysisReport` 对齐，并补充元信息。

```json
{
  "version": "1.0",              // 结构版本，便于未来演进
  "source": "color-get",          // 生成来源（组件/脚本/服务）
  "pixelSize": 100,                // 马赛克像素块大小（用于重现）
  "createdAt": "2025-10-20T12:00:00.000Z", // 生成时间

  "palette": [                     // 主色卡（按占比降序，最多20）
    { "rgb": { "r": 240, "g": 200, "b": 160 }, "hex": "#F0C8A0", "percentage": 28.5 }
  ],

  "mosaicUrl": "data:image/png;base64,....", // 马赛克图（可选，dataURL 或存储链接）

  "metrics": {
    "warmCoolBalance": 12.0,       // 冷暖平衡：-100(冷) 到 100(暖)
    "averageBrightness": 56.0,     // 平均亮度：0-100
    "averageSaturation": 42.5,     // 平均饱和度：0-100
    "diversity": 18                // 色彩多样度（主色数量）
  },

  "histogram": {
    "brightnessBins": [0, 1, 3, 5, 8, 12, 9, 6, 3, 1],
    "saturationBins": [0, 0, 2, 4, 7, 10, 9, 5, 2, 1]
  },

  "splitPoints": [                 // 色彩拆分散点（冷暖/明暗/占比/色值）
    { "temp": 14.2, "brightness": 62.1, "size": 3.5, "color": "#F0C8A0" }
  ]
}
```

**读写约定**
- 写入：将以上对象保存至 `ProjectFile.analysis.color`。
- 读取：若存在 `analysis.color`，组件直接使用并渲染（无需重复计算）。
- 重新分析：覆盖同一字段，保留 `version`、`source`、`pixelSize` 与 `createdAt` 更新。
- 存储位置：优先内嵌对象；如需持久化图像（大体积 `mosaicUrl`），可改为文件存储链接，并在此字段写入其URL。

**fileType 枚举值**:
- `cad`: CAD图纸
- `reference`: 参考图片
- `document`: 文档资料
- `contract`: 合同文件
- `voucher`: 付款凭证
- `other`: 其他

**使用场景示例**:
```typescript
// 上传空间产品相关的全景图文件
const ProjectFile = Parse.Object.extend("ProjectFile");
const file = new ProjectFile();
file.set("project", project.toPointer());
file.set("product", product.toPointer()); // 关联到具体空间产品
file.set("category", "panorama"); // 标记为全景图文件
file.set("fileName", "主卧720全景图.jpg");
file.set("uploadedBy", profile.toPointer());
await file.save();

// 查询空间产品的所有全景图文件
const panoramaQuery = new Parse.Query("ProjectFile");
panoramaQuery.equalTo("product", productId);
panoramaQuery.equalTo("category", "panorama");
panoramaQuery.notEqualTo("isDeleted", true);
const panoramaFiles = await panoramaQuery.find();

// 查询空间产品的所有报价文件
const quotationQuery = new Parse.Query("ProjectFile");
quotationQuery.equalTo("product", productId);
quotationQuery.equalTo("category", "quotation");
const quotationFiles = await quotationQuery.find();
```

---

### 7. ProjectPayment（项目付款表）⭐

**功能描述**: **统一的项目付款管理表**，整合了原有的 ProjectSettlement 和 ProjectVoucher 两张表的功能。每条记录代表一次具体的付款行为，包含付款类型、金额、方式、凭证等完整信息，支持项目级和产品级付款管理。

**字段说明**:
| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "payment001" |
| project | Pointer | 是 | 所属项目 | → Project |
| company | Pointer | 是 | 所属企业 | → Company |
| **type** | **String** | **是** | **付款类型** | **"advance" / "milestone" / "final" / "refund"** |
| **stage** | **String** | **是** | **付款阶段** | **"order" / "requirements" / "delivery" / "aftercare"** |
| **method** | **String** | **是** | **支付方式** | **"cash" / "bank_transfer" / "alipay" / "wechat"** |
| **amount** | **Number** | **是** | **付款金额** | **35000** |
| **currency** | **String** | **是** | **货币类型** | **"CNY"** |
| **percentage** | **Number** | **否** | **占总价百分比** | **30** |
| **paymentDate** | **Date** | **否** | **实际付款时间** | **2024-12-01T10:00:00.000Z** |
| **dueDate** | **Date** | **是** | **应付款时间** | **2024-12-01T00:00:00.000Z** |
| **recordedDate** | **Date** | **自动** | **记录时间** | **2024-11-30T15:30:00.000Z** |
| **status** | **String** | **是** | **付款状态** | **"pending" / "paid" / "overdue" / "cancelled"** |
| **voucherFile** | **Pointer** | **否** | **付款凭证文件** | **→ ProjectFile** |
| **voucherUrl** | **String** | **否** | **凭证URL** | **"https://..."** |
| **transactionId** | **String** | **否** | **第三方交易ID** | **"wx_123456789"** |
| **paymentReference** | **String** | **否** | **付款参考号/发票号** | **"INV-2024-001"** |
| **paidBy** | **Pointer** | **是** | **付款人** | **→ ContactInfo** |
| **recordedBy** | **Pointer** | **是** | **记录人** | **→ Profile** |
| **verifiedBy** | **Pointer** | **否** | **验证人** | **→ Profile** |
| **description** | **String** | **否** | **付款描述** | **"首期款"** |
| **notes** | **String** | **否** | **备注信息** | **"客户通过银行转账付款"** |
| **relatedStage** | **String** | **否** | **关联执行阶段** | **"modeling"** |
| **product** | **Pointer** | **否** | **关联产品** | **→ Product** |
| **autoReminderSent** | **Boolean** | **否** | **是否已发送提醒** | **false** |
| **reminderCount** | **Number** | **否** | **提醒次数** | **0** |
| **data** | **Object** | **否** | **扩展数据** | **{bankInfo, approvalFlow}** |
| **isDeleted** | **Boolean** | **否** | **软删除标记** | **false** |

**type 枚举值**:
- `advance`: 预付款/定金
- `milestone`: 里程碑付款
- `final`: 尾款/结算款
- `refund`: 退款

**stage 枚举值**:
- `order`: 订单分配阶段
- `requirements`: 方案深化阶段
- `delivery`: 交付执行阶段
- `aftercare`: 售后归档阶段

**method 枚举值**:
- `cash`: 现金
- `bank_transfer`: 银行转账
- `alipay`: 支付宝
- `wechat`: 微信支付
- `credit_card`: 信用卡
- `other`: 其他

**status 枚举值**:
- `pending`: 待付款
- `paid`: 已付款
- `overdue`: 逾期
- `cancelled`: 已取消
- `refunded`: 已退款

**data 字段结构示例**:
```json
{
  "bankInfo": {
    "bankName": "中国工商银行",
    "accountNumber": "****1234",
    "accountName": "李四"
  },
  "approvalFlow": {
    "requestedBy": "客服小王",
    "approvedBy": "财务张三",
    "approvedAt": "2024-11-30T16:00:00.000Z",
    "comments": "款项已确认到账"
  },
  "automaticPayment": {
    "provider": "alipay",
    "gateway": "alipay_scan",
    "qrCodeUrl": "https://..."
  }
}
```

**使用场景示例**:
```typescript
// 创建项目付款记录
const ProjectPayment = Parse.Object.extend("ProjectPayment");
const payment = new ProjectPayment();
payment.set("project", project.toPointer());
payment.set("company", company.toPointer());
payment.set("type", "advance");
payment.set("stage", "order");
payment.set("method", "bank_transfer");
payment.set("amount", 35000);
payment.set("currency", "CNY");
payment.set("percentage", 30);
payment.set("dueDate", new Date("2024-12-01"));
payment.set("paidBy", customer.toPointer());
payment.set("recordedBy", profile.toPointer());
payment.set("status", "pending");
payment.set("description", "项目首期款");
await payment.save();

// 上传付款凭证并关联
const voucherFile = await uploadVoucherFile(file);
payment.set("voucherFile", voucherFile.toPointer());
payment.set("status", "paid");
payment.set("paymentDate", new Date());
await payment.save();

// 查询项目的所有付款记录
const paymentQuery = new Parse.Query("ProjectPayment");
paymentQuery.equalTo("project", projectId);
paymentQuery.include("voucherFile");
paymentQuery.include("paidBy");
paymentQuery.include("recordedBy");
paymentQuery.descending("paymentDate");
const payments = await paymentQuery.find();

// 查询某个产品的付款记录
const productPaymentQuery = new Parse.Query("ProjectPayment");
productPaymentQuery.equalTo("product", { __type: "Pointer", className: "Product", objectId: productId });
productPaymentQuery.equalTo("status", "paid");
const productPayments = await productPaymentQuery.find();

// 计算项目总付款金额
const totalPaid = payments.reduce((sum, payment) => {
  return sum + (payment.get("status") === "paid" ? payment.get("amount") : 0);
}, 0);
```

---

### 8. ProjectFeedback（客户反馈表）

**用途**: 记录客户在各阶段的反馈和评价。**已更新支持产品关联**。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "feedback001" |
| project | Pointer | 是 | 所属项目 | → Project |
| **product** | **Pointer** | **否** | **关联空间产品** | **→ Product** |
| customer | Pointer | 是 | 反馈客户 | → ContactInfo |
| stage | String | 是 | 反馈阶段 | "建模" / "渲染" |
| feedbackType | String | 是 | 反馈类型 | "suggestion" / "complaint" |
| content | String | 是 | 反馈内容 | "客厅颜色希望再暖一些" |
| rating | Number | 否 | 评分(1-5) | 4 |
| status | String | 是 | 处理状态 | "待处理" / "已解决" |
| data | Object | 否 | 扩展数据 | { response, attachments, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

---

## Product表统一空间管理的优势

### 1. 行业语义清晰
- **Product即空间设计产品**：符合家装设计行业的业务理解
- **产品化管理**：每个空间都是一个独立的设计产品
- **商业逻辑一致**：报价、交付、评价都围绕产品展开

### 2. 数据结构极简
- **单表多能**：Product表承载空间管理的所有核心功能
- **文件分类管理**：通过ProjectFile.category区分不同类型文件
- **设计师直连**：通过Product.profile直接关联负责设计师

### 3. 使用场景优化
- **产品清单**：通过Product表直接获得项目的空间设计产品清单
- **交付物管理**：通过Product.fileUrl和ProjectFile管理交付物
- **报价管理**：通过Product.quotation管理产品级报价

### 4. 实现示例对比

**原有方式（多表操作）**:
```typescript
// 需要查询多个表
const spaces = await spaceQuery.equalTo("project", projectId).find();
const quotations = await quotationQuery.equalTo("project", projectId).find();
const panoramas = await panoramaQuery.equalTo("project", projectId).find();
const deliveries = await deliveryQuery.equalTo("project", projectId).find();
```

**Product表方式（单表操作）**:
```typescript
// 一次查询获得所有空间设计产品
const products = await productQuery.equalTo("project", projectId).find();
// 每个product包含完整的space、quotation、requirements信息
// 文件通过ProjectFile按category分类查询
```
