日期: 2025-10-22 16:00
任务: 为组长端接入真实数据库,实现增删查改功能
状态: ✅ 已完成
根据项目规则文档(.trae/rules/project_rules.md)和数据库文档(docs/Database/database-tables-overview.md),为组长端实现完整的数据库接入,使组长端能够:
fmode-ng/parse (FmodeParse)company 字段隔离isDeleted 字段创建了3个核心数据服务:
src/app/pages/team-leader/services/
├── project-data.service.ts      # 项目数据服务(增删查改)
├── dashboard-data.service.ts    # 仪表盘数据服务(统计)
└── designer.service.ts          # 设计师服务(已完善)
| 服务 | 职责 | 主要方法 | 
|---|---|---|
| ProjectDataService | 项目增删查改 | getProjects, createProject, updateProject, deleteProject, assignProject | 
| DashboardDataService | 统计数据 | getKPIStats, getStageDistribution, getDesignerWorkloadDistribution, getTodoTasks | 
| DesignerService | 设计师管理 | getDesigners, getDesignerWorkload, updateDesignerTags, getRecommendedDesigners | 
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') 获取公司IDFmodeParse.with("nova") 初始化Parse连接async getProjects(filters?: {
  status?: string;
  assignee?: string;
  currentStage?: string;
  searchTerm?: string;
}): Promise<any[]>
功能:
示例:
// 获取所有进行中的项目
const projects = await projectDataService.getProjects({
  status: '进行中'
});
// 获取某个设计师的项目
const designerProjects = await projectDataService.getProjects({
  assignee: 'designerId123'
});
// 搜索项目
const searchResults = await projectDataService.getProjects({
  searchTerm: '现代简约'
});
async getProjectById(projectId: string): Promise<any>
功能:
async getProjectsByDesigner(designerId: string): Promise<any[]>
功能:
async getOverdueProjects(): Promise<any[]>
功能:
async getDueSoonProjects(): Promise<any[]>
功能:
async createProject(projectData: {
  title: string;
  customer: string;     // ContactInfo ID
  assignee?: string;    // Profile ID
  status?: string;
  currentStage?: string;
  deadline?: Date;
  data?: any;
}): Promise<any>
功能:
示例:
const newProject = await projectDataService.createProject({
  title: '李总现代简约全案',
  customer: 'contactId123',
  assignee: 'designerId456',
  status: '进行中',
  currentStage: '方案深化',
  deadline: new Date('2024-12-31'),
  data: {
    totalBudget: 120000,
    tags: ['全案设计', '现代简约']
  }
});
async updateProject(projectId: string, updates: {
  title?: string;
  status?: string;
  currentStage?: string;
  assignee?: string;
  deadline?: Date;
  data?: any;
}): Promise<boolean>
功能:
示例:
// 更新项目状态
await projectDataService.updateProject('projectId123', {
  status: '已完成',
  currentStage: '售后归档'
});
// 延长截止时间
await projectDataService.updateProject('projectId123', {
  deadline: new Date('2025-01-15')
});
async assignProject(projectId: string, designerId: string): Promise<boolean>
功能:
示例:
await projectDataService.assignProject('projectId123', 'designerId456');
async updateProjectStage(projectId: string, stage: string): Promise<boolean>
功能:
async deleteProject(projectId: string): Promise<boolean>
功能:
isDeleted = true)示例:
await projectDataService.deleteProject('projectId123');
async destroyProject(projectId: string): Promise<boolean>
功能:
async getProjectStats(): Promise<{
  total: number;
  inProgress: number;
  completed: number;
  overdue: number;
  dueSoon: number;
  unassigned: number;
}>
功能:
async getKPIStats(): Promise<{
  totalProjects: number;
  inProgressProjects: number;
  completedProjects: number;
  overdueProjects: number;
  dueSoonProjects: number;
  totalDesigners: number;
  availableDesigners: number;
  busyDesigners: number;
  overloadedDesigners: number;
}>
功能:
使用示例:
const kpi = await dashboardDataService.getKPIStats();
console.log(`总项目数: ${kpi.totalProjects}`);
console.log(`超期项目: ${kpi.overdueProjects}`);
console.log(`空闲设计师: ${kpi.availableDesigners}`);
async getStageDistribution(): Promise<Record<string, number>>
功能:
{ '订单分配': 5, '方案深化': 10, ... }async getDesignerWorkloadDistribution(): Promise<{
  idle: number;
  busy: number;
  overload: number;
}>
功能:
async getTodoTasks(): Promise<any[]>
功能:
返回数据结构:
{
  id: string;              // 任务ID
  title: string;           // 任务标题
  description: string;     // 任务描述
  deadline: Date;          // 截止时间
  priority: 'high' | 'medium' | 'low';  // 优先级
  type: 'assign' | 'review' | 'performance';  // 任务类型
  targetId: string;        // 关联项目ID
}
async getDesigners(): Promise<any[]>
功能:
返回数据结构:
{
  id: string;
  name: string;
  mobile: string;
  department: string;
  departmentId: string;
  tags: DesignerTags;  // 设计师画像
  data: any;
  profile: ParseObject;
}
async updateDesignerTags(designerId: string, tags: Partial<DesignerTags>): Promise<boolean>
功能:
示例:
await designerService.updateDesignerTags('designerId123', {
  expertise: {
    styles: ['现代简约', '北欧风'],
    skills: ['建模', '渲染', '后期'],
    spaceTypes: ['客厅', '卧室']
  },
  capacity: {
    weeklyProjects: 3,
    maxConcurrent: 5,
    avgDaysPerProject: 7
  }
});
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
async getRecommendedDesigners(project: any): Promise<any[]>
功能:
评分规则:
总分 = 风格匹配分 × 40% + 负载适配分 × 30% + 历史表现分 × 20% + 紧急加分 × 10%
风格匹配分: 0-100(基于技能、风格、空间类型匹配度)
负载适配分: 0-100(负载率越低分数越高)
历史表现分: 0-100(完成率、评分、按时率)
紧急加分: 0-20(是否接受紧急单)
所有数据服务都提供了 transformProject 等方法,将Parse对象转换为前端友好的格式:
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',
    // ... 更多字段
  };
}
Parse返回的日期可能是 Date 对象或字符串,统一转换:
const deadline = deadlineRaw instanceof Date 
  ? deadlineRaw 
  : (deadlineRaw ? new Date(deadlineRaw) : null);
