# 组长端数据库接入完整指南 **日期**: 2025-10-22 16:00 **任务**: 为组长端接入真实数据库,实现增删查改功能 **状态**: ✅ 已完成 --- ## 一、概述 ### 1.1 背景 根据项目规则文档(`.trae/rules/project_rules.md`)和数据库文档(`docs/Database/database-tables-overview.md`),为组长端实现完整的数据库接入,使组长端能够: - 📊 查看真实的项目数据 - 👥 管理设计师信息 - ✏️ 分配项目给设计师 - 📈 查看实时统计数据 - 🔄 执行增删查改操作 ### 1.2 技术栈 - **数据库**: Parse Server (MongoDB) - **数据服务**: `fmode-ng/parse` (FmodeParse) - **多租户**: 通过 `company` 字段隔离 - **软删除**: 使用 `isDeleted` 字段 --- ## 二、数据服务架构 ### 2.1 服务层设计 创建了3个核心数据服务: ``` src/app/pages/team-leader/services/ ├── project-data.service.ts # 项目数据服务(增删查改) ├── dashboard-data.service.ts # 仪表盘数据服务(统计) └── designer.service.ts # 设计师服务(已完善) ``` ### 2.2 服务职责 | 服务 | 职责 | 主要方法 | |------|------|----------| | **ProjectDataService** | 项目增删查改 | getProjects, createProject, updateProject, deleteProject, assignProject | | **DashboardDataService** | 统计数据 | getKPIStats, getStageDistribution, getDesignerWorkloadDistribution, getTodoTasks | | **DesignerService** | 设计师管理 | getDesigners, getDesignerWorkload, updateDesignerTags, getRecommendedDesigners | --- ## 三、ProjectDataService(项目数据服务) ### 3.1 初始化 ```typescript constructor() { this.cid = localStorage.getItem('company') || ''; console.log('🏢 ProjectDataService初始化,当前公司ID:', this.cid); this.initParse(); } private async initParse(): Promise { const { FmodeParse } = await import('fmode-ng/parse'); this.Parse = FmodeParse.with("nova"); } ``` **关键点**: - 从 `localStorage.getItem('company')` 获取公司ID - 使用 `FmodeParse.with("nova")` 初始化Parse连接 - 延迟加载,避免阻塞应用启动 ### 3.2 查询操作 #### 3.2.1 获取所有项目 ```typescript async getProjects(filters?: { status?: string; assignee?: string; currentStage?: string; searchTerm?: string; }): Promise ``` **功能**: - 查询当前公司的所有项目 - 支持按状态、设计师、阶段筛选 - 支持关键词搜索(项目名称、客户名称) - 自动关联客户和设计师信息 **示例**: ```typescript // 获取所有进行中的项目 const projects = await projectDataService.getProjects({ status: '进行中' }); // 获取某个设计师的项目 const designerProjects = await projectDataService.getProjects({ assignee: 'designerId123' }); // 搜索项目 const searchResults = await projectDataService.getProjects({ searchTerm: '现代简约' }); ``` #### 3.2.2 根据ID获取项目 ```typescript async getProjectById(projectId: string): Promise ``` **功能**: - 查询单个项目详情 - 包含客户、设计师、部门关联信息 #### 3.2.3 获取设计师的项目列表 ```typescript async getProjectsByDesigner(designerId: string): Promise ``` **功能**: - 查询设计师负责的所有项目 - 自动排除已删除项目 #### 3.2.4 获取超期项目 ```typescript async getOverdueProjects(): Promise ``` **功能**: - 查询所有超期项目 - 按超期时间倒序排列 #### 3.2.5 获取临期项目 ```typescript async getDueSoonProjects(): Promise ``` **功能**: - 查询7天内到期的项目 - 按截止时间正序排列 ### 3.3 创建操作 #### 3.3.1 创建项目 ```typescript async createProject(projectData: { title: string; customer: string; // ContactInfo ID assignee?: string; // Profile ID status?: string; currentStage?: string; deadline?: Date; data?: any; }): Promise ``` **功能**: - 创建新项目 - 自动设置公司ID - 初始化阶段历史记录 - 返回创建后的项目 **示例**: ```typescript const newProject = await projectDataService.createProject({ title: '李总现代简约全案', customer: 'contactId123', assignee: 'designerId456', status: '进行中', currentStage: '方案深化', deadline: new Date('2024-12-31'), data: { totalBudget: 120000, tags: ['全案设计', '现代简约'] } }); ``` ### 3.4 更新操作 #### 3.4.1 更新项目 ```typescript async updateProject(projectId: string, updates: { title?: string; status?: string; currentStage?: string; assignee?: string; deadline?: Date; data?: any; }): Promise ``` **功能**: - 更新项目字段 - 更新阶段时自动记录阶段历史 - 支持部分更新 **示例**: ```typescript // 更新项目状态 await projectDataService.updateProject('projectId123', { status: '已完成', currentStage: '售后归档' }); // 延长截止时间 await projectDataService.updateProject('projectId123', { deadline: new Date('2025-01-15') }); ``` #### 3.4.2 分配项目 ```typescript async assignProject(projectId: string, designerId: string): Promise ``` **功能**: - 分配项目给设计师 - 自动更新状态为"进行中" - 自动更新阶段为"方案深化" **示例**: ```typescript await projectDataService.assignProject('projectId123', 'designerId456'); ``` #### 3.4.3 更新项目阶段 ```typescript async updateProjectStage(projectId: string, stage: string): Promise ``` **功能**: - 单独更新项目阶段 - 自动记录阶段历史 ### 3.5 删除操作 #### 3.5.1 软删除项目 ```typescript async deleteProject(projectId: string): Promise ``` **功能**: - 软删除项目(设置 `isDeleted = true`) - 项目仍保留在数据库中 - 查询时会自动过滤 **示例**: ```typescript await projectDataService.deleteProject('projectId123'); ``` #### 3.5.2 物理删除项目(谨慎使用) ```typescript async destroyProject(projectId: string): Promise ``` **功能**: - 物理删除项目 - 永久删除,无法恢复 - **仅在必要时使用** ### 3.6 统计查询 #### 3.6.1 获取项目统计 ```typescript async getProjectStats(): Promise<{ total: number; inProgress: number; completed: number; overdue: number; dueSoon: number; unassigned: number; }> ``` **功能**: - 统计项目数量 - 包含总数、进行中、已完成、超期、临期、未分配 --- ## 四、DashboardDataService(仪表盘数据服务) ### 4.1 获取KPI统计 ```typescript async getKPIStats(): Promise<{ totalProjects: number; inProgressProjects: number; completedProjects: number; overdueProjects: number; dueSoonProjects: number; totalDesigners: number; availableDesigners: number; busyDesigners: number; overloadedDesigners: number; }> ``` **功能**: - 项目统计:总数、进行中、已完成、超期、临期 - 设计师统计:总数、空闲、忙碌、超负荷 **使用示例**: ```typescript const kpi = await dashboardDataService.getKPIStats(); console.log(`总项目数: ${kpi.totalProjects}`); console.log(`超期项目: ${kpi.overdueProjects}`); console.log(`空闲设计师: ${kpi.availableDesigners}`); ``` ### 4.2 获取项目阶段分布 ```typescript async getStageDistribution(): Promise> ``` **功能**: - 统计各阶段的项目数量 - 返回 `{ '订单分配': 5, '方案深化': 10, ... }` ### 4.3 获取设计师负载分布 ```typescript async getDesignerWorkloadDistribution(): Promise<{ idle: number; busy: number; overload: number; }> ``` **功能**: - 统计设计师负载情况 - 空闲(0个项目)、忙碌(1-2个项目)、超负荷(≥3个项目) ### 4.4 获取待办任务 ```typescript async getTodoTasks(): Promise ``` **功能**: - 自动生成待办任务列表 - 包含:待分配项目、超期项目、临期项目 - 按优先级排序 **返回数据结构**: ```typescript { id: string; // 任务ID title: string; // 任务标题 description: string; // 任务描述 deadline: Date; // 截止时间 priority: 'high' | 'medium' | 'low'; // 优先级 type: 'assign' | 'review' | 'performance'; // 任务类型 targetId: string; // 关联项目ID } ``` --- ## 五、DesignerService(设计师服务) ### 5.1 获取设计师列表 ```typescript async getDesigners(): Promise ``` **功能**: - 查询所有设计师(roleName = '组员') - 包含部门信息 - 包含设计师tags(技能、容量、历史等) **返回数据结构**: ```typescript { id: string; name: string; mobile: string; department: string; departmentId: string; tags: DesignerTags; // 设计师画像 data: any; profile: ParseObject; } ``` ### 5.2 更新设计师Tags ```typescript async updateDesignerTags(designerId: string, tags: Partial): Promise ``` **功能**: - 更新设计师的技能、容量、紧急单偏好等 - 支持部分更新(深度合并) **示例**: ```typescript await designerService.updateDesignerTags('designerId123', { expertise: { styles: ['现代简约', '北欧风'], skills: ['建模', '渲染', '后期'], spaceTypes: ['客厅', '卧室'] }, capacity: { weeklyProjects: 3, maxConcurrent: 5, avgDaysPerProject: 7 } }); ``` ### 5.3 获取设计师负载 ```typescript async getDesignerWorkload(designerId: string): Promise<{ projects: any[]; weightedTotal: number; overdueCount: number; loadRate: number; }> ``` **功能**: - 查询设计师当前负责的项目 - 计算加权负载总量 - 计算超期项目数 - 计算负载率 **加权算法**: ``` 项目权重 = 类型系数 × 时间系数 × 紧急度系数 类型系数: 硬装=2.0, 软装=1.0 时间系数: 超期=1.5, 临期(≤3天)=1.3, 正常(≤7天)=1.0, 充裕=0.8 紧急度系数: 高=1.2, 中=1.0, 低=0.8 ``` ### 5.4 智能推荐设计师 ```typescript async getRecommendedDesigners(project: any): Promise ``` **功能**: - 根据项目需求推荐最合适的设计师 - 综合考虑:风格匹配、负载情况、历史表现、紧急单偏好 - 返回排序后的推荐列表(带匹配度分数) **评分规则**: ``` 总分 = 风格匹配分 × 40% + 负载适配分 × 30% + 历史表现分 × 20% + 紧急加分 × 10% 风格匹配分: 0-100(基于技能、风格、空间类型匹配度) 负载适配分: 0-100(负载率越低分数越高) 历史表现分: 0-100(完成率、评分、按时率) 紧急加分: 0-20(是否接受紧急单) ``` --- ## 六、数据转换与兼容 ### 6.1 Parse对象转换 所有数据服务都提供了 `transformProject` 等方法,将Parse对象转换为前端友好的格式: ```typescript private transformProject(project: any): any { return { id: project.id, name: project.get('title'), title: project.get('title'), customerName: customer?.get('name') || '未知客户', designerName: assignee?.get('name') || '未分配', status: project.get('status'), currentStage: project.get('currentStage'), deadline: project.get('deadline'), isOverdue: boolean, overdueDays: number, dueSoon: boolean, urgency: 'high' | 'medium' | 'low', // ... 更多字段 }; } ``` ### 6.2 日期处理 Parse返回的日期可能是 `Date` 对象或字符串,统一转换: ```typescript const deadline = deadlineRaw instanceof Date ? deadlineRaw : (deadlineRaw ? new Date(deadlineRaw) : null); ``` ### 6.3 Pointer处理 创建Pointer引用: ```typescript const Profile = Parse.Object.extend('Profile'); const assignee = new Profile(); assignee.id = designerId; project.set('assignee', assignee); ``` --- ## 七、在Dashboard中使用 ### 7.1 注入服务 ```typescript import { ProjectDataService } from '../services/project-data.service'; import { DashboardDataService } from '../services/dashboard-data.service'; import { DesignerService } from '../services/designer.service'; export class Dashboard { constructor( private projectDataService: ProjectDataService, private dashboardDataService: DashboardDataService, private designerService: DesignerService ) {} } ``` ### 7.2 加载数据 ```typescript async ngOnInit() { // 加载KPI数据 const kpi = await this.dashboardDataService.getKPIStats(); this.totalProjects = kpi.totalProjects; this.overdueCount = kpi.overdueProjects; // 加载项目列表 this.projects = await this.projectDataService.getProjects(); // 加载设计师列表 this.designers = await this.designerService.getDesigners(); // 加载待办任务 this.todoTasks = await this.dashboardDataService.getTodoTasks(); } ``` ### 7.3 分配项目 ```typescript async onAssignProject(projectId: string, designerId: string) { const success = await this.projectDataService.assignProject( projectId, designerId ); if (success) { // 刷新项目列表 this.projects = await this.projectDataService.getProjects(); console.log('✅ 项目分配成功'); } } ``` ### 7.4 更新项目状态 ```typescript async onUpdateStatus(projectId: string, newStatus: string) { const success = await this.projectDataService.updateProject( projectId, { status: newStatus } ); if (success) { // 刷新列表 this.loadProjects(); } } ``` ### 7.5 智能推荐 ```typescript async onSmartRecommend(project: any) { this.recommendations = await this.designerService.getRecommendedDesigners( project ); this.showSmartMatch = true; } ``` --- ## 八、数据库查询最佳实践 ### 8.1 总是过滤已删除数据 ```typescript query.notEqualTo('isDeleted', true); ``` ### 8.2 总是过滤公司数据 ```typescript query.equalTo('company', this.cid); ``` ### 8.3 使用include减少查询次数 ```typescript query.include('customer', 'assignee', 'assignee.department'); ``` ### 8.4 限制查询数量 ```typescript query.limit(1000); // 避免一次查询过多数据 ``` ### 8.5 使用索引字段 优先使用已建立索引的字段进行查询: - `company` + `isDeleted` - `assignee` + `status` - `customer` + `isDeleted` - `currentStage` + `status` - `deadline` --- ## 九、错误处理 ### 9.1 Parse未初始化 ```typescript const Parse = await this.ensureParse(); if (!Parse) { console.error('❌ Parse未初始化'); return defaultValue; } ``` ### 9.2 查询失败 ```typescript try { const projects = await query.find(); return projects; } catch (error) { console.error('❌ 查询失败:', error); return []; } ``` ### 9.3 更新失败 ```typescript try { await project.save(); console.log('✅ 更新成功'); return true; } catch (error) { console.error('❌ 更新失败:', error); return false; } ``` --- ## 十、数据初始化 ### 10.1 设置公司ID 确保 `localStorage` 中有 `company` 字段: ```typescript localStorage.setItem('company', 'your-company-id'); ``` ### 10.2 测试数据创建 如果数据库为空,可以通过以下方式创建测试数据: 1. **创建公司**: ```typescript const Company = Parse.Object.extend('Company'); const company = new Company(); company.set('name', '测试公司'); await company.save(); ``` 2. **创建部门**: ```typescript const Department = Parse.Object.extend('Department'); const dept = new Department(); dept.set('name', '设计一组'); dept.set('type', 'project'); dept.set('company', company.toPointer()); await dept.save(); ``` 3. **创建设计师**: ```typescript const Profile = Parse.Object.extend('Profile'); const designer = new Profile(); designer.set('name', '张三'); designer.set('roleName', '组员'); designer.set('company', company.toPointer()); designer.set('department', dept.toPointer()); await designer.save(); ``` 4. **创建客户**: ```typescript const ContactInfo = Parse.Object.extend('ContactInfo'); const contact = new ContactInfo(); contact.set('name', '李总'); contact.set('mobile', '13800138000'); contact.set('company', company.toPointer()); await contact.save(); ``` 5. **创建项目**: ```typescript const project = await projectDataService.createProject({ title: '李总现代简约全案', customer: contact.id, assignee: designer.id, status: '进行中', currentStage: '方案深化', deadline: new Date('2024-12-31') }); ``` --- ## 十一、服务文件清单 ### 11.1 新增文件 | 文件 | 行数 | 说明 | |------|------|------| | `src/app/pages/team-leader/services/project-data.service.ts` | 579 | 项目数据服务(增删查改) | | `src/app/pages/team-leader/services/dashboard-data.service.ts` | 256 | 仪表盘数据服务(统计) | | `docs/task/2025102216-team-leader-database-integration.md` | - | 本文档 | ### 11.2 更新文件 | 文件 | 变更 | 说明 | |------|------|------| | `src/app/pages/team-leader/services/designer.service.ts` | +44行 | 新增 updateDesignerTags 方法,优化 getDesigners 方法 | --- ## 十二、测试清单 ### 12.1 ProjectDataService测试 - [ ] getProjects() - 获取项目列表 - [ ] getProjects({ status: '进行中' }) - 按状态筛选 - [ ] getProjects({ assignee: 'xxx' }) - 按设计师筛选 - [ ] getProjects({ searchTerm: '现代' }) - 关键词搜索 - [ ] getProjectById() - 获取单个项目 - [ ] createProject() - 创建项目 - [ ] updateProject() - 更新项目 - [ ] assignProject() - 分配项目 - [ ] deleteProject() - 删除项目 - [ ] getProjectStats() - 项目统计 ### 12.2 DashboardDataService测试 - [ ] getKPIStats() - KPI统计 - [ ] getStageDistribution() - 阶段分布 - [ ] getDesignerWorkloadDistribution() - 负载分布 - [ ] getTodoTasks() - 待办任务 ### 12.3 DesignerService测试 - [ ] getDesigners() - 设计师列表 - [ ] updateDesignerTags() - 更新tags - [ ] getDesignerWorkload() - 设计师负载 - [ ] getRecommendedDesigners() - 智能推荐 --- ## 十三、常见问题 ### Q1: 为什么查询不到数据? **A**: 检查以下几点: 1. `localStorage.getItem('company')` 是否正确 2. 数据库中是否有对应公司的数据 3. 数据的 `isDeleted` 是否为 `false` 4. Parse连接是否正常初始化 ### Q2: 如何调试查询? **A**: 在服务中添加详细日志: ```typescript console.log('🔍 查询条件:', { company: this.cid, status: filters?.status, assignee: filters?.assignee }); const projects = await query.find(); console.log('✅ 查询结果:', projects.length, '个项目'); ``` ### Q3: Pointer字段如何处理? **A**: 创建Pointer时使用: ```typescript const Profile = Parse.Object.extend('Profile'); const pointer = new Profile(); pointer.id = 'profileId'; project.set('assignee', pointer); ``` 读取Pointer时使用 `include`: ```typescript query.include('assignee'); const assignee = project.get('assignee'); console.log(assignee.get('name')); ``` ### Q4: 如何处理日期? **A**: 统一转换为Date对象: ```typescript const deadline = project.get('deadline'); const date = deadline instanceof Date ? deadline : new Date(deadline); ``` --- ## 十四、下一步 ### 14.1 待实现功能 - [ ] 实时数据更新(Parse LiveQuery) - [ ] 批量操作(批量分配、批量删除) - [ ] 数据缓存(减少查询次数) - [ ] 离线支持(本地数据库) ### 14.2 性能优化 - [ ] 分页查询(避免一次加载过多数据) - [ ] 虚拟滚动(长列表优化) - [ ] 查询去抖动(防止频繁查询) - [ ] 数据预加载(提前加载可能用到的数据) ### 14.3 功能扩展 - [ ] 项目文件管理(ProjectFile) - [ ] 空间产品管理(Product) - [ ] 付款记录管理(ProjectPayment) - [ ] 反馈管理(ProjectFeedback) - [ ] 问题追踪(ProjectIssue) --- ## 十五、总结 ### 15.1 核心成果 ✅ **3个数据服务**: ProjectDataService、DashboardDataService、DesignerService ✅ **完整的增删查改**: 项目、设计师、统计数据 ✅ **智能推荐算法**: 基于多维度匹配的设计师推荐 ✅ **数据转换层**: Parse对象与前端格式的无缝转换 ✅ **错误处理**: 完善的异常捕获和日志记录 ### 15.2 技术要点 - 🔐 **多租户隔离**: 通过 `company` 字段 - 🗑️ **软删除**: 使用 `isDeleted` 字段 - 🔗 **关联查询**: 使用 `include` 减少请求 - 📊 **数据统计**: 实时计算KPI和分布 - 🤖 **智能算法**: 加权负载计算和推荐评分 ### 15.3 使用建议 1. 确保正确设置公司ID 2. 优先使用索引字段查询 3. 合理使用 `include` 关联数据 4. 总是处理异常情况 5. 添加详细的日志记录 --- **文档版本**: v1.0 **最后更新**: 2025-10-22 16:00 **作者**: AI Assistant **状态**: ✅ 完成