# 组长端数据库接入完整指南

**日期**: 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<void> {
  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<any[]>
```

**功能**:
- 查询当前公司的所有项目
- 支持按状态、设计师、阶段筛选
- 支持关键词搜索（项目名称、客户名称）
- 自动关联客户和设计师信息

**示例**:
```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<any>
```

**功能**:
- 查询单个项目详情
- 包含客户、设计师、部门关联信息

#### 3.2.3 获取设计师的项目列表

```typescript
async getProjectsByDesigner(designerId: string): Promise<any[]>
```

**功能**:
- 查询设计师负责的所有项目
- 自动排除已删除项目

#### 3.2.4 获取超期项目

```typescript
async getOverdueProjects(): Promise<any[]>
```

**功能**:
- 查询所有超期项目
- 按超期时间倒序排列

#### 3.2.5 获取临期项目

```typescript
async getDueSoonProjects(): Promise<any[]>
```

**功能**:
- 查询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<any>
```

**功能**:
- 创建新项目
- 自动设置公司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<boolean>
```

**功能**:
- 更新项目字段
- 更新阶段时自动记录阶段历史
- 支持部分更新

**示例**:
```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<boolean>
```

**功能**:
- 分配项目给设计师
- 自动更新状态为"进行中"
- 自动更新阶段为"方案深化"

**示例**:
```typescript
await projectDataService.assignProject('projectId123', 'designerId456');
```

#### 3.4.3 更新项目阶段

```typescript
async updateProjectStage(projectId: string, stage: string): Promise<boolean>
```

**功能**:
- 单独更新项目阶段
- 自动记录阶段历史

### 3.5 删除操作

#### 3.5.1 软删除项目

```typescript
async deleteProject(projectId: string): Promise<boolean>
```

**功能**:
- 软删除项目（设置 `isDeleted = true`）
- 项目仍保留在数据库中
- 查询时会自动过滤

**示例**:
```typescript
await projectDataService.deleteProject('projectId123');
```

#### 3.5.2 物理删除项目（谨慎使用）

```typescript
async destroyProject(projectId: string): Promise<boolean>
```

**功能**:
- 物理删除项目
- 永久删除，无法恢复
- **仅在必要时使用**

### 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<Record<string, number>>
```

**功能**:
- 统计各阶段的项目数量
- 返回 `{ '订单分配': 5, '方案深化': 10, ... }`

### 4.3 获取设计师负载分布

```typescript
async getDesignerWorkloadDistribution(): Promise<{
  idle: number;
  busy: number;
  overload: number;
}>
```

**功能**:
- 统计设计师负载情况
- 空闲（0个项目）、忙碌（1-2个项目）、超负荷（≥3个项目）

### 4.4 获取待办任务

```typescript
async getTodoTasks(): Promise<any[]>
```

**功能**:
- 自动生成待办任务列表
- 包含：待分配项目、超期项目、临期项目
- 按优先级排序

**返回数据结构**:
```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<any[]>
```

**功能**:
- 查询所有设计师（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<DesignerTags>): Promise<boolean>
```

**功能**:
- 更新设计师的技能、容量、紧急单偏好等
- 支持部分更新（深度合并）

**示例**:
```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<any[]>
```

**功能**:
- 根据项目需求推荐最合适的设计师
- 综合考虑：风格匹配、负载情况、历史表现、紧急单偏好
- 返回排序后的推荐列表（带匹配度分数）

**评分规则**:
```
总分 = 风格匹配分 × 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  
**状态**: ✅ 完成


