--- 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 指向当前帐套 ```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