# 运营调度领域技术方案

**领域编号**: 4.2  
**微服务**: ether-ops  
**最后更新**: 2026-02-10

---

## 一、领域概述

### 1.1 领域职责

运营调度领域是 Ether 平台的核心业务领域，负责管理：
- 综合工单管理（创建、分配、处理、关闭）
- 工单流转记录与状态机
- 消息通知系统（模板、规则、渠道）
- 工单统计与分析

### 1.2 核心概念

| 概念 | 说明 | 对应实体 |
|------|------|----------|
| **工单** | 综合业务单据，支持多种类型 | WorkOrder |
| **工单流转** | 工单状态变更记录 | WorkOrderFlow |
| **通知渠道** | 消息发送通道 | NotificationChannel |
| **通知模板** | 消息内容模板 | NotificationTemplate |
| **通知规则** | 触发条件和发送策略 | NotificationRule |
| **通知历史** | 已发送消息记录 | NotificationHistory |

---

## 二、领域模型

### 2.1 聚合根设计

#### WorkOrder（综合工单）

```java
@Entity
@Table(name = "ops_work_order")
@Data
public class WorkOrder {
    @Id
    private UUID id;
    private UUID projectId;
    
    private String orderNo;        // 工单编号: WO2024021000001
    
    // 工单类型
    private WorkOrderType orderType; // REPAIR/COMPLAINT/CLEANING/SECURITY/OTHER
    private WorkOrderStatus status;  // 状态机
    private WorkOrderPriority priority; // URGENT/HIGH/MEDIUM/LOW
    private WorkOrderSource source;   // APP/PHONE/INSPECTION/IOT/SYSTEM
    
    // 基本信息
    private String title;
    private String description;
    
    // 报修人信息
    private UUID reporterId;
    private String reporterName;
    private String reporterPhone;
    private String reporterAddress;
    
    // 关联信息
    private UUID spaceNodeId;      // 关联空间
    private UUID equipmentId;      // 关联设备
    
    // 处理人信息
    private UUID assigneeId;
    private String assigneeName;
    private LocalDateTime assignedAt;
    private LocalDateTime acceptedAt;
    private LocalDateTime startedAt;
    private LocalDateTime completedAt;
    private LocalDateTime closedAt;
    
    // 费用
    private BigDecimal actualCost;
    private BigDecimal materialCost;
    private BigDecimal laborCost;
    
    // 结果
    private String resultDescription;
    private Integer satisfactionScore;
    private String satisfactionComment;
    
    // 附件
    private String images;
    private String attachments;
    
    // 扩展属性
    private String attributes;
    
    // 审计字段
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    private UUID createdBy;
}
```

**业务规则**:
- orderNo 自动生成，格式: WO + yyyyMMdd + 5位序号
- 状态流转必须通过合法的业务操作
- 关闭工单时必须填写处理结果

#### WorkOrderFlow（工单流转记录）

```java
@Entity
@Table(name = "ops_work_order_flow")
@Data
public class WorkOrderFlow {
    @Id
    private UUID id;
    private UUID workOrderId;
    
    // 流转信息
    private WorkOrderStatus fromStatus;
    private WorkOrderStatus toStatus;
    private String action;         // 操作: ASSIGN/ACCEPT/START/COMPLETE/CLOSE
    
    // 操作人
    private UUID operatorId;
    private String operatorName;
    private LocalDateTime operateTime;
    
    // 备注
    private String remark;
    
    // 附件
    private String images;
}
```

### 2.2 状态机设计

```
┌─────────────┐
│   CREATED   │ ← 创建工单
│   (已创建)   │
└──────┬──────┘
       │ 分配
       ▼
┌─────────────┐
│  ASSIGNED   │ ← 分配给处理人
│   (已分配)   │
└──────┬──────┘
       │ 接单
       ▼
┌─────────────┐
│  ACCEPTED   │ ← 处理人接单
│   (已接单)   │
└──────┬──────┘
       │ 开始处理
       ▼
┌─────────────┐
│ IN_PROGRESS │ ← 开始处理
│   (处理中)   │
└──────┬──────┘
       │ 完成
       ▼
┌─────────────┐
│  COMPLETED  │ ← 处理完成
│   (已完成)   │
└──────┬──────┘
       │ 关闭
       ▼
┌─────────────┐
│   CLOSED    │ ← 工单关闭
│   (已关闭)   │
└─────────────┘

特殊状态:
- SUSPENDED (已挂起): 任意状态可转入，可恢复
- RETURNED (已退回): ASSIGNED状态可转入，需重新分配
```

