---
category: schema
title: YSS项目Parse Server数据范式
subtitle: 映三色设计师项目管理系统完整数据表结构
name: 'yss-schemas'
label: database
---

# Parse Server 数据范式 - 映三色项目管理系统

## 概述

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

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

---

## 数据表关系图


- 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)
}

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(Product, "Product\n产品即交付物表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(company, Pointer→Company)
    FIELD(stage, String)
    FIELD(processType, String)
    FIELD(space, String)
    FIELD(fileUrl, String)
    FIELD(reviewStatus, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectFile, "ProjectFile\n项目文件表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(uploadedBy, Pointer→Profile)
    FIELD(fileType, String)
    FIELD(fileUrl, String)
    FIELD(fileName, String)
    FIELD(fileSize, Number)
    FIELD(stage, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 财务模块 ============


TABLE(ProjectSettlement, "ProjectSettlement\n结算记录表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(company, Pointer→Company)
    FIELD(stage, String)
    FIELD(amount, Number)
    FIELD(percentage, Number)
    FIELD(status, String)
    FIELD(dueDate, Date)
    FIELD(settledAt, Date)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectVoucher, "ProjectVoucher\n付款凭证表") {
    FIELD(objectId, String)
    FIELD(settlement, Pointer→ProjectSettlement)
    FIELD(project, Pointer→Project)
    FIELD(amount, Number)
    FIELD(voucherUrl, String)
    FIELD(recognizedInfo, Object)
    FIELD(verifiedBy, Pointer→Profile)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

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

TABLE(ProductCheck, "ProductCheck\n产品质量检查表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(checkType, String)
    FIELD(checkedBy, Pointer→Profile)
    FIELD(checkedAt, Date)
    FIELD(isPassed, Boolean)
    FIELD(items, Array)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

TABLE(ProjectIssue, "ProjectIssue\n异常记录表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(reportedBy, Pointer→Profile)
    FIELD(exceptionType, String)
    FIELD(severity, String)
    FIELD(description, String)
    FIELD(status, String)
    FIELD(resolution, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}

' ============ 跟进记录 ============
TABLE(ContactFollow, "ContactFollow\n跟进记录表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    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 : 项目团队

' 交付与财务
Project "1" --> "n" Product : 交付物
Project "1" --> "n" ProjectFile : 项目文件
Project "1" --> "n" ProjectSettlement : 结算记录
ProjectSettlement "1" --> "n" ProjectVoucher : 付款凭证

' 质量与沟通
Project "1" --> "n" ProjectFeedback : 客户反馈
Project "1" --> "n" ProductCheck : 质量检查
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 |

**data字段扩展示例**:
```json
{
  "settings": {
    "timezone": "Asia/Shanghai",
    "currency": "CNY",
    "workingHours": { "start": "09:00", "end": "18:00" }
  },
  "modules": ["project", "crm", "finance", "hr"],
  "wxwork": {
    "agentId": "1000002",
    "secret": "***"
  }
}
```

**索引建议**:
- `objectId` (主键)
- `corpId`
- `name`

---

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

**data字段扩展示例**:
```json
{
  "avatar": "https://...",
  "email": "zhangsan@example.com",
  "department": "设计一组",
  "position": "高级设计师",
  "skills": ["建模", "渲染", "软装"],
  "workload": {
    "currentProjects": 5,
    "maxCapacity": 8,
    "utilizationRate": 0.625
  },
  "performance": {
    "completedProjects": 120,
    "averageRating": 4.8,
    "onTimeRate": 0.95
  }
}
```

**使用场景**:
```typescript
// 获取当前登录员工
const profileId = localStorage.getItem("Parse/ProfileId");
const query = new Parse.Query("Profile");
const profile = await query.get(profileId);

// 查询设计师列表
const designerQuery = new Parse.Query("Profile");
designerQuery.equalTo("company", companyId);
designerQuery.equalTo("roleName", "组员");
designerQuery.notEqualTo("isDeleted", true);
const designers = await designerQuery.find();

// 查询客服人员
const csQuery = new Parse.Query("Profile");
csQuery.equalTo("roleName", "客服");
csQuery.equalTo("company", companyId);
```

**索引建议**:
- `company + isDeleted`
- `userId + company`
- `roleName + company`
- `mobile`

---

### 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 |

**source 枚举值**:
- `朋友圈`: 微信朋友圈引流
- `信息流`: 广告投放
- `转介绍`: 老客户推荐
- `其他`: 其他渠道

**data字段扩展示例**:
```json
{
  "avatar": "https://...",
  "wechat": "lisi_wx",
  "type": "new",
  "tags": {
    "needType": "全案",
    "preference": "现代简约",
    "colorAtmosphere": "明亮温馨",
    "budget": { "min": 50000, "max": 100000 }
  },
  "demandType": "价格敏感",
  "preferenceTags": ["环保材料", "智能家电", "大窗户"],
  "followUpStatus": "待报价",
  "remark": "要求使用环保材料，对价格比较敏感",
  "follow_user": [{
    "userid": "zhangsan",
    "remark": "李总",
    "createtime": 1605171726,
    "tags": [...]
  }]
}
```

**使用场景**:
```typescript
// 搜索客户
const searchQuery = new Parse.Query("ContactInfo");
searchQuery.equalTo("company", companyId);
searchQuery.matches("name", keyword, "i");
searchQuery.matches("mobile", keyword, "i");

// 同步企微外部联系人
const contactInfo = await wxwork.syncContact(externalContact);

// 查询客户的所有项目
const projectQuery = new Parse.Query("Project");
projectQuery.equalTo("customer", contactInfo.toPointer());
projectQuery.notEqualTo("isDeleted", true);
```

**索引建议**:
- `company + isDeleted`
- `external_userid + company`
- `mobile + company`
- `source + company`

---

### 4. GroupChat（企微群聊表）

**用途**: 存储企业微信群聊信息，支持与项目关联。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "group001" |
| chat_id | String | 是 | 企微群聊ID | "wrxxx" |
| name | String | 是 | 群聊名称 | "李总-现代简约全案" |
| company | Pointer | 是 | 所属企业 | → Company |
| project | Pointer | 否 | 关联项目 | → Project |
| member_list | Array | 否 | 成员列表 | [{userid: "zhangsan", ...}] |
| joinUrl | String | 否 | 入群链接 | "https://..." |
| data | Object | 否 | 扩展数据 | { owner, notice, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**data字段扩展示例**:
```json
{
  "owner": "zhangsan",
  "notice": "本群为李总项目专属群，请及时沟通设计需求",
  "member_version": "1234567890",
  "joinQrcode": "https://...",
  "create_time": 1605171726
}
```

**与Project的关系**:
- **简单模式**: GroupChat.project 直接指向 Project（一个群当前进行中的项目）
- **复杂模式**: 通过 ProjectGroup 中间表实现多对多（一个项目多个群，或多个项目一个群）

**使用场景**:
```typescript
// 从企微会话中获取群聊并同步
const { GroupChat } = await wxwork.getCurrentChatObject();
console.log("当前群聊:", GroupChat.get("name"));
console.log("关联项目:", GroupChat.get("project")?.id);

// 查询项目的所有群聊
const groupQuery = new Parse.Query("GroupChat");
groupQuery.equalTo("project", projectId);
groupQuery.notEqualTo("isDeleted", true);
const groups = await groupQuery.find();

// 创建项目群聊
const GroupChat = Parse.Object.extend("GroupChat");
const group = new GroupChat();
group.set("chat_id", chatId);
group.set("name", "项目群");
group.set("company", company.toPointer());
group.set("project", project.toPointer());
await group.save();
```

**索引建议**:
- `chat_id + company` (联合唯一)
- `project + isDeleted`
- `company + isDeleted`

---

### 5. ProjectGroup（项目群组关联表）

**用途**: 实现项目与群聊的多对多关系（高级场景）。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "pg001" |
| project | Pointer | 是 | 关联项目 | → Project |
| groupChat | Pointer | 是 | 关联群聊 | → GroupChat |
| isPrimary | Boolean | 否 | 是否主群 | true |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |

**使用场景**:
```typescript
// 查询项目的所有群聊（通过中间表）
const pgQuery = new Parse.Query("ProjectGroup");
pgQuery.equalTo("project", projectId);
pgQuery.include("groupChat");
const pgs = await pgQuery.find();
const groups = pgs.map(pg => pg.get("groupChat"));

// 查询群聊关联的所有项目
const pgQuery2 = new Parse.Query("ProjectGroup");
pgQuery2.equalTo("groupChat", groupChatId);
pgQuery2.include("project");
const projects = (await pgQuery2.find()).map(pg => pg.get("project"));
```

**索引建议**:
- `project + groupChat` (联合唯一)
- `groupChat`
- `isPrimary`

---

### 6. 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 |

**status 枚举值**:
- `待分配`: 新建项目，等待分配设计师
- `进行中`: 设计师已开始工作
- `已完成`: 项目交付完成
- `已暂停`: 客户要求暂停
- `已延期`: 超过原定截止时间
- `已取消`: 项目取消

**currentStage 枚举值**:
- `订单分配`: 客服下单，待分配设计师
- `需求沟通`: 设计师与客户沟通需求
- `方案确认`: 设计方案等待客户确认
  - 并列任务
    - `建模`: 3D建模阶段
    - `软装`: 软装设计阶段
    - `渲染`: 效果图渲染阶段
    - `后期`: 后期处理与优化
- `尾款结算`: 等待客户支付尾款
- `客户评价`: 客户评价阶段
- `投诉处理`: 客户投诉处理

**data字段扩展示例**:
```json
{
  "projectCode": "YSS-2024-001",
  "estimatedCompletionDate": "2024-12-20T00:00:00.000Z",
  "actualCompletionDate": null,
  "customerServiceId": "prof002",
  "priority": "high",
  "tags": ["全案", "现代简约", "120平"],
  "stageHistory": [
    {
      "stage": "订单分配",
      "startTime": "2024-10-01T09:00:00.000Z",
      "endTime": "2024-10-01T10:30:00.000Z",
      "duration": 1.5,
      "status": "completed",
      "operator": { "id": "prof002", "name": "王客服", "roleName": "客服" }
    }
  ],
  "workflow": {
    "overallProgress": 45,
    "estimatedHours": 160,
    "actualHours": 72
  }
}
```

**使用场景**:
```typescript
// 创建项目
const Project = Parse.Object.extend("Project");
const project = new Project();
project.set("title", "李总现代简约全案");
project.set("company", company.toPointer());
project.set("customer", customer.toPointer());
project.set("status", "待分配");
project.set("currentStage", "订单分配");
await project.save();

// 查询设计师的项目列表
const query = new Parse.Query("Project");
query.equalTo("assignee", profileId);
query.containedIn("status", ["待分配", "进行中"]);
query.notEqualTo("isDeleted", true);
query.include("customer", "assignee");
query.descending("updatedAt");

// 更新项目阶段
project.set("currentStage", "建模");
project.set("status", "进行中");
await project.save();
```

**索引建议**:
- `company + isDeleted`
- `assignee + status`
- `customer + isDeleted`
- `currentStage + status`
- `deadline`
- `updatedAt` (降序)

---

### 7. ProjectRequirement（需求信息表）

**用途**: 存储项目的详细需求信息，包括空间信息、设计需求、素材分析等。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "req001" |
| project | Pointer | 是 | 所属项目 | → Project |
| company | Pointer | 是 | 所属企业 | → Company |
| spaces | Array | 否 | 空间信息 | [{name: "客厅", area: 40, ...}] |
| designRequirements | Object | 否 | 设计需求 | { style: [...], color: "...", ... } |
| materialAnalysis | Object | 否 | 素材分析结果 | { colorAnalysis: {...}, ... } |
| data | Object | 否 | 扩展数据 | { functionalRequirements, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**spaces 结构示例**:
```json
[
  {
    "id": "space001",
    "name": "客厅",
    "area": 40,
    "dimensions": {
      "length": 8,
      "width": 5,
      "height": 2.8
    },
    "existingConditions": ["采光好", "南向"],
    "requirements": ["需要大储物空间", "开放式设计"]
  }
]
```

**designRequirements 结构示例**:
```json
{
  "style": ["现代简约", "北欧"],
  "colorPreference": "明亮温馨",
  "materialPreference": ["实木", "环保材料"],
  "specialRequirements": ["智能家电", "宠物友好设计"]
}
```

**materialAnalysis 结构示例**:
```json
{
  "colorAnalysis": {
    "primaryHue": 180,
    "saturation": 45,
    "temperature": "冷色调",
    "colorDistribution": [
      { "hex": "#F5F5DC", "percentage": 40, "name": "米白色" },
      { "hex": "#8B4513", "percentage": 30, "name": "原木色" }
    ]
  },
  "formAnalysis": {
    "lineType": "straight",
    "complexity": 60,
    "symmetry": "symmetric"
  },
  "textureAnalysis": {
    "dominantTexture": "光滑",
    "materials": ["实木", "大理石", "布艺"]
  },
  "lightingAnalysis": {
    "naturalLight": "充足",
    "lightColor": "暖白",
    "lightIntensity": "中等"
  }
}
```

**索引建议**:
- `project` (唯一)
- `company + isDeleted`

---

### 8. ProjectTeam（项目团队表）

**用途**: 管理项目团队成员及其角色分工。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "team001" |
| project | Pointer | 是 | 所属项目 | → Project |
| profile | Pointer | 是 | 团队成员 | → Profile |
| role | String | 是 | 项目角色 | "主设计师" / "建模师" / "渲染师" |
| workload | Number | 否 | 工作量占比 | 0.6 |
| data | Object | 否 | 扩展数据 | { spaces: [...], performance: {...} } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 加入时间 | 2024-01-01T00:00:00.000Z |

**role 枚举值**:
- `主设计师`: 项目负责人
- `建模师`: 负责3D建模
- `渲染师`: 负责效果图渲染
- `软装师`: 负责软装设计
- `助理`: 辅助工作

**data字段扩展示例**:
```json
{
  "spaces": ["客厅", "主卧"],
  "assignedAt": "2024-10-17T10:00:00.000Z",
  "assignedBy": "prof001",
  "performance": {
    "quality": 4.5,
    "efficiency": 4.0,
    "communication": 5.0,
    "notes": "设计方案客户满意度高，沟通及时"
  },
  "review": {
    "reviewedAt": "2024-11-15T10:00:00.000Z",
    "reviewer": "prof003",
    "comments": "表现优秀，按时完成任务"
  }
}
```

**使用场景**:
```typescript
// 添加团队成员
const ProjectTeam = Parse.Object.extend("ProjectTeam");
const team = new ProjectTeam();
team.set("project", project.toPointer());
team.set("profile", designer.toPointer());
team.set("role", "建模师");
team.set("workload", 0.4);
await team.save();

// 查询项目团队
const teamQuery = new Parse.Query("ProjectTeam");
teamQuery.equalTo("project", projectId);
teamQuery.include("profile");
teamQuery.notEqualTo("isDeleted", true);
```

**索引建议**:
- `project + isDeleted`
- `profile + project` (联合唯一)

---

### 9. Product（交付物表）

**用途**: 记录项目各阶段的交付物，如效果图、施工图等。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "deliv001" |
| project | Pointer | 是 | 所属项目 | → Project |
| company | Pointer | 是 | 所属企业 | → Company |
| stage | String | 是 | 所属阶段 | "建模" / "渲染" / "后期" |
| processType | String | 否 | 工序类型 | "modeling" / "rendering" |
| space | String | 否 | 空间名称 | "客厅" / "主卧" |
| fileUrl | String | 是 | 文件URL | "https://..." |
| reviewStatus | String | 是 | 审核状态 | "pending" / "approved" |
| data | Object | 否 | 扩展数据 | { thumbnailUrl, version, ... } |
| quotation | Object | 否 | 产品报价 | { price, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**stage 枚举值**: 与 Project.currentStage 一致

**processType 枚举值**:
- `modeling`: 建模
- `softDecor`: 软装
- `rendering`: 渲染
- `postProcess`: 后期

**reviewStatus 枚举值**:
- `pending`: 待审核
- `approved`: 已通过
- `rejected`: 已驳回
- `revision_required`: 需要修改

**data字段扩展示例**:
```json
{
  "fileName": "客厅效果图v2.jpg",
  "fileSize": 2048576,
  "fileType": "image",
  "format": "jpg",
  "thumbnailUrl": "https://...",
  "uploadedBy": "prof001",
  "version": 2,
  "reviewNotes": "色调需要再暖一些",
  "progress": 100
}
```

**quotation字段扩展说明**
```
Product.quotation 产品报价字段

**用途**: 记录项目报价信息和审批流程。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| price | Number | 是 | 总金额 | 1200 |
| currency | String | 是 | 货币单位 | "CNY" |
| breakdown | Object | 否 | 费用明细 | { design: 30000, ... } |
| status | String | 是 | 审核状态 | "待审核" / "已通过" |
| approvedBy | Pointer | 否 | 审批人 | → Profile |
| data | Object | 否 | 扩展数据 | { discount, notes, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**status 枚举值**:
- `待审核`: 等待财务或管理员审核
- `已通过`: 审核通过
- `已驳回`: 审核未通过
- `已修改`: 报价已修改，需重新审核

**breakdown 结构示例**:
``\`json
{
  "design": 30000,
  "modeling": 20000,
  "rendering": 15000,
  "softDecor": 10000,
  "postProcessing": 5000
}
``\`

**data字段扩展示例**:
``\`json
{
  "discount": 0.9,
  "discountReason": "老客户优惠",
  "notes": "含3次修改",
  "validUntil": "2024-11-01T00:00:00.000Z",
  "approvalHistory": [
    {
      "approver": "prof003",
      "action": "approved",
      "timestamp": "2024-10-15T10:00:00.000Z",
      "comment": "价格合理"
    }
  ]
}
``\`

**索引建议**:
- `project + isDeleted`
- `status + company`
- `approvedBy`

```

**索引建议**:
- `project + stage + isDeleted`
- `project + space`
- `reviewStatus + project`

---

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

**用途**: 存储项目相关的所有文件，如CAD图纸、参考图等。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "file001" |
| project | Pointer | 是 | 所属项目 | → Project |
| uploadedBy | Pointer | 是 | 上传人 | → Profile |
| fileType | String | 是 | 文件类型 | "cad" / "reference" / "document" |
| fileUrl | String | 是 | 文件URL | "https://..." |
| fileName | String | 是 | 文件名 | "户型图.dwg" |
| fileSize | Number | 否 | 文件大小(字节) | 1024000 |
| stage | String | 否 | 关联阶段 | "需求沟通" |
| data | Object | 否 | 扩展数据 | { thumbnailUrl, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

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

**索引建议**:
- `project + fileType + isDeleted`
- `uploadedBy + project`

---

### 12. ProjectSettlement （结算记录表）

**用途**: 记录项目分阶段结算信息。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "settle001" |
| project | Pointer | 是 | 所属项目 | → Project |
| company | Pointer | 是 | 所属企业 | → Company |
| stage | String | 是 | 结算阶段 | "定金" / "进度款" / "尾款" |
| amount | Number | 是 | 结算金额 | 24000 |
| percentage | Number | 否 | 占总额百分比 | 0.3 |
| status | String | 是 | 结算状态 | "待结算" / "已结算" |
| dueDate | Date | 否 | 应付日期 | 2024-10-20T00:00:00.000Z |
| settledAt | Date | 否 | 实际结算时间 | 2024-10-18T14:30:00.000Z |
| data | Object | 否 | 扩展数据 | { paymentMethod, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**stage 枚举值**:
- `定金`: 项目启动定金（通常30%）
- `进度款`: 中期进度款（通常40%）
- `尾款`: 项目完成尾款（通常30%）

**status 枚举值**:
- `待结算`: 等待客户付款
- `已结算`: 已收到款项
- `逾期`: 超过应付日期未付款

**data字段扩展示例**:
```json
{
  "paymentMethod": "微信转账",
  "transactionId": "WX20241018143000",
  "notes": "已收到定金",
  "invoiceRequired": true,
  "invoiceIssued": false
}
```

**索引建议**:
- `project + stage`
- `status + company`
- `dueDate`

---

### 13. ProjectVoucher（付款凭证表）

**用途**: 记录客户付款凭证及OCR识别结果。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "voucher001" |
| settlement | Pointer | 是 | 关联结算记录 | → ProjectSettlement |
| project | Pointer | 是 | 所属项目 | → Project |
| amount | Number | 是 | 付款金额 | 24000 |
| voucherUrl | String | 是 | 凭证图片URL | "https://..." |
| recognizedInfo | Object | 否 | OCR识别结果 | { amount: 24000, ... } |
| verifiedBy | Pointer | 否 | 核验人 | → Profile |
| data | Object | 否 | 扩展数据 | { paymentTime, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**recognizedInfo 结构示例**:
```json
{
  "amount": 24000,
  "paymentTime": "2024-10-18 14:30:00",
  "paymentMethod": "微信支付",
  "transactionId": "WX20241018143000",
  "payerName": "李四",
  "confidence": 0.95
}
```

**索引建议**:
- `settlement + project`
- `project + isDeleted`

---

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

**用途**: 记录客户在各阶段的反馈和评价。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "feedback001" |
| project | Pointer | 是 | 所属项目 | → Project |
| 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 |

**feedbackType 枚举值**:
- `suggestion`: 建议
- `complaint`: 投诉
- `praise`: 表扬
- `question`: 疑问

**status 枚举值**:
- `待处理`: 新反馈，待响应
- `处理中`: 正在处理
- `已解决`: 问题已解决
- `已关闭`: 反馈已关闭

**data字段扩展示例**:
```json
{
  "isSatisfied": false,
  "problemLocation": "客厅沙发区",
  "expectedEffect": "希望更温馨舒适",
  "referenceCase": "case123",
  "response": "已调整色调，请查看最新版本",
  "respondedBy": "prof001",
  "respondedAt": "2024-10-16T10:00:00.000Z",
  "attachments": ["https://..."]
}
```

**索引建议**:
- `project + status + isDeleted`
- `customer + project`
- `feedbackType + stage`

---

### 15. ProductCheck（产品质量检查表）

**用途**: 记录项目质量检查结果，如模型检查、效果图审核等。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "check001" |
| project | Pointer | 是 | 所属项目 | → Project |
| checkType | String | 是 | 检查类型 | "model" / "render" / "final" |
| checkedBy | Pointer | 是 | 检查人 | → Profile |
| checkedAt | Date | 是 | 检查时间 | 2024-10-15T10:00:00.000Z |
| isPassed | Boolean | 是 | 是否通过 | true |
| items | Array | 否 | 检查项目 | [{name: "尺寸准确性", passed: true}] |
| data | Object | 否 | 扩展数据 | { notes, images, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**checkType 枚举值**:
- `model`: 模型检查
- `render`: 渲染效果检查
- `final`: 最终交付检查

**items 结构示例**:
```json
[
  {
    "id": "item001",
    "name": "尺寸准确性",
    "category": "基础",
    "isPassed": true,
    "notes": "符合要求"
  },
  {
    "id": "item002",
    "name": "材质真实性",
    "category": "渲染质量",
    "isPassed": false,
    "notes": "地板材质需要调整"
  }
]
```

**索引建议**:
- `project + checkType + isDeleted`
- `checkedBy + isPassed`

---

### 16. ProjectIssue（异常记录表）

**用途**: 记录项目执行过程中的异常情况。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "except001" |
| project | Pointer | 是 | 所属项目 | → Project |
| reportedBy | Pointer | 是 | 报告人 | → Profile |
| exceptionType | String | 是 | 异常类型 | "failed" / "stuck" / "quality" |
| severity | String | 是 | 严重程度 | "low" / "high" / "critical" |
| description | String | 是 | 异常描述 | "建模软件崩溃导致进度延误" |
| status | String | 是 | 处理状态 | "待处理" / "已解决" |
| resolution | String | 否 | 解决方案 | "重新建模，延期2天" |
| data | Object | 否 | 扩展数据 | { assignedTo, impact, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**exceptionType 枚举值**:
- `failed`: 任务失败
- `stuck`: 进度停滞
- `quality`: 质量问题
- `timeline`: 进度延期
- `resource`: 资源不足
- `other`: 其他

**severity 枚举值**:
- `low`: 低，影响较小
- `medium`: 中，需要关注
- `high`: 高，严重影响进度
- `critical`: 紧急，项目面临风险

**data字段扩展示例**:
```json
{
  "assignedTo": "prof003",
  "stage": "建模",
  "impact": "预计延期2天",
  "preventiveMeasures": ["增加备份频率", "升级建模软件"],
  "resolvedAt": "2024-10-16T15:00:00.000Z"
}
```

**索引建议**:
- `project + status + isDeleted`
- `exceptionType + severity`
- `reportedBy`

---

### 17. ContactFollow（跟进记录表）

**用途**: 记录项目相关的跟进历史。

| 字段名 | 类型 | 必填 | 说明 | 示例值 |
|--------|------|------|------|--------|
| objectId | String | 是 | 主键ID | "comm001" |
| project | Pointer | 是 | 所属项目 | → Project |
| profile | Pointer | 是 | 跟进人 | → Profile |
| contact | Pointer | 是 | 联系人 | → ContactInfo |
| content | String | 是 | 跟进内容 | "客厅效果图已发送，请查收" |
| type | String | 是 | 跟进类型 | "message" / "call" / "meeting" |
| stage | String | 否 | 关联阶段 | "渲染" |
| attachments | Array | 否 | 附件列表 | ["https://..."] |
| data | Object | 否 | 扩展数据 | { isRead, priority, ... } |
| isDeleted | Boolean | 否 | 软删除标记 | false |
| createdAt | Date | 自动 | 创建时间 | 2024-01-01T00:00:00.000Z |
| updatedAt | Date | 自动 | 更新时间 | 2024-01-01T00:00:00.000Z |

**type 枚举值**:
- `message`: 文字消息
- `call`: 电话沟通
- `meeting`: 会议
- `email`: 邮件
- `feedback`: 客户反馈

**索引建议**:
- `project + createdAt`
- `sender + project`
- `stage`

---

## 数据范式设计原则

### 1. 租户隔离
所有业务表都包含 `company` 字段，确保多租户数据隔离：
```typescript
query.equalTo("company", companyId);
```

### 2. 软删除
所有表都包含 `isDeleted` 字段，避免数据永久删除：
```typescript
query.notEqualTo("isDeleted", true);
```

### 3. 扩展性
使用 `data` Object 字段存储灵活的扩展数据，避免频繁修改表结构：
```typescript
// 读取扩展数据
const extraData = project.get("data");
const workflow = extraData.workflow;

// 写入扩展数据
project.set("data", {
  ...project.get("data"),
  workflow: { overallProgress: 50 }
});
```

### 4. 关联查询优化
使用 `include` 减少查询次数：
```typescript
query.include("customer", "assignee", "project.customer");
```

### 5. 索引策略
- 每个表的 `company + isDeleted` 必建复合索引
- 所有 Pointer 字段必建索引
- 高频查询字段建立索引
- `updatedAt` / `createdAt` 建立降序索引

---

## 使用场景示例

### 场景1: 从企微群聊快速加载项目

```typescript
// 1. 从企微会话获取群聊
const { GroupChat } = await wxwork.getCurrentChatObject();

// 2. 直接获取关联项目
const project = GroupChat.get("project");
if (project) {
  await project.fetch({ include: ["customer", "assignee"] });
  console.log("项目:", project.get("title"));
  console.log("客户:", project.get("customer").get("name"));
  console.log("设计师:", project.get("assignee").get("name"));
}
```

### 场景2: 创建项目并关联群聊

```typescript
// 1. 创建客户
const ContactInfo = Parse.Object.extend("ContactInfo");
const customer = new ContactInfo();
customer.set("name", "李总");
customer.set("mobile", "13900139000");
customer.set("company", company.toPointer());
await customer.save();

// 2. 创建项目
const Project = Parse.Object.extend("Project");
const project = new Project();
project.set("title", "李总现代简约全案");
project.set("company", company.toPointer());
project.set("customer", customer.toPointer());
project.set("status", "待分配");
project.set("currentStage", "订单分配");
await project.save();

// 3. 关联群聊
const groupChat = await wxwork.syncGroupChat(currentChat.group);
groupChat.set("project", project.toPointer());
await groupChat.save();
```

### 场景3: 查询设计师的工作负载

```typescript
// 查询设计师当前进行中的项目
const query = new Parse.Query("Project");
query.equalTo("assignee", designerId);
query.containedIn("status", ["待分配", "进行中"]);
query.notEqualTo("isDeleted", true);
query.include("customer");
const projects = await query.find();

console.log(`当前负载: ${projects.length}个项目`);

// 统计各阶段项目数
const stageCount = {};
projects.forEach(p => {
  const stage = p.get("currentStage");
  stageCount[stage] = (stageCount[stage] || 0) + 1;
});
```

### 场景4: 项目阶段推进

```typescript
// 推进项目到下一阶段
const project = await new Parse.Query("Project").get(projectId);
const currentStage = project.get("currentStage");
const data = project.get("data") || {};

// 更新阶段历史
const stageHistory = data.stageHistory || [];
stageHistory.push({
  stage: currentStage,
  startTime: lastStartTime,
  endTime: new Date(),
  status: "completed",
  operator: { id: profileId, name: "张设计师", role: "组员" }
});

// 更新当前阶段
project.set("currentStage", "渲染");
project.set("data", {
  ...data,
  stageHistory
});
await project.save();
```

---

## 数据迁移与兼容性

### 从现有系统迁移

如果系统中已有 `Contact` 表（企微外部联系人），建议：

1. **保留 Contact 表**用于企微原始数据同步
2. **ContactInfo 表**作为业务客户表
3. **数据同步脚本**：定期从 Contact 同步到 ContactInfo

```typescript
// 同步脚本示例
const contacts = await new Parse.Query("Contact")
  .equalTo("company", companyId)
  .limit(1000)
  .find();

for (const contact of contacts) {
  // 查询或创建 ContactInfo
  let contactInfo = await new Parse.Query("ContactInfo")
    .equalTo("external_userid", contact.get("external_userid"))
    .equalTo("company", companyId)
    .first();

  if (!contactInfo) {
    contactInfo = new Parse.Object("ContactInfo");
    contactInfo.set("company", company.toPointer());
    contactInfo.set("external_userid", contact.get("external_userid"));
  }

  contactInfo.set("name", contact.get("name"));
  contactInfo.set("mobile", contact.get("mobile"));
  contactInfo.set("data", contact.get("data"));
  await contactInfo.save();
}
```

---

## 总结

本数据范式具有以下特点：

✅ **统一规范**: Profile（员工）、ContactInfo（客户）统一管理
✅ **灵活关联**: Project ←→ GroupChat 支持简单和复杂场景
✅ **角色清晰**: roleName 字段区分客服/组员/组长
✅ **扩展性强**: data Object 字段支持灵活扩展
✅ **多租户**: company 字段确保租户隔离
✅ **软删除**: isDeleted 字段保护数据安全
✅ **企微集成**: 与企业微信深度集成

通过实施这个数据范式，可以实现：
- 从企微群聊快速访问项目信息
- 统一的员工和客户管理
- 完整的项目生命周期跟踪
- 灵活的角色权限控制
- 高效的数据查询和统计

---

**文档版本**: v1.0
**最后更新**: 2025-10-15
**维护者**: YSS Development Team
