schemas.md 41 KB


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 组长
    • company Pointer 指向当前帐套

      @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(company, Pointer→Company)
      FIELD(userId, String)
      FIELD(role, 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(Communication, "Communication\n沟通记录表") {
      FIELD(objectId, String)
      FIELD(project, Pointer→Project)
      FIELD(sender, Pointer→Profile/ContactInfo)
      FIELD(content, String)
      FIELD(communicationType, String)
      FIELD(relatedStage, 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" Communication : 沟通记录
      
      ' 群聊关系
      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字段扩展示例:

    {
      "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"
    company Pointer 所属企业 → Company
    userId String 企微UserID "zhangsan"
    role 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字段扩展示例:

    {
      "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
      }
    }
    

    使用场景:

    // 获取当前登录员工
    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("role", "组员");
    designerQuery.notEqualTo("isDeleted", true);
    const designers = await designerQuery.find();
    
    // 查询客服人员
    const csQuery = new Parse.Query("Profile");
    csQuery.equalTo("role", "客服");
    csQuery.equalTo("company", companyId);
    

    索引建议:

    • company + isDeleted
    • userId + company
    • role + 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字段扩展示例:

    {
      "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": [...]
      }]
    }
    

    使用场景:

    // 搜索客户
    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字段扩展示例:

    {
      "owner": "zhangsan",
      "notice": "本群为李总项目专属群,请及时沟通设计需求",
      "member_version": "1234567890",
      "joinQrcode": "https://...",
      "create_time": 1605171726
    }
    

    与Project的关系:

    • 简单模式: GroupChat.project 直接指向 Project(一个群当前进行中的项目)
    • 复杂模式: 通过 ProjectGroup 中间表实现多对多(一个项目多个群,或多个项目一个群)

    使用场景:

    // 从企微会话中获取群聊并同步
    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

    使用场景:

    // 查询项目的所有群聊(通过中间表)
    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字段扩展示例:

    {
      "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": "王客服", "role": "客服" }
        }
      ],
      "workflow": {
        "overallProgress": 45,
        "estimatedHours": 160,
        "actualHours": 72
      }
    }
    

    使用场景:

    // 创建项目
    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 结构示例:

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

    designRequirements 结构示例:

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

    materialAnalysis 结构示例:

    {
      "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
    isDeleted Boolean 软删除标记 false
    createdAt Date 自动 加入时间 2024-01-01T00:00:00.000Z

    role 枚举值:

    • 主设计师: 项目负责人
    • 建模师: 负责3D建模
    • 渲染师: 负责效果图渲染
    • 软装师: 负责软装设计
    • 助理: 辅助工作

    使用场景:

    // 添加团队成员
    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字段扩展示例:

    {
      "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字段扩展示例:

    {
      "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 结构示例:

    {
      "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字段扩展示例:

    {
      "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 结构示例:

    [
      {
        "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字段扩展示例:

    {
      "assignedTo": "prof003",
      "relatedStage": "建模",
      "impact": "预计延期2天",
      "preventiveMeasures": ["增加备份频率", "升级建模软件"],
      "resolvedAt": "2024-10-16T15:00:00.000Z"
    }
    

    索引建议:

    • project + status + isDeleted
    • exceptionType + severity
    • reportedBy

    17. Communication(沟通记录表)

    用途: 记录项目相关的沟通历史。

    字段名 类型 必填 说明 示例值
    objectId String 主键ID "comm001"
    project Pointer 所属项目 → Project
    sender Pointer 发送人 → Profile / ContactInfo
    content String 沟通内容 "客厅效果图已发送,请查收"
    communicationType String 沟通类型 "message" / "call" / "meeting"
    relatedStage 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

    communicationType 枚举值:

    • message: 文字消息
    • call: 电话沟通
    • meeting: 会议
    • email: 邮件
    • feedback: 客户反馈

    索引建议:

    • project + createdAt
    • sender + project
    • relatedStage

    数据范式设计原则

    1. 租户隔离

    所有业务表都包含 company 字段,确保多租户数据隔离:

    query.equalTo("company", companyId);
    

    2. 软删除

    所有表都包含 isDeleted 字段,避免数据永久删除:

    query.notEqualTo("isDeleted", true);
    

    3. 扩展性

    使用 data Object 字段存储灵活的扩展数据,避免频繁修改表结构:

    // 读取扩展数据
    const extraData = project.get("data");
    const workflow = extraData.workflow;
    
    // 写入扩展数据
    project.set("data", {
      ...project.get("data"),
      workflow: { overallProgress: 50 }
    });
    

    4. 关联查询优化

    使用 include 减少查询次数:

    query.include("customer", "assignee", "project.customer");
    

    5. 索引策略

    • 每个表的 company + isDeleted 必建复合索引
    • 所有 Pointer 字段必建索引
    • 高频查询字段建立索引
    • updatedAt / createdAt 建立降序索引

    使用场景示例

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

    // 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: 创建项目并关联群聊

    // 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: 查询设计师的工作负载

    // 查询设计师当前进行中的项目
    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: 项目阶段推进

    // 推进项目到下一阶段
    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

      // 同步脚本示例
      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 支持简单和复杂场景 ✅ 角色清晰: role 字段区分客服/组员/组长 ✅ 扩展性强: data Object 字段支持灵活扩展 ✅ 多租户: company 字段确保租户隔离 ✅ 软删除: isDeleted 字段保护数据安全 ✅ 企微集成: 与企业微信深度集成

    通过实施这个数据范式,可以实现:

    • 从企微群聊快速访问项目信息
    • 统一的员工和客户管理
    • 完整的项目生命周期跟踪
    • 灵活的角色权限控制
    • 高效的数据查询和统计

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