**状态流转规则**:

| 当前状态 | 允许操作 | 下一状态 | 权限 |
|---------|---------|---------|------|
| CREATED | 分配 | ASSIGNED | 管理员/调度员 |
| ASSIGNED | 接单 | ACCEPTED | 被指派人 |
| ASSIGNED | 退回 | RETURNED | 被指派人 |
| ACCEPTED | 开始 | IN_PROGRESS | 被指派人 |
| IN_PROGRESS | 完成 | COMPLETED | 被指派人 |
| COMPLETED | 关闭 | CLOSED | 管理员/创建人 |
| * | 挂起 | SUSPENDED | 管理员 |
| SUSPENDED | 恢复 | 原状态 | 管理员 |

---

## 三、消息通知系统

### 3.1 聚合根设计

#### NotificationChannel（通知渠道）

```java
@Entity
@Table(name = "ops_notification_channel")
@Data
public class NotificationChannel {
    @Id
    private UUID id;
    private UUID projectId;
    
    private String name;
    private ChannelType type;      // SITE_MESSAGE/SMS/EMAIL/PUSH/WECHAT_WORK
    
    // 配置(JSONB)
    private String config;         // 渠道配置参数
    // SITE_MESSAGE: {}
    // SMS: {provider, apiKey, apiSecret, templateCode}
    // EMAIL: {host, port, username, password}
    // PUSH: {appKey, appSecret}
    // WECHAT_WORK: {corpId, agentId, secret}
    
    private Boolean enabled;
    private Integer priority;      // 优先级，数字越小优先级越高
    private Integer dailyLimit;    // 日发送限制
    private Integer sentToday;     // 今日已发送
    
    // 审计字段
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}
```

#### NotificationTemplate（消息模板）

```java
@Entity
@Table(name = "ops_notification_template")
@Data
public class NotificationTemplate {
    @Id
    private UUID id;
    private UUID projectId;
    
    private String name;
    private String code;           // 模板编码，唯一
    
    // 模板内容
    private String titleTemplate;
    private String contentTemplate;
    
    // 变量定义
    private String variables;      // ["orderNo", "title", "assigneeName"]
    
    // 适用渠道
    private String channels;       // ["SITE_MESSAGE", "SMS"]
    
    // 示例
    private String example;        // 渲染后的示例
    
    // 审计字段
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}

// 模板示例:
// titleTemplate: "新工单通知: {{title}}"
// contentTemplate: "您有一个新的{{orderType}}工单待处理，工单号: {{orderNo}}，请尽快处理。"
```

#### NotificationRule（通知规则）

```java
@Entity
@Table(name = "ops_notification_rule")
@Data
public class NotificationRule {
    @Id
    private UUID id;
    private UUID projectId;
    
    private String name;
    private String eventType;      // 事件类型: WORK_ORDER_CREATED/ASSIGNED/COMPLETED
    
    // 触发条件(JSONB)
    private String conditions;     // {"orderType": "REPAIR", "priority": "HIGH"}
    
    // 延迟发送
    private Integer delayMinutes;  // 延迟分钟数，0为立即发送
    
    // 通知配置
    private String templateCode;   // 关联模板
    private String receivers;      // 接收人: ["CREATOR", "ASSIGNEE", "MANAGER"]
    private String channels;       // 通知渠道优先级: ["SITE_MESSAGE", "SMS"]
    
    // 免打扰
    private String quietHours;     // 免打扰时段: "22:00-08:00"
    private Boolean enabled;
    
    // 审计字段
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
}
```

