# 动态数据对接和企业微信认证集成文档

## 概述

本文档说明了如何在 yss-project 中集成企业微信认证 (WxworkAuthGuard) 和动态数据服务 (FmodeParse)。

## 已完成的功能

### 1. 企业微信认证集成

#### 路由守卫配置
已在 `src/app/app.routes.ts` 中为所有主要路由添加了 `WxworkAuthGuard`：

```typescript
import { WxworkAuthGuard } from 'fmode-ng/social';

// 客服路由
{
  path: 'customer-service',
  canActivate: [WxworkAuthGuard],
  children: [...]
}

// 设计师路由
{
  path: 'designer',
  canActivate: [WxworkAuthGuard],
  children: [...]
}

// 组长路由
{
  path: 'team-leader',
  canActivate: [WxworkAuthGuard],
  children: [...]
}

// 财务路由
{
  path: 'finance',
  canActivate: [WxworkAuthGuard],
  children: [...]
}

// 人事路由
{
  path: 'hr',
  canActivate: [WxworkAuthGuard],
  children: [...]
}

// 管理员路由
{
  path: 'admin',
  canActivate: [WxworkAuthGuard],
  children: [...]
}
```

#### 组件级认证
已在主要页面组件中集成了 `WxworkAuth` 类：

**管理员仪表板** (`src/app/pages/admin/dashboard/dashboard.ts`)
```typescript
import { WxworkAuth, FmodeQuery, FmodeObject, FmodeUser } from 'fmode-ng/core';

private initAuth(): void {
  this.wxAuth = new WxworkAuth({
    cid: 'cDL6R1hgSi'  // 公司帐套ID
  });
}

private async authenticateAndLoadData(): Promise<void> {
  const { user } = await this.wxAuth.authenticateAndLogin();
  if (user) {
    console.log('✅ 管理员登录成功:', user.get('username'));
    await this.loadDashboardData();
  }
}
```

**客服仪表板** (`src/app/pages/customer-service/dashboard/dashboard.ts`)
```typescript
// 同样的认证模式，加载咨询统计数据
private async loadConsultationStats(): Promise<void> {
  const consultationQuery = new FmodeQuery('Consultation');
  consultationQuery.equalTo('status', 'new');
  const newConsultations = await consultationQuery.count();
  this.stats.newConsultations.set(newConsultations);
}
```

**设计师仪表板** (`src/app/pages/designer/dashboard/dashboard.ts`)
```typescript
// 同样的认证模式，加载任务数据
```

### 2. FmodeParse 初始化

已在 `src/app/app.ts` 中初始化了 FmodeParse：

```typescript
import { FmodeParse } from 'fmode-ng/core';

private initParse(): void {
  try {
    const Parse = FmodeParse.with("nova");
    console.log('✅ FmodeParse 初始化成功');
  } catch (error) {
    console.error('❌ FmodeParse 初始化失败:', error);
  }
}
```

### 3. 动态数据对接

#### 管理员仪表板数据源

**项目统计**
```typescript
private async loadProjectStats(): Promise<void> {
  const projectQuery = new FmodeQuery('Project');

  // 总项目数
  const totalProjects = await projectQuery.count();
  this.stats.totalProjects.set(totalProjects);

  // 进行中项目数
  projectQuery.equalTo('status', '进行中');
  const activeProjects = await projectQuery.count();
  this.stats.activeProjects.set(activeProjects);
}
```

**用户统计**
```typescript
private async loadUserStats(): Promise<void> {
  // 设计师统计
  const designerQuery = new FmodeQuery('Profile');
  designerQuery.equalTo('role', 'designer');
  const designers = await designerQuery.count();
  this.stats.totalDesigners.set(designers);
}
```

**收入统计**
```typescript
private async loadRevenueStats(): Promise<void> {
  const orderQuery = new FmodeQuery('Order');
  orderQuery.equalTo('status', 'paid');
  const orders = await orderQuery.find();

  let totalRevenue = 0;
  for (const order of orders) {
    const amount = order.get('amount') || 0;
    totalRevenue += amount;
  }
  this.stats.totalRevenue.set(totalRevenue);
}
```

#### 客服仪表板数据源

**咨询统计**
```typescript
private async loadConsultationStats(): Promise<void> {
  // 新咨询数
  const consultationQuery = new FmodeQuery('Consultation');
  consultationQuery.equalTo('status', 'new');
  consultationQuery.greaterThanOrEqualTo('createdAt', new Date(new Date().setHours(0,0,0,0)));
  const newConsultations = await consultationQuery.count();
  this.stats.newConsultations.set(newConsultations);
}
```

### 4. NovaStorage 上传组件