创建Pointer引用:
const Profile = Parse.Object.extend('Profile');
const assignee = new Profile();
assignee.id = designerId;
project.set('assignee', assignee);
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
  ) {}
}
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();
}
async onAssignProject(projectId: string, designerId: string) {
  const success = await this.projectDataService.assignProject(
    projectId, 
    designerId
  );
  
  if (success) {
    // 刷新项目列表
    this.projects = await this.projectDataService.getProjects();
    console.log('✅ 项目分配成功');
  }
}
async onUpdateStatus(projectId: string, newStatus: string) {
  const success = await this.projectDataService.updateProject(
    projectId,
    { status: newStatus }
  );
  
  if (success) {
    // 刷新列表
    this.loadProjects();
  }
}
async onSmartRecommend(project: any) {
  this.recommendations = await this.designerService.getRecommendedDesigners(
    project
  );
  this.showSmartMatch = true;
}
query.notEqualTo('isDeleted', true);
query.equalTo('company', this.cid);
query.include('customer', 'assignee', 'assignee.department');
query.limit(1000);  // 避免一次查询过多数据
优先使用已建立索引的字段进行查询:
company + isDeletedassignee + statuscustomer + isDeletedcurrentStage + statusdeadlineconst Parse = await this.ensureParse();
if (!Parse) {
  console.error('❌ Parse未初始化');
  return defaultValue;
}
try {
  const projects = await query.find();
  return projects;
} catch (error) {
  console.error('❌ 查询失败:', error);
  return [];
}
try {
  await project.save();
  console.log('✅ 更新成功');
  return true;
} catch (error) {
  console.error('❌ 更新失败:', error);
  return false;
}
确保 localStorage 中有 company 字段:
localStorage.setItem('company', 'your-company-id');
如果数据库为空,可以通过以下方式创建测试数据:
创建公司:
const Company = Parse.Object.extend('Company');
const company = new Company();
company.set('name', '测试公司');
await company.save();
创建部门:
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();
创建设计师:
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();
创建客户:
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();
创建项目:
const project = await projectDataService.createProject({
title: '李总现代简约全案',
customer: contact.id,
assignee: designer.id,
status: '进行中',
currentStage: '方案深化',
deadline: new Date('2024-12-31')
});
| 文件 | 行数 | 说明 | 
|---|---|---|
| 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 | - | 本文档 | 
| 文件 | 变更 | 说明 | 
|---|---|---|
| src/app/pages/team-leader/services/designer.service.ts | +44行 | 新增 updateDesignerTags 方法,优化 getDesigners 方法 | 
A: 检查以下几点:
localStorage.getItem('company') 是否正确isDeleted 是否为 falseA: 在服务中添加详细日志:
console.log('🔍 查询条件:', {
  company: this.cid,
  status: filters?.status,
  assignee: filters?.assignee
});
const projects = await query.find();
console.log('✅ 查询结果:', projects.length, '个项目');
A: 创建Pointer时使用:
const Profile = Parse.Object.extend('Profile');
const pointer = new Profile();
pointer.id = 'profileId';
project.set('assignee', pointer);
读取Pointer时使用 include:
query.include('assignee');
const assignee = project.get('assignee');
console.log(assignee.get('name'));
A: 统一转换为Date对象:
const deadline = project.get('deadline');
const date = deadline instanceof Date 
  ? deadline 
  : new Date(deadline);
✅ 3个数据服务: ProjectDataService、DashboardDataService、DesignerService
✅ 完整的增删查改: 项目、设计师、统计数据
✅ 智能推荐算法: 基于多维度匹配的设计师推荐
✅ 数据转换层: Parse对象与前端格式的无缝转换
✅ 错误处理: 完善的异常捕获和日志记录
company 字段isDeleted 字段include 减少请求include 关联数据文档版本: v1.0
最后更新: 2025-10-22 16:00
作者: AI Assistant
状态: ✅ 完成