#### NotificationHistory（通知历史）

```java
@Entity
@Table(name = "ops_notification_history")
@Data
public class NotificationHistory {
    @Id
    private UUID id;
    private UUID projectId;
    
    // 通知内容
    private String title;
    private String content;
    private String eventType;
    
    // 接收人
    private UUID receiverId;
    private String receiverName;
    private String receiverPhone;
    private String receiverEmail;
    
    // 渠道信息
    private ChannelType channel;
    private String channelMsgId;   // 渠道消息ID
    
    // 状态
    private NotificationStatus status; // PENDING/SENT/FAILED/READ
    private String failReason;     // 失败原因
    
    private LocalDateTime sendTime;
    private LocalDateTime readTime;
    
    // 关联业务
    private String businessType;   // WORK_ORDER/INSPECTION/FEE
    private String businessId;
    
    // 审计字段
    private LocalDateTime createdAt;
}
```

### 3.2 通知事件类型

| 事件类型 | 触发时机 | 默认接收人 | 默认渠道 |
|---------|---------|-----------|---------|
| WORK_ORDER_CREATED | 工单创建 | 创建人 | 站内信 |
| WORK_ORDER_ASSIGNED | 工单分配 | 处理人 | 站内信+推送 |
| WORK_ORDER_ACCEPTED | 工单接单 | 创建人 | 站内信 |
| WORK_ORDER_COMPLETED | 工单完成 | 创建人 | 站内信+推送 |
| WORK_ORDER_CLOSED | 工单关闭 | - | - |
| INSPECTION_TODAY | 当天巡检提醒 | 巡检人 | 站内信+推送 |
| INSPECTION_OVERDUE | 巡检逾期 | 巡检人+管理员 | 站内信+短信 |
| FEE_UPCOMING_DUE | 费用即将到期 | 业主 | 站内信+推送 |
| FEE_OVERDUE | 费用逾期 | 业主 | 站内信+短信 |

### 3.3 通知流程

```
1. 业务事件触发
   ↓
2. 查询匹配的通知规则
   ↓
3. 渲染消息模板
   ↓
4. 确定接收人列表
   ↓
5. 选择通知渠道
   ↓
6. 检查免打扰设置
   ↓
7. 发送消息
   ↓
8. 记录发送历史
   ↓
9. 更新站内信未读数
```

---

## 四、工单统计

### 4.1 统计维度

```java
// 工单统计服务
@Service
public class WorkOrderStatisticsService {

    // 概览统计
    public WorkOrderOverviewVO getOverview(UUID projectId, LocalDate startDate, LocalDate endDate) {
        // 工单总数
        // 待处理数
        // 今日新增
        // 今日完成
        // 平均处理时长
        // 满意度评分
    }

    // 趋势统计
    public List<TrendVO> getTrend(UUID projectId, StatisticsType type, LocalDate startDate, LocalDate endDate) {
        // 按日/周/月统计工单量
        // 创建趋势
        // 完成趋势
    }

    // 类型分布
    public List<DistributionVO> getTypeDistribution(UUID projectId, LocalDate startDate, LocalDate endDate) {
        // 按工单类型统计
    }

    // 处理人排行
    public List<RankingVO> getAssigneeRanking(UUID projectId, LocalDate startDate, LocalDate endDate) {
        // 处理人工作量排行
        // 处理人满意度排行
    }

    // 超时分析
    public List<OvertimeVO> getOvertimeAnalysis(UUID projectId, LocalDate startDate, LocalDate endDate) {
        // 超时工单列表
        // 超时原因分析
    }
}
```

### 4.2 统计指标

| 指标 | 说明 | 计算方式 |
|------|------|---------|
| 工单总数 | 指定时间范围内的工单总数 | COUNT(*) |
| 待处理数 | 状态为CREATED/ASSIGNED/ACCEPTED/IN_PROGRESS的工单数 | COUNT(*) WHERE status IN (...) |
| 今日新增 | 今日创建的工单数 | COUNT(*) WHERE created_at >= today |
| 今日完成 | 今日完成的工单数 | COUNT(*) WHERE completed_at >= today |
| 平均处理时长 | 从创建到完成的平均时间 | AVG(completed_at - created_at) |
| 按时完成率 | 在SLA时间内完成的工单比例 | COUNT(on_time) / COUNT(completed) |
| 满意度评分 | 业主评价的平均分 | AVG(satisfaction_score) |

