title: YSS项目管理系统数据库表结构文档 version: v1.0 date: 2025-10-21
映三色项目管理系统是一个基于企业微信的设计师项目全流程管理平台,支持从客户咨询、订单分配、需求确认、交付执行到售后归档的完整生命周期管理。系统采用多租户架构,以 Company(企业)为核心进行数据隔离,支持客服、设计师、组长等多角色协作。
系统采用单库多租户设计模式:
company 字段关联到企业(Company表)系统共包含 18个核心数据表 + 1个系统表(_User),按功能模块分类如下:
| 分类 | 表数量 | 表名列表 | 
|---|---|---|
| 基础表 | 4 | Company, Department, Profile, ContactInfo | 
| 企微集成 | 2 | GroupChat, ProjectGroup | 
| 项目管理 | 3 | Project, ProjectRequirement, ProjectTeam | 
| 产品空间 | 1 | Product | 
| 文件管理 | 2 | ProjectFile, Attachment | 
| 财务管理 | 1 | ProjectPayment | 
| 质量反馈 | 3 | ProjectFeedback, ProductCheck, ProjectIssue | 
| 沟通跟进 | 1 | ContactFollow | 
| 系统认证 | 1 | _User | 
@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(Department, "Department\n部门表") {
    FIELD(objectId, String)
    FIELD(name, String)
    FIELD(type, String)
    FIELD(leader, Pointer→Profile)
    FIELD(company, Pointer→Company)
    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)
}
' ============ 项目模块 ============
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(profile, Pointer→Profile)
    FIELD(stage, String)
    FIELD(processType, String)
    FIELD(productName, String)
    FIELD(productType, String)
    FIELD(status, String)
    FIELD(fileUrl, String)
    FIELD(reviewStatus, String)
    FIELD(space, Object)
    FIELD(quotation, Object)
    FIELD(requirements, Object)
    FIELD(reviews, Array)
    FIELD(estimatedBudget, Number)
    FIELD(estimatedDuration, Number)
    FIELD(order, Number)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}
' ============ 文件管理 ============
TABLE(ProjectFile, "ProjectFile\n项目文件表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(product, Pointer→Product)
    FIELD(attach, Pointer→Attachment)
    FIELD(uploadedBy, Pointer→Profile)
    FIELD(stage, String)
    FIELD(category, String)
    FIELD(data, Object)
    FIELD(analysis, Object)
    FIELD(isDeleted, Boolean)
}
TABLE(Attachment, "Attachment\n附件表") {
    FIELD(objectId, String)
    FIELD(size, Number)
    FIELD(url, String)
    FIELD(name, String)
    FIELD(mime, String)
    FIELD(md5, String)
    FIELD(metadata, Object)
    FIELD(company, Pointer→Company)
    FIELD(user, Pointer→_User)
}
' ============ 财务模块 ============
TABLE(ProjectPayment, "ProjectPayment\n项目付款表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(company, Pointer→Company)
    FIELD(type, String)
    FIELD(stage, String)
    FIELD(method, String)
    FIELD(amount, Number)
    FIELD(currency, String)
    FIELD(percentage, Number)
    FIELD(paymentDate, Date)
    FIELD(dueDate, Date)
    FIELD(recordedDate, Date)
    FIELD(status, String)
    FIELD(voucherFile, Pointer→ProjectFile)
    FIELD(voucherUrl, String)
    FIELD(transactionId, String)
    FIELD(paymentReference, String)
    FIELD(paidBy, Pointer→ContactInfo)
    FIELD(recordedBy, Pointer→Profile)
    FIELD(verifiedBy, Pointer→Profile)
    FIELD(description, String)
    FIELD(notes, String)
    FIELD(relatedStage, String)
    FIELD(product, Pointer→Product)
    FIELD(autoReminderSent, Boolean)
    FIELD(reminderCount, Number)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}
' ============ 质量与反馈 ============
TABLE(ProjectFeedback, "ProjectFeedback\n客户反馈表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(customer, Pointer→ContactInfo)
    FIELD(product, Pointer→Product)
    FIELD(stage, String)
    FIELD(feedbackType, String)
    FIELD(content, String)
    FIELD(rating, Number)
    FIELD(status, String)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}