#### 上传组件 (`src/app/shared/components/upload-component/`)

直接使用 `NovaStorage.withCid(cid).upload()` 方法，无需额外服务层。

**功能特性：**
- 拖拽上传支持
- 多文件上传
- 文件类型和大小验证
- 上传进度显示
- 预览功能
- 错误处理
- 自动文件路径生成

**核心实现：**
```typescript
import { NovaStorage, NovaFile } from 'fmode-ng/core';

// 初始化存储服务
private async initStorage(): Promise<void> {
  const cid = localStorage.getItem('company') || 'cDL6R1hgSi';
  this.storage = await NovaStorage.withCid(cid);
}

// 上传文件
const uploaded: NovaFile = await this.storage.upload(file, {
  prefixKey: 'project/pid/', // 可选的路径前缀
  onProgress: (p) => console.log('进度:', p.total.percent),
});
```

**使用示例：**
```typescript
import { UploadComponent, UploadResult } from './shared/components/upload-component/upload.component';

@Component({
  standalone: true,
  imports: [UploadComponent]
})
export class MyComponent {
  onUploadComplete(results: UploadResult[]) {
    results.forEach(result => {
      if (result.success) {
        console.log('上传成功:', result.file?.url);
        // 保存文件信息到数据库
        this.saveFileInfo(result.file);
      }
    });
  }

  private async saveFileInfo(file: NovaFile) {
    // 保存 key, url, name, type, size, metadata, md5 等信息
    console.log('文件信息:', {
      key: file.key,
      url: file.url,
      name: file.name,
      size: file.size
    });
  }
}
```

```html
<app-upload-component
  [accept]="image/*"
  [multiple]="true"
  [maxSize]="10"
  [prefixKey]="'demo/images/'"
  [showPreview]="true"
  (uploadComplete)="onUploadComplete($event)">
</app-upload-component>
```

**示例组件：** `src/app/shared/components/upload-example/` 提供了完整的使用示例和配置选项演示。

### 5. Stage-order 项目文件管理

#### ProjectFile 表集成

在订单分配阶段 (`src/modules/project/pages/project-detail/stages/stage-order.component`) 中集成了项目文件管理功能：

**核心功能：**
- ✅ 使用 `NovaStorage.withCid(cid).upload()` 上传文件
- ✅ 文件信息存储到 `ProjectFile` 表
- ✅ 支持企业微信拖拽文件上传
- ✅ 文件路径：`projects/{projectId}/` 前缀
- ✅ 支持多文件上传和进度显示

**实现代码：**
```typescript
// 初始化 NovaStorage
private async initStorage(): Promise<void> {
  const cid = localStorage.getItem('company') || this.cid || 'cDL6R1hgSi';
  this.storage = await NovaStorage.withCid(cid);
}

// 上传文件到项目目录
const uploaded: NovaFile = await this.storage.upload(file, {
  prefixKey: `projects/${this.projectId}/`,
  onProgress: (p) => { /* 进度回调 */ }
});

// 保存到 ProjectFile 表
const projectFile = new Parse.Object('ProjectFile');
projectFile.set('project', this.project.toPointer());
projectFile.set('name', file.name);
projectFile.set('url', uploaded.url);
projectFile.set('key', uploaded.key);
projectFile.set('uploadedBy', this.currentUser.toPointer());
projectFile.set('source', source); // 标记来源
await projectFile.save();
```

**企业微信拖拽支持：**
```typescript
// 检测企业微信环境
if (typeof window !== 'undefined' && (window as any).wx) {
  this.wxFileDropSupported = true;
  this.initWxWorkFileDrop();
}

// 监听拖拽事件
document.addEventListener('drop', (e) => {
  if (this.isWxWorkFileDrop(e)) {
    this.handleWxWorkFileDrop(e);
  }
});
```

## 数据表结构

### 新增数据表

**ProjectFile** - 项目文件表
- project: 项目关联 (Pointer to Project)
- name: 文件名称
- originalName: 原始文件名
- url: 文件访问URL
- key: 存储键值
- type: 文件类型
- size: 文件大小
- uploadedBy: 上传者 (Pointer to Profile)
- uploadedAt: 上传时间
- source: 来源 (企业微信拖拽/手动选择/其他)
- md5: 文件MD5值
- metadata: 文件元数据

### 主要数据表

1. **Project** - 项目表
   - name: 项目名称
   - status: 项目状态 (进行中, 已完成, 异常)
   - owner: 负责人 (Pointer to Profile)
   - startDate: 开始日期
   - endDate: 结束日期