---

## 五、API 接口

### 5.1 WorkOrder API

```java
@RestController
@RequestMapping("/api/v1/ops/work-orders")
@Tag(name = "工单管理")
public class WorkOrderController {

    @PostMapping
    @Operation(summary = "创建工单")
    public Result<WorkOrderVO> create(@RequestBody @Valid WorkOrderCreateRequest request);

    @GetMapping("/{id}")
    @Operation(summary = "获取工单详情")
    public Result<WorkOrderVO> getById(@PathVariable UUID id);

    @GetMapping
    @Operation(summary = "分页查询工单")
    public Result<Page<WorkOrderVO>> page(WorkOrderQueryRequest request);

    @PutMapping("/{id}")
    @Operation(summary = "更新工单")
    public Result<WorkOrderVO> update(@PathVariable UUID id, 
                                       @RequestBody @Valid WorkOrderUpdateRequest request);

    @DeleteMapping("/{id}")
    @Operation(summary = "删除工单")
    public Result<Void> delete(@PathVariable UUID id);

    // 业务操作
    @PostMapping("/{id}/assign")
    @Operation(summary = "分配工单")
    public Result<WorkOrderVO> assign(@PathVariable UUID id,
                                       @RequestBody @Valid WorkOrderAssignRequest request);

    @PostMapping("/{id}/accept")
    @Operation(summary = "接单")
    public Result<WorkOrderVO> accept(@PathVariable UUID id);

    @PostMapping("/{id}/start")
    @Operation(summary = "开始处理")
    public Result<WorkOrderVO> start(@PathVariable UUID id);

    @PostMapping("/{id}/complete")
    @Operation(summary = "完成工单")
    public Result<WorkOrderVO> complete(@PathVariable UUID id,
                                         @RequestBody @Valid WorkOrderCompleteRequest request);

    @PostMapping("/{id}/close")
    @Operation(summary = "关闭工单")
    public Result<WorkOrderVO> close(@PathVariable UUID id,
                                      @RequestBody @Valid WorkOrderCloseRequest request);

    @PostMapping("/{id}/suspend")
    @Operation(summary = "挂起工单")
    public Result<WorkOrderVO> suspend(@PathVariable UUID id,
                                        @RequestBody WorkOrderSuspendRequest request);

    @PostMapping("/{id}/resume")
    @Operation(summary = "恢复工单")
    public Result<WorkOrderVO> resume(@PathVariable UUID id);

    // 流转记录
    @GetMapping("/{id}/flows")
    @Operation(summary = "获取流转记录")
    public Result<List<WorkOrderFlowVO>> getFlows(@PathVariable UUID id);
}
```

### 5.2 Notification API