TABLE(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(product, Pointer→Product)
    FIELD(creator, Pointer→Profile)
    FIELD(assignee, Pointer→Profile)
    FIELD(title, String)
    FIELD(description, String)
    FIELD(relatedSpace, String)
    FIELD(relatedStage, String)
    FIELD(relatedContentType, String)
    FIELD(relatedFiles, Array)
    FIELD(priority, String)
    FIELD(issueType, String)
    FIELD(dueDate, Date)
    FIELD(status, String)
    FIELD(resolution, String)
    FIELD(lastReminderAt, Date)
    FIELD(reminderCount, Number)
    FIELD(data, Object)
    FIELD(isDeleted, Boolean)
}
' ============ 跟进记录 ============
TABLE(ContactFollow, "ContactFollow\n跟进记录表") {
    FIELD(objectId, String)
    FIELD(project, Pointer→Project)
    FIELD(profile, Pointer→Profile)
    FIELD(contact, Pointer→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 : 负责人
Project "1" --> "1" ProjectRequirement : 需求信息
Project "1" <--> "n" GroupChat : ProjectGroup群聊关联
Project "1" --> "n" ProjectTeam : 项目团队
' Product表统一空间管理关系
Project "1" --> "n" Product : 空间设计产品
Product "n" --> "1" Profile : 负责设计师
Product "1" --> "n" ProjectFile : 产品文件
Product "1" --> "n" ProjectFeedback : 产品反馈
Product "1" --> "n" ProjectIssue : 产品异常
Product "1" --> "n" ContactFollow : 产品跟进
' 交付与财务
Project "1" --> "n" ProjectFile : 项目文件
Project "1" --> "n" ProjectPayment : 项目付款
ProjectPayment "1" --> "1" ProjectFile : 付款凭证
ProjectPayment "1" --> "1" ContactInfo : 付款人
ProjectPayment "1" --> "1" Profile : 记录人/验证人
Product "1" --> "n" ProjectPayment : 产品级付款
' 质量与沟通
Project "1" --> "n" ProjectFeedback : 客户反馈
Project "1" --> "n" ProjectIssue : 异常记录
Project "1" --> "n" ContactFollow : 跟进记录
' 群聊关系
GroupChat "n" --> "1" Company : 所属企业
GroupChat "n" --> "1" Project : 关联项目
' 文件关系
ProjectFile "n" --> "1" Attachment : 附件引用
Attachment "n" --> "1" Company : 所属企业
@enduml
用途: 多租户系统的核心表,所有业务数据通过 company 字段进行租户隔离。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "cDL6R1hgSi" | 
| name | String | 是 | - | 企业名称 | "映三色设计" | 
| corpId | String | 否 | - | 企业微信CorpID | "ww1234567890abcdef" | 
| data | Object | 否 | {} | 扩展数据(配置、模块等) | { settings: {...}, modules: [...] } | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
索引配置:
// 从 scripts/migration/create-schema.js
indexes: [
  { name: 'corpId_index', fields: { corpId: 1 } },
  { name: 'name_index', fields: { name: 1 } }
]
data字段结构示例:
{
  "settings": {
    "timezone": "Asia/Shanghai",
    "currency": "CNY",
    "workingHours": {
      "start": "09:00",
      "end": "18:00"
    }
  },
  "modules": ["project", "customer", "finance"],
  "features": {
    "aiAnalysis": true,
    "wxworkIntegration": true
  },
  "branding": {
    "logo": "https://...",
    "primaryColor": "#3880FF"
  }
}
使用示例:
// 获取当前企业
const cid = localStorage.getItem('company');
const companyQuery = new Parse.Query('Company');
const company = await companyQuery.get(cid);
// 创建新企业
const Company = Parse.Object.extend('Company');
const newCompany = new Company();
newCompany.set('name', '映三色设计');
newCompany.set('corpId', 'ww1234567890abcdef');
newCompany.set('data', {
  settings: { timezone: 'Asia/Shanghai' },
  modules: ['project', 'customer']
});
await newCompany.save();
// 查询所有未删除的企业
const query = new Parse.Query('Company');
query.equalTo('isDeleted', false);
const companies = await query.find();
用途: 存储企业的组织架构,主要用于项目组管理。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "dept001" | 
| name | String | 是 | - | 部门名称 | "设计一组" | 
| type | String | 是 | "project" | 部门类型 | "project" | 
| leader | Pointer | 否 | - | 组长 | → Profile | 
| company | Pointer | 是 | - | 所属企业 | → Company | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
type枚举值:
project: 项目组sales: 销售部finance: 财务部hr: 人事部other: 其他索引配置:
indexes: [
  { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
  { name: 'leader_index', fields: { leader: 1 } }
]
使用示例:
// 创建项目组
const Department = Parse.Object.extend('Department');
const dept = new Department();
dept.set('name', '设计一组');
dept.set('type', 'project');
dept.set('company', company.toPointer());
dept.set('leader', leaderProfile.toPointer());
await dept.save();
// 查询企业的所有项目组
const deptQuery = new Parse.Query('Department');
deptQuery.equalTo('company', company.toPointer());
deptQuery.equalTo('type', 'project');
deptQuery.equalTo('isDeleted', false);
deptQuery.include('leader');
const departments = await deptQuery.find();
// 查询组长负责的部门
const leaderDeptQuery = new Parse.Query('Department');
leaderDeptQuery.equalTo('leader', profileId);
const leaderDepts = await leaderDeptQuery.find();
用途: 统一管理企业员工信息,支持客服、设计师(组员)、组长等多种角色。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "prof001" | 
| name | String | 是 | - | 员工姓名 | "张三" | 
| mobile | String | 否 | - | 手机号 | "13800138000" | 
| department | Pointer | 是 | - | 所属部门 | → Department | 
| company | Pointer | 是 | - | 所属企业 | → Company | 
| userId | String | 否 | - | 企微UserID | "zhangsan" | 
| roleName | String | 是 | - | 员工角色 | "客服" / "组员" / "组长" | 
| data | Object | 否 | {} | 扩展数据 | { avatar, skills, ... } | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
roleName枚举值:
客服: 客户服务人员,负责接单、跟进组员: 设计师,负责具体设计工作组长: 团队负责人,负责审核、分配财务: 财务人员人事: 人事人员管理员: 系统管理员索引配置:
indexes: [
  { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
  { name: 'userId_company', fields: { userId: 1, company: 1 }, unique: true },
  { name: 'roleName_company', fields: { roleName: 1, company: 1 } },
  { name: 'mobile_company', fields: { mobile: 1, company: 1 } }
]
data字段结构示例:
{
  "avatar": "https://...",
  "gender": "male",
  "email": "zhangsan@example.com",
  "skills": ["建模", "渲染", "软装"],
  "level": "中级设计师",
  "joinDate": "2024-01-01",
  "wxworkInfo": {
    "userid": "zhangsan",
    "department": [1, 2],
    "position": "设计师",
    "mobile": "13800138000",
    "avatar": "https://..."
  },
  "workload": {
    "currentProjects": 3,
    "completedProjects": 15,
    "averageQuality": 4.5
  }
}
使用示例:
// 创建员工档案
const Profile = Parse.Object.extend('Profile');
const profile = new Profile();
profile.set('name', '张三');
profile.set('mobile', '13800138000');
profile.set('department', department.toPointer());
profile.set('company', company.toPointer());
profile.set('userId', 'zhangsan');
profile.set('roleName', '组员');
profile.set('data', {
  avatar: 'https://...',
  skills: ['建模', '渲染']
});
await profile.save();
// 查询企业的所有设计师
const designerQuery = new Parse.Query('Profile');
designerQuery.equalTo('company', company.toPointer());
designerQuery.equalTo('roleName', '组员');
designerQuery.equalTo('isDeleted', false);
designerQuery.include('department');
const designers = await designerQuery.find();
// 根据企微UserID查询员工
const userQuery = new Parse.Query('Profile');
userQuery.equalTo('userId', 'zhangsan');
userQuery.equalTo('company', company.toPointer());
const user = await userQuery.first();
用途: 统一管理所有客户信息,支持企微外部联系人同步。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| 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 | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
source枚举值:
朋友圈: 微信朋友圈广告信息流: 抖音/小红书等信息流广告转介绍: 老客户转介绍其他: 其他来源索引配置:
indexes: [
  { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
  { name: 'external_userid_company', fields: { external_userid: 1, company: 1 }, unique: true },
  { name: 'mobile_company', fields: { mobile: 1, company: 1 } },
  { name: 'source_company', fields: { source: 1, company: 1 } }
]
data字段结构示例:
{
  "avatar": "https://...",
  "wechat": "lisi_wechat",
  "gender": "female",
  "age": 28,
  "tags": {
    "needType": "硬装",
    "preference": "现代",
    "budget": { "min": 50000, "max": 100000 },
    "colorAtmosphere": "暖色调"
  },
  "wxworkInfo": {
    "external_userid": "wmxxx",
    "name": "李四",
    "avatar": "https://...",
    "type": 1,
    "gender": 2,
    "unionid": "xxx"
  },
  "followUpStatus": "confirm",
  "demandType": "value-sensitive",
  "preferenceTags": ["现代简约", "温馨舒适", "储物充足"]
}
使用示例:
// 创建客户信息
const ContactInfo = Parse.Object.extend('ContactInfo');
const contact = new ContactInfo();
contact.set('name', '李四');
contact.set('mobile', '13900139000');
contact.set('company', company.toPointer());
contact.set('external_userid', 'wmxxx');
contact.set('source', '朋友圈');
contact.set('data', {
  wechat: 'lisi_wechat',
  tags: {
    needType: '硬装',
    preference: '现代',
    budget: { min: 50000, max: 100000 }
  }
});
await contact.save();
// 根据企微external_userid查询客户
const externalQuery = new Parse.Query('ContactInfo');
externalQuery.equalTo('external_userid', 'wmxxx');
externalQuery.equalTo('company', company.toPointer());
const customer = await externalQuery.first();
// 查询某来源渠道的所有客户
const sourceQuery = new Parse.Query('ContactInfo');
sourceQuery.equalTo('company', company.toPointer());
sourceQuery.equalTo('source', '朋友圈');
sourceQuery.equalTo('isDeleted', false);
const customers = await sourceQuery.find();
用途: 存储企业微信群聊信息,支持与项目的灵活关联。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "gc001" | 
| chat_id | String | 是 | - | 企微群聊ID | "wrxxxxxx" | 
| name | String | 是 | - | 群聊名称 | "李总-现代简约全案设计" | 
| company | Pointer | 是 | - | 所属企业 | → Company | 
| project | Pointer | 否 | - | 关联项目 | → Project | 
| member_list | Array | 否 | [] | 群成员列表 | [{ userid: "zhangsan", type: 1 }] | 
| joinUrl | String | 否 | - | 群聊加入链接 | "https://work.weixin.qq.com/..." | 
| data | Object | 否 | {} | 扩展数据 | { owner, create_time, ... } | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
索引配置:
indexes: [
  { name: 'chat_id_company', fields: { chat_id: 1, company: 1 }, unique: true },
  { name: 'project_isDeleted', fields: { project: 1, isDeleted: 1 } },
  { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } }
]
member_list结构示例:
[
  {
    "userid": "zhangsan",
    "type": 1,
    "join_time": 1701234567,
    "join_scene": 3,
    "invitor": { "userid": "lisi" }
  },
  {
    "userid": "wmxxx",
    "type": 2,
    "join_time": 1701234567,
    "join_scene": 3,
    "unionid": "xxx"
  }
]
data字段结构示例:
{
  "owner": "zhangsan",
  "create_time": 1701234567,
  "notice": "本群用于项目沟通",
  "admin_list": [{ "userid": "zhangsan" }]
}
使用示例:
// 创建群聊记录
const GroupChat = Parse.Object.extend('GroupChat');
const groupChat = new GroupChat();
groupChat.set('chat_id', 'wrxxxxxx');
groupChat.set('name', '李总-现代简约全案设计');
groupChat.set('company', company.toPointer());
groupChat.set('project', project.toPointer());
groupChat.set('member_list', [
  { userid: 'zhangsan', type: 1, join_time: Date.now() },
  { userid: 'wmxxx', type: 2, join_time: Date.now() }
]);
await groupChat.save();
// 根据chat_id查询群聊
const chatQuery = new Parse.Query('GroupChat');
chatQuery.equalTo('chat_id', 'wrxxxxxx');
chatQuery.equalTo('company', company.toPointer());
chatQuery.include('project');
const chat = await chatQuery.first();
// 查询项目关联的所有群聊
const projectChatQuery = new Parse.Query('GroupChat');
projectChatQuery.equalTo('project', project.toPointer());
projectChatQuery.equalTo('isDeleted', false);
const projectChats = await projectChatQuery.find();
用途: 管理项目与群聊的多对多关联关系,支持一个项目关联多个群聊。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "pg001" | 
| project | Pointer | 是 | - | 关联项目 | → Project | 
| groupChat | Pointer | 是 | - | 关联群聊 | → GroupChat | 
| isPrimary | Boolean | 否 | false | 是否主群 | true | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
索引配置:
indexes: [
  { name: 'project_groupChat', fields: { project: 1, groupChat: 1 }, unique: true },
  { name: 'groupChat_index', fields: { groupChat: 1 } },
  { name: 'isPrimary_index', fields: { isPrimary: 1 } }
]
使用示例:
// 创建项目群组关联
const ProjectGroup = Parse.Object.extend('ProjectGroup');
const pg = new ProjectGroup();
pg.set('project', project.toPointer());
pg.set('groupChat', groupChat.toPointer());
pg.set('isPrimary', true); // 标记为主群
await pg.save();
// 查询项目的所有群聊
const pgQuery = new Parse.Query('ProjectGroup');
pgQuery.equalTo('project', project.toPointer());
pgQuery.include('groupChat');
const groups = await pgQuery.find();
// 查询项目的主群
const primaryGroupQuery = new Parse.Query('ProjectGroup');
primaryGroupQuery.equalTo('project', project.toPointer());
primaryGroupQuery.equalTo('isPrimary', true);
primaryGroupQuery.include('groupChat');
const primaryGroup = await primaryGroupQuery.first();
用途: 项目管理的核心表,记录设计项目的全生命周期信息。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| 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 | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
status枚举值:
待分配: 项目已创建,等待分配设计师进行中: 项目正在进行已完成: 项目已完成已暂停: 项目已暂停已延期: 项目已延期已取消: 项目已取消currentStage枚举值:
订单分配: 项目创建,分配设计师阶段方案深化: 需求沟通与方案确认阶段交付执行: 设计执行与交付阶段售后归档: 尾款结算与售后服务阶段索引配置:
indexes: [
  { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } },
  { name: 'assignee_status', fields: { assignee: 1, status: 1 } },
  { name: 'customer_isDeleted', fields: { customer: 1, isDeleted: 1 } },
  { name: 'currentStage_status', fields: { currentStage: 1, status: 1 } },
  { name: 'deadline_index', fields: { deadline: 1 } },
  { name: 'updatedAt_desc', fields: { updatedAt: -1 } }
]
data字段结构示例:
{
  "stageHistory": [
    {
      "stage": "订单分配",
      "startTime": "2024-01-01T00:00:00.000Z",
      "endTime": "2024-01-02T00:00:00.000Z",
      "status": "completed"
    },
    {
      "stage": "方案深化",
      "startTime": "2024-01-02T00:00:00.000Z",
      "status": "in_progress"
    }
  ],
  "totalBudget": 120000,
  "estimatedDuration": 30,
  "priority": "high",
  "tags": ["全案设计", "现代简约"],
  "notes": "客户要求尽快完成"
}
使用示例:
// 创建项目
const Project = Parse.Object.extend('Project');
const project = new Project();
project.set('title', '李总现代简约全案');
project.set('company', company.toPointer());
project.set('customer', contact.toPointer());
project.set('assignee', designer.toPointer());
project.set('status', '进行中');
project.set('currentStage', '方案深化');
project.set('deadline', new Date('2024-12-31'));
project.set('data', {
  totalBudget: 120000,
  priority: 'high',
  tags: ['全案设计', '现代简约']
});
await project.save();
// 查询设计师负责的项目
const designerProjectQuery = new Parse.Query('Project');
designerProjectQuery.equalTo('assignee', designerId);
designerProjectQuery.equalTo('status', '进行中');
designerProjectQuery.equalTo('isDeleted', false);
designerProjectQuery.include('customer');
designerProjectQuery.descending('updatedAt');
const projects = await designerProjectQuery.find();
// 更新项目阶段
project.set('currentStage', '交付执行');
const stageHistory = project.get('data').stageHistory || [];
stageHistory.push({
  stage: '交付执行',
  startTime: new Date(),
  status: 'in_progress'
});
project.set('data', { ...project.get('data'), stageHistory });
await project.save();
用途: 存储项目的详细需求信息,包括空间信息和设计需求。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "req001" | 
| project | Pointer | 是 | - | 关联项目 | → Project | 
| company | Pointer | 是 | - | 所属企业 | → Company | 
| spaces | Array | 否 | [] | 空间列表 | [{ name: "客厅", area: 25 }] | 
| designRequirements | Object | 否 | {} | 设计需求 | { style: [...], color: "..." } | 
| materialAnalysis | Object | 否 | {} | 材料分析 | { preferred: [...], budget: {...} } | 
| data | Object | 否 | {} | 扩展数据 | { functionalNeeds, ... } | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
索引配置:
indexes: [
  { name: 'project_unique', fields: { project: 1 }, unique: true },
  { name: 'company_isDeleted', fields: { company: 1, isDeleted: 1 } }
]
spaces数组结构示例:
[
  {
    "spaceName": "客厅",
    "area": 25,
    "dimensions": {
      "length": 5.0,
      "width": 5.0,
      "height": 2.8
    },
    "features": ["朝南", "采光好"],
    "requirements": ["需要电视墙", "储物空间充足"]
  },
  {
    "spaceName": "主卧",
    "area": 18.5,
    "dimensions": {
      "length": 4.5,
      "width": 4.1,
      "height": 2.8
    },
    "features": ["朝南", "飘窗"],
    "requirements": ["独立卫浴", "衣柜空间"]
  }
]
designRequirements对象结构示例:
{
  "style": ["现代简约", "北欧风"],
  "colorPreference": "暖色调,以米白色为主",
  "materialPreference": ["实木", "环保材料", "石材"],
  "specialRequirements": [
    "需要大储物空间",
    "家有小孩,注意安全",
    "预留智能家居接口"
  ]
}
使用示例:
// 创建项目需求
const ProjectRequirement = Parse.Object.extend('ProjectRequirement');
const requirement = new ProjectRequirement();
requirement.set('project', project.toPointer());
requirement.set('company', company.toPointer());
requirement.set('spaces', [
  {
    spaceName: '客厅',
    area: 25,
    dimensions: { length: 5.0, width: 5.0, height: 2.8 },
    features: ['朝南', '采光好']
  }
]);
requirement.set('designRequirements', {
  style: ['现代简约'],
  colorPreference: '暖色调',
  materialPreference: ['实木', '环保材料']
});
await requirement.save();
// 查询项目需求
const reqQuery = new Parse.Query('ProjectRequirement');
reqQuery.equalTo('project', project.toPointer());
const projectReq = await reqQuery.first();
用途: 管理项目团队成员及其角色。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "team001" | 
| project | Pointer | 是 | - | 关联项目 | → Project | 
| profile | Pointer | 是 | - | 团队成员 | → Profile | 
| role | String | 是 | - | 成员角色 | "设计师" / "客服" | 
| workload | Number | 否 | 0 | 工作量占比 | 50 | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
role枚举值:
设计师: 负责设计工作客服: 负责沟通跟进组长: 负责审核把关协作设计师: 协助设计工作索引配置:
indexes: [
  { name: 'project_isDeleted', fields: { project: 1, isDeleted: 1 } },
  { name: 'profile_project', fields: { profile: 1, project: 1 }, unique: true }
]
使用示例:
// 添加团队成员
const ProjectTeam = Parse.Object.extend('ProjectTeam');
const teamMember = new ProjectTeam();
teamMember.set('project', project.toPointer());
teamMember.set('profile', designer.toPointer());
teamMember.set('role', '设计师');
teamMember.set('workload', 100);
await teamMember.save();
// 查询项目团队
const teamQuery = new Parse.Query('ProjectTeam');
teamQuery.equalTo('project', project.toPointer());
teamQuery.equalTo('isDeleted', false);
teamQuery.include('profile');
const team = await teamQuery.find();
// 查询设计师参与的所有项目
const designerTeamQuery = new Parse.Query('ProjectTeam');
designerTeamQuery.equalTo('profile', designerId);
designerTeamQuery.include('project');
const designerProjects = await designerTeamQuery.find();
用途: 核心创新表 - 统一管理空间设计产品,每个Product代表一个空间的设计产品(如"李总主卧设计"),包含空间信息、报价、需求、评价等全生命周期数据。
设计理念:
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "prod001" | 
| project | Pointer | 是 | - | 所属项目 | → Project | 
| company | Pointer | 是 | - | 所属企业 | → Company | 
| profile | Pointer | 是 | - | 负责设计师 | → Profile | 
| stage | String | 是 | "not_started" | 设计阶段 | "modeling" / "rendering" | 
| processType | String | 否 | - | 工序类型 | "modeling" / "rendering" | 
| productName | String | 是 | - | 产品名称 | "李总主卧设计" | 
| productType | String | 是 | - | 空间类型 | "bedroom" | 
| status | String | 是 | "not_started" | 产品状态 | "in_progress" | 
| fileUrl | String | 否 | - | 主要效果图URL | "https://..." | 
| reviewStatus | String | 是 | "pending" | 审核状态 | "approved" | 
| space | Object | 否 | {} | 空间信息 | {name, area, dimensions} | 
| quotation | Object | 否 | {} | 产品报价 | {price, breakdown} | 
| requirements | Object | 否 | {} | 设计需求 | {color, material} | 
| reviews | Array | 否 | [] | 产品评价 | [{rating, comments}] | 
| estimatedBudget | Number | 否 | - | 预估预算 | 35000 | 
| estimatedDuration | Number | 否 | - | 预估工期(天) | 7 | 
| order | Number | 否 | 0 | 排序顺序 | 1 | 
| data | Object | 否 | {} | 扩展数据 | { version, progress, ... } | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
productType枚举值:
living_room: 客厅bedroom: 卧室master_bedroom: 主卧kitchen: 厨房bathroom: 卫生间dining_room: 餐厅study: 书房balcony: 阳台corridor: 走廊storage: 储物间entrance: 玄关other: 其他status枚举值:
not_started: 未开始in_progress: 进行中awaiting_review: 待审核completed: 已完成blocked: 已阻塞delayed: 已延期stage枚举值:
modeling: 建模阶段softDecor: 软装阶段rendering: 渲染阶段postProcess: 后期阶段reviewStatus枚举值:
pending: 待审核approved: 已通过rejected: 已驳回revision_required: 需要修改索引配置:
indexes: [
  { name: 'project_company_isDeleted', fields: { project: 1, company: 1, isDeleted: 1 } },
  { name: 'profile_company', fields: { profile: 1, company: 1 } },
  { name: 'productType_order', fields: { productType: 1, order: 1 } },
  { name: 'stage_status', fields: { stage: 1, status: 1 } },
  { name: 'reviewStatus', fields: { reviewStatus: 1 } }
]
space字段结构示例:
{
  "spaceName": "主卧",
  "area": 18.5,
  "dimensions": {
    "length": 4.5,
    "width": 4.1,
    "height": 2.8
  },
  "features": ["朝南", "飘窗", "独立卫浴"],
  "constraints": ["承重墙不可动"],
  "priority": "high",
  "complexity": "medium"
}
quotation字段结构示例:
{
  "price": 35000,
  "currency": "CNY",
  "breakdown": {
    "design": 15000,
    "modeling": 10000,
    "rendering": 8000,
    "softDecor": 2000
  },
  "unitPrice": 1891,
  "estimatedDays": 7,
  "status": "approved",
  "approvedBy": {
    "__type": "Pointer",
    "className": "Profile",
    "objectId": "prof001"
  },
  "approvedAt": "2024-01-05T10:00:00.000Z",
  "validUntil": "2024-12-31T00:00:00.000Z",
  "notes": "包含全套效果图和施工图"
}
requirements字段结构示例:
{
  "colorRequirement": {
    "primaryHue": 180,
    "saturation": 45,
    "temperature": "暖色调",
    "colorDistribution": [
      {
        "hex": "#F5F5DC",
        "percentage": 40,
        "name": "米白色"
      },
      {
        "hex": "#8B4513",
        "percentage": 30,
        "name": "原木色"
      }
    ]
  },
  "materialRequirement": {
    "preferred": ["实木", "环保材料", "石材"],
    "avoid": ["塑料", "合成材料"],
    "budget": {
      "min": 20000,
      "max": 40000
    }
  },
  "lightingRequirement": {
    "naturalLight": "充足",
    "lightColor": "暖白",
    "specialRequirements": ["床头阅读灯", "氛围灯"]
  },
  "specificRequirements": [
    "需要大储物空间",
    "独立卫浴",
    "飘窗设计"
  ],
  "referenceImages": ["https://...", "https://..."]
}
reviews字段结构示例:
[
  {
    "reviewId": "review001",
    "satisfactionScore": 4.5,
    "spaceSpecificRatings": {
      "design": 5,
      "functionality": 4,
      "material": 4,
      "lighting": 5
    },
    "usageFeedback": {
      "positive": ["储物空间充足", "光线舒适"],
      "improvements": ["可以增加插座数量"]
    },
    "comments": "整体设计很满意,储物功能强大",
    "afterPhotos": ["https://...", "https://..."],
    "submittedAt": "2024-11-15T10:00:00.000Z",
    "submittedBy": {
      "__type": "Pointer",
      "className": "ContactInfo",
      "objectId": "contact001"
    }
  }
]
使用示例:
// 创建空间设计产品
const Product = Parse.Object.extend('Product');
const product = new Product();
product.set('project', project.toPointer());
product.set('company', company.toPointer());
product.set('profile', designer.toPointer());
product.set('productName', '李总主卧设计');
product.set('productType', 'master_bedroom');
product.set('stage', 'modeling');
product.set('status', 'in_progress');
// 设置空间信息
product.set('space', {
  spaceName: '主卧',
  area: 18.5,
  dimensions: { length: 4.5, width: 4.1, height: 2.8 },
  features: ['朝南', '飘窗', '独立卫浴'],
  priority: 'high',
  complexity: 'medium'
});
// 设置产品报价
product.set('quotation', {
  price: 35000,
  currency: 'CNY',
  breakdown: {
    design: 15000,
    modeling: 10000,
    rendering: 8000,
    softDecor: 2000
  },
  status: 'pending',
  validUntil: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000)
});
// 设置设计需求
product.set('requirements', {
  colorRequirement: {
    temperature: '暖色调',
    colorDistribution: [
      { hex: '#F5F5DC', percentage: 40, name: '米白色' },
      { hex: '#8B4513', percentage: 30, name: '原木色' }
    ]
  },
  materialRequirement: {
    preferred: ['实木', '环保材料'],
    budget: { min: 20000, max: 40000 }
  },
  specificRequirements: ['需要大储物空间', '独立卫浴']
});
await product.save();
// 查询项目的所有空间设计产品
const productQuery = new Parse.Query('Product');
productQuery.equalTo('project', projectId);
productQuery.equalTo('isDeleted', false);
productQuery.include('profile');
productQuery.ascending('order');
const products = await productQuery.find();
// 查询设计师负责的空间产品
const designerProductQuery = new Parse.Query('Product');
designerProductQuery.equalTo('profile', designerId);
designerProductQuery.equalTo('status', 'in_progress');
designerProductQuery.include('project');
const designerProducts = await designerProductQuery.find();
// 更新产品状态和审核
product.set('status', 'awaiting_review');
product.set('reviewStatus', 'pending');
product.set('fileUrl', 'https://file.example.com/render.jpg');
await product.save();
// 添加客户评价
const reviews = product.get('reviews') || [];
reviews.push({
  reviewId: uuidv4(),
  satisfactionScore: 4.5,
  spaceSpecificRatings: {
    design: 5,
    functionality: 4,
    material: 4,
    lighting: 5
  },
  comments: '整体设计很满意',
  submittedAt: new Date(),
  submittedBy: customer.toPointer()
});
product.set('reviews', reviews);
await product.save();
用途: 存储项目相关的所有文件,通过category字段区分文件类型,支持AI分析结果存储。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "file001" | 
| project | Pointer | 是 | - | 所属项目 | → Project | 
| product | Pointer | 否 | - | 关联空间产品 | → Product | 
| attach | Pointer | 是 | - | 附件文件 | → Attachment | 
| uploadedBy | Pointer | 是 | - | 上传人 | → Profile | 
| stage | String | 否 | - | 关联阶段 | "requirements" | 
| category | String | 否 | "other" | 文件分类 | "panorama" / "delivery" | 
| data | Object | 否 | {} | 扩展数据 | { thumbnailUrl, ... } | 
| analysis | Object | 否 | {} | AI分析结果 | { ai: {...}, color: {...} } | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
category枚举值:
quotation: 财务凭据(报价单等)panorama: 全景素材(720全景图)delivery: 交付文件(效果图、施工图)reference: 参考文件(客户提供的参考图)requirement: 需求文件(需求说明文档)other: 其他文件stage枚举值:
order: 订单分配阶段requirements: 方案深化阶段delivery: 交付执行阶段aftercare: 售后归档阶段索引配置:
indexes: [
  { name: 'project_product_stage_isDeleted', fields: { project: 1, product: 1, stage: 1, isDeleted: 1 } },
  { name: 'attach', fields: { attach: 1 } },
  { name: 'uploadedBy_project', fields: { uploadedBy: 1, project: 1 } },
  { name: 'category_index', fields: { category: 1 } }
]
analysis.ai字段结构示例 (AI模型分析结果):
{
  "styleElements": ["现代简约", "线条流畅", "留白设计"],
  "colorPalette": ["#FFFFFF", "#F5F5F5", "#3880FF"],
  "materialAnalysis": ["实木地板", "布艺沙发", "金属装饰"],
  "layoutFeatures": ["开放式布局", "功能分区明确", "采光良好"],
  "mood": "简洁明亮,温馨舒适",
  "confidence": 0.92,
  "analyzedAt": "2024-10-21T12:00:00.000Z",
  "version": "1.0",
  "source": "image_analysis"
}
analysis.color字段结构示例 (色彩分析插件结果):
{
  "version": "1.0",
  "source": "color-get",
  "pixelSize": 100,
  "createdAt": "2024-10-20T12:00:00.000Z",
  "palette": [
    {
      "rgb": { "r": 240, "g": 200, "b": 160 },
      "hex": "#F0C8A0",
      "percentage": 28.5
    },
    {
      "rgb": { "r": 139, "g": 69, "b": 19 },
      "hex": "#8B4513",
      "percentage": 22.3
    }
  ],
  "mosaicUrl": "data:image/png;base64,...",
  "metrics": {
    "warmCoolBalance": 12.0,
    "averageBrightness": 56.0,
    "averageSaturation": 42.5,
    "diversity": 18
  },
  "histogram": {
    "brightnessBins": [0, 1, 3, 5, 8, 12, 9, 6, 3, 1],
    "saturationBins": [0, 0, 2, 4, 7, 10, 9, 5, 2, 1]
  },
  "splitPoints": [
    {
      "temp": 14.2,
      "brightness": 62.1,
      "size": 3.5,
      "color": "#F0C8A0"
    }
  ]
}
使用示例:
// 上传项目文件
const ProjectFile = Parse.Object.extend('ProjectFile');
const projectFile = new ProjectFile();
projectFile.set('project', project.toPointer());
projectFile.set('product', product.toPointer());
projectFile.set('attach', attachment.toPointer());
projectFile.set('uploadedBy', profile.toPointer());
projectFile.set('stage', 'delivery');
projectFile.set('category', 'panorama');
projectFile.set('data', {
  thumbnailUrl: 'https://...',
  description: '主卧720全景图'
});
await projectFile.save();
// 保存AI分析结果
projectFile.set('analysis', {
  ai: {
    styleElements: ['现代简约', '线条流畅'],
    colorPalette: ['#FFFFFF', '#F5F5F5'],
    confidence: 0.92
  }
});
await projectFile.save();
// 保存色彩分析结果
projectFile.set('analysis', {
  ...projectFile.get('analysis'),
  color: {
    version: '1.0',
    source: 'color-get',
    palette: [
      { hex: '#F0C8A0', percentage: 28.5 }
    ],
    metrics: {
      warmCoolBalance: 12.0,
      averageBrightness: 56.0
    }
  }
});
await projectFile.save();
// 查询项目的全景图文件
const panoramaQuery = new Parse.Query('ProjectFile');
panoramaQuery.equalTo('project', projectId);
panoramaQuery.equalTo('category', 'panorama');
panoramaQuery.equalTo('isDeleted', false);
panoramaQuery.include('attach', 'uploadedBy', 'product');
const panoramaFiles = await panoramaQuery.find();
// 查询已进行色彩分析的文件
const analyzedQuery = new Parse.Query('ProjectFile');
analyzedQuery.exists('analysis.color');
analyzedQuery.equalTo('project', projectId);
const analyzedFiles = await analyzedQuery.find();
用途: 存储文件的元数据信息,由fmode-ng NovaStorage服务管理,与ProjectFile表关联使用。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID(NovaFile.id) | "attach001" | 
| size | Number | 是 | - | 文件大小(字节) | 1024000 | 
| url | String | 是 | - | 文件访问URL | "https://file.fmode.cn/..." | 
| name | String | 是 | - | 文件名 | "主卧效果图.jpg" | 
| mime | String | 是 | - | MIME类型 | "image/jpeg" | 
| md5 | String | 否 | - | 文件MD5哈希值 | " 5d41402abc..." | 
| metadata | Object | 否 | - | 文件元数据 | { width, height, duration } | 
| company | Pointer | 否 | - | 所属企业 | → Company | 
| user | Pointer | 否 | - | 上传用户 | → _User | 
| createdAt | Date | 自动 | 当前时间 | 创建时间 | 2024-01-01T00:00:00.000Z | 
| updatedAt | Date | 自动 | 当前时间 | 更新时间 | 2024-01-01T00:00:00.000Z | 
metadata字段结构示例:
{
  "width": 1920,
  "height": 1080,
  "duration": 120,
  "lastModified": 1701234567000,
  "projectId": "proj001",
  "fileType": "image",
  "spaceId": "space001",
  "stage": "delivery"
}
与NovaStorage集成:
NovaStorage是fmode-ng提供的文件存储服务,上传流程如下:
// 1. 通过NovaStorage上传文件
import { NovaStorage, NovaFile } from 'fmode-ng/core';
const cid = localStorage.getItem('company');
const storage = await NovaStorage.withCid(cid);
const uploadedFile: NovaFile = await storage.upload(file, {
  prefixKey: `project/${projectId}`,
  onProgress: (progress) => {
    console.log('上传进度:', progress.total.percent);
  }
});
// 2. NovaFile自动保存到Attachment表
// uploadedFile.id 即为 Attachment.objectId
// 3. 创建ProjectFile记录关联Attachment
const ProjectFile = Parse.Object.extend('ProjectFile');
const projectFile = new ProjectFile();
projectFile.set('project', project.toPointer());
// 使用NovaFile.id创建Attachment指针
const Attachment = Parse.Object.extend('Attachment');
const attachment = new Attachment();
attachment.id = uploadedFile.id; // NovaFile.id
projectFile.set('attach', attachment.toPointer());
projectFile.set('uploadedBy', profile.toPointer());
projectFile.set('category', 'delivery');
await projectFile.save();
使用示例:
// 查询Attachment信息
const Attachment = Parse.Object.extend('Attachment');
const attachQuery = new Parse.Query('Attachment');
const attachment = await attachQuery.get('attach001');
console.log('文件名:', attachment.get('name'));
console.log('文件大小:', attachment.get('size'));
console.log('文件URL:', attachment.get('url'));
console.log('文件MD5:', attachment.get('md5'));
// 通过ProjectFile查询关联的Attachment
const fileQuery = new Parse.Query('ProjectFile');
fileQuery.include('attach');
const projectFile = await fileQuery.get('file001');
const relatedAttach = projectFile.get('attach');
console.log('附件URL:', relatedAttach.get('url'));
用途: 统一的项目付款管理表,整合了原有的ProjectSettlement和ProjectVoucher功能,每条记录代表一次具体的付款行为。
字段说明:
| 字段名 | 类型 | 必填 | 默认值 | 说明 | 示例值 | 
|---|---|---|---|---|---|
| objectId | String | 是 | 自动生成 | 主键ID | "payment001" | 
| project | Pointer | 是 | - | 所属项目 | → Project | 
| company | Pointer | 是 | - | 所属企业 | → Company | 
| type | String | 是 | - | 付款类型 | "advance" / "milestone" / "final" | 
| stage | String | 是 | - | 付款阶段 | "order" / "delivery" | 
| method | String | 是 | - | 支付方式 | "bank_transfer" / "wechat" | 
| amount | Number | 是 | - | 付款金额 | 35000 | 
| currency | String | 是 | "CNY" | 货币类型 | "CNY" | 
| percentage | Number | 否 | 0 | 占总价百分比 | 30 | 
| paymentDate | Date | 否 | - | 实际付款时间 | 2024-12-01T10:00:00.000Z | 
| dueDate | Date | 是 | - | 应付款时间 | 2024-12-01T00:00:00.000Z | 
| recordedDate | Date | 否 | - | 记录时间 | 2024-11-30T15:30:00.000Z | 
| status | String | 是 | "pending" | 付款状态 | "paid" / "overdue" | 
| voucherFile | Pointer | 否 | - | 付款凭证文件 | → ProjectFile | 
| voucherUrl | String | 否 | - | 凭证URL | "https://..." | 
| transactionId | String | 否 | - | 第三方交易ID | "wx_123456789" | 
| paymentReference | String | 否 | - | 付款参考号/发票号 | "INV-2024-001" | 
| paidBy | Pointer | 是 | - | 付款人 | → ContactInfo | 
| recordedBy | Pointer | 是 | - | 记录人 | → Profile | 
| verifiedBy | Pointer | 否 | - | 验证人 | → Profile | 
| description | String | 否 | - | 付款描述 | "首期款" | 
| notes | String | 否 | - | 备注信息 | "客户通过银行转账付款" | 
| relatedStage | String | 否 | - | 关联执行阶段 | "modeling" | 
| product | Pointer | 否 | - | 关联产品 | → Product | 
| autoReminderSent | Boolean | 否 | false | 是否已发送提醒 | false | 
| reminderCount | Number | 否 | 0 | 提醒次数 | 0 | 
| data | Object | 否 | {} | 扩展数据 | {bankInfo, approvalFlow} | 
| isDeleted | Boolean | 否 | false | 软删除标记 | false | 
枚举值: 参考rules/schemas.md中的详细定义(type: advance/milestone/final/refund, method: cash/bank_transfer/alipay/wechat等)
用途: 记录客户在各阶段的反馈和评价。
关键字段: objectId, project, customer, product, stage, feedbackType, content, rating, status, data
使用示例:
const ProjectFeedback = Parse.Object.extend('ProjectFeedback');
const feedback = new ProjectFeedback();
feedback.set('project', project.toPointer());
feedback.set('customer', customer.toPointer());
feedback.set('product', product.toPointer());
feedback.set('stage', '渲染');
feedback.set('feedbackType', 'suggestion');
feedback.set('content', '客厅颜色希望再暖一些');
feedback.set('rating', 4);
feedback.set('status', '待处理');
await feedback.save();
用途: 记录产品的质量检查结果。
关键字段: objectId, project, checkType, checkedBy, checkedAt, isPassed, items, data
使用示例:
const ProductCheck = Parse.Object.extend('ProductCheck');
const check = new ProductCheck();
check.set('project', project.toPointer());
check.set('checkType', 'modeling');
check.set('checkedBy', leader.toPointer());
check.set('checkedAt', new Date());
check.set('isPassed', true);
check.set('items', [
  { name: '建模精度', passed: true },
  { name: '材质贴图', passed: true }
]);
await check.save();
用途: 记录项目中的问题、投诉、改图需求等。
字段说明:
| 字段名 | 类型 | 必填 | 说明 | 示例值 | 
|---|---|---|---|---|
| objectId | String | 是 | 主键ID | "issue001" | 
| project | Pointer | 是 | 所属项目 | → Project | 
| product | Pointer | 否 | 关联产品 | → Product | 
| creator | Pointer | 是 | 创建人 | → Profile | 
| assignee | Pointer | 否 | 责任人 | → Profile | 
| title | String | 是 | 问题标题 | "客厅灯光需要调整" | 
| description | String | 是 | 问题描述 | "客户反馈灯光太暗..." | 
| relatedSpace | String | 否 | 相关空间 | "客厅" | 
| relatedStage | String | 否 | 相关阶段 | "渲染" | 
| relatedContentType | String | 否 | 相关内容类型 | "白模/软装/渲染/后期" | 
| relatedFiles | Array | 否 | 相关项目文件 | [fileId1, fileId2] | 
| priority | String | 是 | 优先程度 | "low/medium/high/urgent" | 
| issueType | String | 是 | 问题类型 | "bug/task/feedback/risk" | 
| dueDate | Date | 否 | 截止时间 | 2024-12-31T00:00:00.000Z | 
| status | String | 是 | 状态 | "待处理/处理中/已解决/已关闭" | 
| resolution | String | 否 | 解决方案 | "已调整灯光亮度" | 
| lastReminderAt | Date | 否 | 最后催单时间 | 2024-11-15T10:00:00.000Z | 
| reminderCount | Number | 否 | 催单次数 | 2 | 
| data | Object | 否 | 扩展数据 | { comments: [...] } | 
使用示例:
// 创建问题
const ProjectIssue = Parse.Object.extend('ProjectIssue');
const issue = new ProjectIssue();
issue.set('project', project.toPointer());
issue.set('product', product.toPointer());
issue.set('creator', profile.toPointer());
issue.set('assignee', designer.toPointer());
issue.set('title', '客厅灯光需要调整');
issue.set('description', '客户反馈客厅灯光太暗,需要增加亮度');
issue.set('relatedSpace', '客厅');
issue.set('relatedStage', '渲染');
issue.set('priority', 'high');
issue.set('issueType', 'feedback');
issue.set('status', '待处理');
issue.set('data', {
  comments: [
    { author: '客服小王', content: '已转达给设计师', time: new Date() }
  ]
});
await issue.save();
// 查询待处理的问题
const issueQuery = new Parse.Query('ProjectIssue');
issueQuery.equalTo('project', projectId);
issueQuery.equalTo('status', '待处理');
issueQuery.include('assignee', 'creator');
const issues = await issueQuery.find();
用途: 记录项目沟通跟进历史。
关键字段: objectId, project, profile, contact, content, type, stage, attachments, data
使用示例:
const ContactFollow = Parse.Object.extend('ContactFollow');
const follow = new ContactFollow();
follow.set('project', project.toPointer());
follow.set('profile', profile.toPointer());
follow.set('contact', contact.toPointer());
follow.set('content', '与客户沟通确认了主卧设计方案');
follow.set('type', 'call');
follow.set('stage', '方案深化');
follow.set('attachments', ['fileId1', 'fileId2']);
await follow.save();
用途: Parse Server内置的用户认证表,用于系统登录和权限管理。
关键字段:
与Profile关系:
使用示例:
// 查询当前登录用户
const currentUser = Parse.User.current();
if (currentUser) {
  console.log('当前用户:', currentUser.get('username'));
  
  // 查询用户的Profile
  const profileQuery = new Parse.Query('Profile');
  profileQuery.equalTo('userId', currentUser.get('username'));
  const profile = await profileQuery.first();
}
| 值 | 中文 | 说明 | 
|---|---|---|
| 待分配 | - | 项目已创建,等待分配设计师 | 
| 进行中 | - | 项目正在进行 | 
| 已完成 | - | 项目已完成 | 
| 已暂停 | - | 项目已暂停 | 
| 已延期 | - | 项目已延期 | 
| 已取消 | - | 项目已取消 | 
| 值 | 说明 | 
|---|---|
| 订单分配 | 项目创建,分配设计师阶段 | 
| 方案深化 | 需求沟通与方案确认阶段 | 
| 交付执行 | 设计执行与交付阶段 | 
| 售后归档 | 尾款结算与售后服务阶段 | 
| 值 | 说明 | 
|---|---|
| 客服 | 客户服务人员,负责接单、跟进 | 
| 组员 | 设计师,负责具体设计工作 | 
| 组长 | 团队负责人,负责审核、分配 | 
| 财务 | 财务人员 | 
| 人事 | 人事人员 | 
| 管理员 | 系统管理员 | 
| 值 | 中文 | 
|---|---|
| living_room | 客厅 | 
| bedroom | 卧室 | 
| master_bedroom | 主卧 | 
| kitchen | 厨房 | 
| bathroom | 卫生间 | 
| dining_room | 餐厅 | 
| study | 书房 | 
| balcony | 阳台 | 
| corridor | 走廊 | 
| storage | 储物间 | 
| entrance | 玄关 | 
| other | 其他 | 
| Parse类型 | JavaScript类型 | 说明 | 示例 | 
|---|---|---|---|
| String | string | 字符串 | "项目名称" | 
| Number | number | 数字(整数或浮点数) | 100, 3.14 | 
| Boolean | boolean | 布尔值 | true, false | 
| Date | Date | 日期时间 | new Date() | 
| Array | Array | 数组 | ["tag1", "tag2"] | 
| Object | Object | JSON对象 | { key: "value" } | 
| Pointer | Parse.Object | 关联引用 | → OtherTable | 
| File | Parse.File | 文件 | Parse.File | 
系统为高频查询字段配置了索引,查询时尽量使用已索引的字段:
// ✅ 好 - 使用了索引字段
query.equalTo('company', companyId);
query.equalTo('isDeleted', false);
// ❌ 避免 - 未索引字段的复杂查询
query.matches('data.customField', /pattern/);
查询时一次性include需要的关联数据,避免N+1查询:
// ✅ 好
const query = new Parse.Query('Project');
query.include('customer', 'assignee', 'company');
const projects = await query.find();
// ❌ 避免
const projects = await projectQuery.find();
for (const project of projects) {
  const customer = await project.get('customer').fetch(); // N+1查询
}
对于大数据量,使用分页查询:
const query = new Parse.Query('Project');
query.limit(20);
query.skip(page * 20);
query.descending('createdAt');
const projects = await query.find();
完整的数据表结构定义和迁移脚本参考:
scripts/migration/create-schema.jsnode scripts/migration/create-schema.jsrules/schemas.mdrules/storage.mdrules/parse.mddocs/prd/wxwork-project-management.mddocs/Database/customer-designer-normalization.md本文档详细描述了YSS项目管理系统的完整数据库表结构,共包含18个核心业务表和1个系统认证表。
文档版本: v1.0
最后更新: 2024-10-21
维护团队: 后端开发团队