2. **Profile** - 用户档案表
   - name: 姓名
   - role: 角色 (designer, customer, admin)
   - level: 级别 (junior, mid, senior)
   - completedProjects: 完成项目数
   - activeProjects: 进行中项目数

3. **Consultation** - 咨询表
   - status: 状态 (new, pending_assignment, processing)
   - customer: 客户 (Pointer to Profile)
   - createdAt: 创建时间

4. **Order** - 订单表
   - status: 订单状态
   - amount: 金额
   - customer: 客户 (Pointer to Profile)
   - invoiceNo: 发票号

5. **AfterSales** - 售后表
   - status: 状态
   - project: 项目 (Pointer to Project)
   - createdAt: 创建时间

6. **Images** - 图片表
   - originalName: 原始文件名
   - url: 图片URL
   - thumbnailUrl: 缩略图URL
   - size: 文件大小

## 使用指南

### 1. 新页面添加认证

对于新页面，按以下步骤添加认证：

```typescript
import { WxworkAuth, FmodeQuery, FmodeObject, FmodeUser } from 'fmode-ng/core';

@Component({...})
export class NewPageComponent {
  private wxAuth: WxworkAuth;
  private currentUser: FmodeUser | null = null;

  constructor() {
    this.initAuth();
  }

  private initAuth(): void {
    this.wxAuth = new WxworkAuth({
      cid: 'cDL6R1hgSi'
    });
  }

  async ngOnInit(): Promise<void> {
    await this.authenticateAndLoadData();
  }

  private async authenticateAndLoadData(): Promise<void> {
    const { user } = await this.wxAuth.authenticateAndLogin();
    if (user) {
      this.currentUser = user;
      await this.loadData();
    }
  }
}
```

### 2. 数据查询示例

```typescript
// 基本查询
const query = new FmodeQuery('Project');
const projects = await query.find();

// 条件查询
query.equalTo('status', '进行中');
query.greaterThan('createdAt', new Date('2025-01-01'));

// 排序
query.descending('createdAt');

// 分页
query.limit(20);
query.skip(0);

// 计数
const count = await query.count();

// 关联查询
const userQuery = new FmodeQuery('Profile');
userQuery.include('projects');
```

### 3. 数据保存示例

```typescript
// 创建新对象
const project = new FmodeObject('Project');
project.set('name', '新项目');
project.set('status', '进行中');
const savedProject = await project.save();

// 更新对象
savedProject.set('status', '已完成');
await savedProject.save();

// 删除对象
await savedProject.destroy();
```

### 4. NovaStorage 上传组件使用

```typescript
import { UploadComponent, UploadResult } from '../shared/components/upload-component/upload.component';

@Component({
  standalone: true,
  imports: [UploadComponent]
})
export class MyComponent {
  async onUploadComplete(results: UploadResult[]) {
    // 处理上传结果
    for (const result of results) {
      if (result.success && result.file) {
        console.log('上传成功:', result.file.url);
        // 保存文件信息到数据库
        await this.saveFileInfo(result.file);
      }
    }
  }

  async onUploadError(error: string) {
    console.error('上传失败:', error);
  }

  private async saveFileInfo(file: NovaFile) {
    // 保存文件信息到 Attachment 表或其他相关表
    console.log('保存文件:', file.key, file.url);
  }
}
```

## 错误处理

所有数据操作都包含错误处理，在API调用失败时会自动降级到模拟数据：

```typescript
try {
  await this.loadDashboardData();
} catch (error) {
  console.error('❌ 数据加载失败:', error);
  // 降级到模拟数据
  this.loadMockData();
}
```

## 注意事项

1. **认证配置**: 确保 `cid: 'cDL6R1hgSi'` 在 `WxworkSDK.companyMap` 中有对应配置
2. **数据表名**: 确保后端数据表名与代码中使用的表名一致
3. **权限设置**: 确保企业微信用户有相应的数据访问权限
4. **NovaStorage**: 使用 `NovaStorage.withCid(cid)` 自动初始化，无需手动配置 Provider
5. **文件路径**: 自动生成格式 `storage/company/<cid>/<prefixKey>/<YYYYMMDD>/<HHmmss-rand>-<name>`
6. **文件大小**: 默认上传文件大小限制为 10MB，可在组件中自定义
7. **prefixKey**: 通过 `prefixKey` 参数指定文件存储路径前缀，如 `'project/pid/'`

## 后续优化建议

1. **缓存机制**: 为频繁查询的数据添加缓存
2. **实时更新**: 使用 WebSocket 实现数据实时更新
3. **离线支持**: 添加离线数据存储和同步功能
4. **批量操作**: 优化批量数据操作的性能
5. **权限细化**: 根据用户角色实现更细粒度的数据访问控制