```java
@RestController
@RequestMapping("/api/v1/ops/notifications")
@Tag(name = "消息通知")
public class NotificationController {

    // 渠道管理
    @PostMapping("/channels")
    @Operation(summary = "创建通知渠道")
    public Result<NotificationChannelVO> createChannel(@RequestBody @Valid ChannelCreateRequest request);

    @GetMapping("/channels")
    @Operation(summary = "查询渠道列表")
    public Result<List<NotificationChannelVO>> listChannels();

    // 模板管理
    @PostMapping("/templates")
    @Operation(summary = "创建消息模板")
    public Result<NotificationTemplateVO> createTemplate(@RequestBody @Valid TemplateCreateRequest request);

    @GetMapping("/templates")
    @Operation(summary = "查询模板列表")
    public Result<List<NotificationTemplateVO>> listTemplates();

    // 规则管理
    @PostMapping("/rules")
    @Operation(summary = "创建通知规则")
    public Result<NotificationRuleVO> createRule(@RequestBody @Valid RuleCreateRequest request);

    @GetMapping("/rules")
    @Operation(summary = "查询规则列表")
    public Result<List<NotificationRuleVO>> listRules();

    // 消息历史
    @GetMapping("/history")
    @Operation(summary = "分页查询消息历史")
    public Result<Page<NotificationHistoryVO>> pageHistory(NotificationHistoryQueryRequest request);

    // 个人消息
    @GetMapping("/my")
    @Operation(summary = "获取我的消息列表")
    public Result<Page<NotificationVO>> getMyNotifications(@RequestParam(required = false) Boolean unread);

    @GetMapping("/my/unread-count")
    @Operation(summary = "获取未读消息数量")
    public Result<Long> getUnreadCount();

    @PostMapping("/{id}/read")
    @Operation(summary = "标记已读")
    public Result<Void> markAsRead(@PathVariable UUID id);

    @PostMapping("/read-all")
    @Operation(summary = "全部已读")
    public Result<Void> markAllAsRead();
}
```

### 5.3 Statistics API

```java
@RestController
@RequestMapping("/api/v1/ops/statistics")
@Tag(name = "工单统计")
public class WorkOrderStatisticsController {

    @GetMapping("/overview")
    @Operation(summary = "概览统计")
    public Result<WorkOrderOverviewVO> getOverview(
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate);

    @GetMapping("/trend")
    @Operation(summary = "趋势统计")
    public Result<List<TrendVO>> getTrend(
            @RequestParam StatisticsType type,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate);

    @GetMapping("/type-distribution")
    @Operation(summary = "类型分布")
    public Result<List<DistributionVO>> getTypeDistribution(
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate);

    @GetMapping("/assignee-ranking")
    @Operation(summary = "处理人排行")
    public Result<List<RankingVO>> getAssigneeRanking(
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate startDate,
            @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate endDate);
}
```

---

## 六、实现状态与差异

### 6.1 实现状态

| 功能模块 | 实现状态 | 备注 |
|---------|---------|------|
| WorkOrder | 🟢 已实现 | 完整状态机、CRUD |
| WorkOrderFlow | 🟢 已实现 | 自动记录流转 |
| NotificationChannel | 🟢 已实现 | 基础CRUD |
| NotificationTemplate | 🟢 已实现 | 基础CRUD |
| NotificationRule | 🟢 已实现 | 基础CRUD |
| NotificationHistory | 🟢 已实现 | 基础CRUD |
| 工单与通知集成 | 🟢 已实现 | 事件驱动 |
| 工单统计 | 🟢 已实现 | 多维度统计 |
| SLA监控 | 🔴 未实现 | 超时预警 |
| 智能派单 | 🔴 未实现 | 自动分配 |

### 6.2 与设计方案的差异

| 设计项 | 设计方案 | 现有实现 | 差异分析 |
|--------|----------|----------|----------|
| **工单状态机** | 完整状态流转 | 已实现所有状态 | ✅ 符合设计 |
| **通知渠道** | 多渠道支持 | 仅站内信实现 | ⚠️ 其他渠道待扩展 |
| **工单统计** | 多维度分析 | 基本实现 | 🟡 可扩展更多维度 |
| **巡检归属** | ether-ops | ether-mdm | 🟡 领域边界模糊 |

### 6.3 待改进项

| 优先级 | 改进项 | 说明 |
|--------|--------|------|
| P2 | 实现SLA监控 | 工单超时预警和自动升级 |
| P2 | 扩展通知渠道 | 短信、邮件、推送渠道 |
| P3 | 智能派单算法 | 基于负载、技能、位置的自动分配 |
| P3 | 添加工单满意度 | 业主评价机制 |

---

## 七、数据库表结构

```sql
-- 工单表
CREATE TABLE ops_work_order (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL,
    order_no VARCHAR(32) NOT NULL,
    order_type VARCHAR(20) NOT NULL,
    status VARCHAR(20) NOT NULL DEFAULT 'CREATED',
    priority VARCHAR(20) NOT NULL DEFAULT 'MEDIUM',
    source VARCHAR(20) NOT NULL,
    title VARCHAR(200) NOT NULL,
    description TEXT,
    reporter_id UUID,
    reporter_name VARCHAR(100),
    reporter_phone VARCHAR(20),
    reporter_address VARCHAR(255),
    space_node_id UUID,
    equipment_id UUID,
    assignee_id UUID,
    assignee_name VARCHAR(100),
    assigned_at TIMESTAMP,
    accepted_at TIMESTAMP,
    started_at TIMESTAMP,
    completed_at TIMESTAMP,
    closed_at TIMESTAMP,
    actual_cost NUMERIC(12,2),
    material_cost NUMERIC(12,2),
    labor_cost NUMERIC(12,2),
    result_description TEXT,
    satisfaction_score INTEGER,
    satisfaction_comment VARCHAR(500),
    images TEXT,
    attachments TEXT,
    attributes JSONB,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    created_by UUID,
    updated_by UUID,
    UNIQUE(project_id, order_no)
);

-- 工单流转表
CREATE TABLE ops_work_order_flow (
    id UUID PRIMARY KEY,
    work_order_id UUID NOT NULL,
    from_status VARCHAR(20),
    to_status VARCHAR(20) NOT NULL,
    action VARCHAR(50) NOT NULL,
    operator_id UUID,
    operator_name VARCHAR(100),
    operate_time TIMESTAMP NOT NULL DEFAULT NOW(),
    remark VARCHAR(500),
    images TEXT
);

-- 通知渠道表
CREATE TABLE ops_notification_channel (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL,
    name VARCHAR(100) NOT NULL,
    type VARCHAR(20) NOT NULL,
    config JSONB,
    enabled BOOLEAN DEFAULT TRUE,
    priority INTEGER DEFAULT 0,
    daily_limit INTEGER,
    sent_today INTEGER DEFAULT 0,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 消息模板表
CREATE TABLE ops_notification_template (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL,
    name VARCHAR(100) NOT NULL,
    code VARCHAR(50) NOT NULL,
    title_template VARCHAR(200),
    content_template TEXT NOT NULL,
    variables JSONB,
    channels JSONB,
    example TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
    UNIQUE(project_id, code)
);

-- 通知规则表
CREATE TABLE ops_notification_rule (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL,
    name VARCHAR(100) NOT NULL,
    event_type VARCHAR(50) NOT NULL,
    conditions JSONB,
    delay_minutes INTEGER DEFAULT 0,
    template_code VARCHAR(50) NOT NULL,
    receivers JSONB,
    channels JSONB,
    quiet_hours VARCHAR(20),
    enabled BOOLEAN DEFAULT TRUE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 通知历史表
CREATE TABLE ops_notification_history (
    id UUID PRIMARY KEY,
    project_id UUID NOT NULL,
    title VARCHAR(200),
    content TEXT,
    event_type VARCHAR(50),
    receiver_id UUID,
    receiver_name VARCHAR(100),
    receiver_phone VARCHAR(20),
    receiver_email VARCHAR(100),
    channel VARCHAR(20) NOT NULL,
    channel_msg_id VARCHAR(100),
    status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
    fail_reason VARCHAR(500),
    send_time TIMESTAMP,
    read_time TIMESTAMP,
    business_type VARCHAR(50),
    business_id VARCHAR(50),
    created_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- 创建索引
CREATE INDEX idx_work_order_project ON ops_work_order(project_id);
CREATE INDEX idx_work_order_status ON ops_work_order(status);
CREATE INDEX idx_work_order_assignee ON ops_work_order(assignee_id);
CREATE INDEX idx_work_order_created ON ops_work_order(created_at);
CREATE INDEX idx_work_order_flow_order ON ops_work_order_flow(work_order_id);
CREATE INDEX idx_notification_history_receiver ON ops_notification_history(receiver_id);
CREATE INDEX idx_notification_history_status ON ops_notification_history(status);
```

---

**文档维护**: 本领域技术方案由 ether-ops 服务负责人维护
