---
doc_id: line-group-implementation-spec
title: LineGroup 規格說明書
description: Aile LINE 群組聊天室類型、群組成員關係、Gateway 事件、Saga 整合、發送規則與驗收條件規格，供開發團隊參考。
slug: /specifications/line-group-implementation-spec
product: Aile
category: specification
audience:
  - developer
  - architect
  - qa
visibility: public
status: published
version: 1.0.0
owner: aile-platform
updated_at: 2026-06-13
tags:
  - Aile
  - LINE
  - LineGroup
  - Room
  - 規格說明書
rendered_html: /rendered/specifications/line-group-implementation-spec/
download: true
sidebar_position: 11
---

# LineGroup 規格說明書

**項目名稱：** Aile  
**範圍：** `Gateway Line 群組事件 → tenant → room(lineGroup)`  
**最後更新：** 2026-06-13  
**版本：** v2.0 — 結合代碼實現的完整規格  

---

## 1. 概述

LineGroup 是 Aile 針對 LINE 渠道「群組聊天」場景設計的專用聊天室類型。與私聊 `services` 聊天室不同，LineGroup 以群為單位管理 LINE 群組內的成員、消息和外發路由。

### 1.1 設計目標

| 目標 | 說明 |
|------|------|
| 一個群一個房 | 一個 `serviceNumberId + chat.code` 只對應一個啟用中的 `lineGroup` 聊天室 |
| 真實發言人 | 群消息顯示真實發言人頭像/暱稱，不是「群本身」 |
| 群級路由 | 對外回發消息只向群發一次，不按成員逐個外發 |
| 持久化關係 | 持久化群成員與聊天室的關係，支援成員展示與歸屬追蹤 |

### 1.2 非目標

- 不創建 `ServiceSession`（不走客服會話流程）
- 不走機器人歡迎語、自動接待、會話超時分配邏輯
- 不要求機器人入群前回補歷史群成員

---

## 2. 資料模型

### 2.1 聊天室類型

```java
// aile-api/aile-room-api/.../constant/RoomType.java
public static final String LineGroup = "lineGroup";
```

RoomModel 中的關鍵字段（用於 LineGroup）：

| 字段 | 來源 | 說明 |
|------|------|------|
| `type` | 固定 `"lineGroup"` | 聊天室類型標識 |
| `serviceNumberId` | `webhook.to.code` | 歸屬服務號 |
| `name` | `webhook.chat.name` | 群組名稱 |
| `avatar` | `webhook.chat.pictureUrl` | 群組頭像 |
| `externalRoomId` | `webhook.chat.code` | LINE 群組唯一 ID |
| `channel` | `"Line"` | 來源渠道 |

### 2.2 LineGroupContactRelationModel

MongoDB 集合：`aile.tenant.contact.linegroup.relation`

此模型記錄「LINE 群組 ↔ 客戶（聯繫人）」的多對多關聯。

```java
// aile-api/aile-tenant-api/.../model/servicenumber/LineGroupContactRelationModel.java
@Document("aile.tenant.contact.linegroup.relation")
@CompoundIndexes({
    @CompoundIndex(name = "serviceNumberId_1_lineGroupId_1_scopeId_1", 
        def = "{'serviceNumberId': 1, 'lineGroupId': 1, 'scopeId': 1}"),
    @CompoundIndex(name = "roomId_1_status_1", def = "{'roomId': 1, 'status': 1}"),
    @CompoundIndex(name = "contactId_1_status_1", def = "{'contactId': 1, 'status': 1}"),
    @CompoundIndex(name = "tenantId_1_lineGroupId_1_status_1", 
        def = "{'tenantId': 1, 'lineGroupId': 1, 'status': 1}")
})
public class LineGroupContactRelationModel extends BaseModel {
    String tenantId;          // 租戶 ID
    String serviceNumberId;   // 服務號 ID
    String lineGroupId;       // LINE 群組 ID（即 chat.code）
    String roomId;            // Aile 聊天室 ID
    String scopeId;           // LINE 用戶 scopeId（即 sender.code）
    String contactId;         // Aile 聯繫人 ID
    String status;            // 狀態（Enable / Disable）
}
```

---

## 3. 核心服務與實現

### 3.1 LineGroupRoomImpl（聊天室工廠策略）

位置：`aile-service/aile-service-room/.../factory/strategy/impl/LineGroupRoomImpl.java`

繼承 `CommonRoomImpl`，覆寫關鍵方法以適應群組場景：

| 方法 | 行為 |
|------|------|
| `checkMessage()` | 識別系統帳號消息，設置發送人名稱 |
| `createRoom()` | 基於 `LineGroupRoomCreateDto` 建立聊天室 |
| 消息分發 | 走 `roomMessageDispatchBatchWorkerExecutor` 批量分發 |

### 3.2 Gateway 事件處理（tenant 服務）

位置：`aile-service/aile-service-tenant/.../gateway/factory/strategy/impl/`

| 事件 | 實現類 | 處理邏輯 |
|------|--------|----------|
| `Join` | `AbstractEventImpl` (共用) | 建立/恢復群級 scope + lineGroup 聊天室 |
| `Leave` | `AbstractEventImpl` (共用) | 停用 lineGroup + 群級 scope + 外部成員關係 |
| `MemberJoin` | `MemberJoinEventImpl` | 建立聯繫人 + 成員級 scope + 房間成員 |
| `MemberLeave` | `MemberLeaveEventImpl` | 停用房間成員關係 |
| `Messaging` | `MessagingEventImpl` | 懶建立群/成員 → 寫入消息 |

### 3.3 LineGroupContactRelationService

位置：`aile-service/aile-service-tenant/.../service/servicenumber/`

提供 Line 群組與客戶關聯的 CRUD 及查詢能力：

```java
public interface LineGroupContactRelationService extends BaseMongoService<LineGroupContactRelationModel> {
    // 依 lineGroupId + scopeId 查找關聯
    // 依 roomId 查詢成員列表
    // 批量啟用/停用關聯
}
```

對應 Controller：`LineGroupContactRelationController`（路由：`/servicenumber/linegroup-contact-relation`）

### 3.4 Saga 整合

在租戶資料歸戶/清理流程中，LineGroup 關聯資料也納入 Saga 步驟：

- `MergeLineGroupContactRelationStep`：合併 LineGroup 聯繫人關聯
- `MergeAnonymousVisitorContactDataStep`：合併匿名訪客資料（包含 LineGroup 關聯）

---

## 4. 事件處理完整流程

### 4.1 Join（LINE OA 被拉入群）

```
Gateway ──→ aile-service-tenant (GatewayController)
  │
  ├── 解析 webhook：提取 chat.code、chat.name、chat.pictureUrl、to.code
  │
  ├── 建立/恢復群級 scope
  │     scopeId = chat.code
  │     code = from.code
  │     channel = Line
  │     serviceNumberId = to.code
  │
  ├── 建立/恢復 lineGroup 聊天室（透過 RoomFeign 呼叫 room 服務）
  │     type = lineGroup
  │     serviceNumberId = to.code
  │     externalRoomId = chat.code
  │     name = chat.name
  │     avatar = chat.pictureUrl
  │
  └── 加入服務號為房間成員
```

### 4.2 MemberJoin（新成員加入）

```
Gateway ──→ aile-service-tenant
  │
  ├── 解析 members[]：提取每個 member 的 code、name、pictureUrl
  │
  ├── 確保群級 scope 和 lineGroup 存在（不存在則補建）
  │
  └── 對每個 member：
        ├── 建立/更新 TenantContactModel（聯繫人資料）
        ├── 建立/更新成員級 scope（scopeId = member.code）
        ├── 建立 LineGroupContactRelationModel
        └── 加入 lineGroup 房間成員
```

### 4.3 Messaging（群消息）

```
Gateway ──→ aile-service-tenant ──→ aile-service-room
  │
  ├── 確保群級 scope 和 lineGroup 存在
  │
  ├── 依 sender.code 懶建立聯繫人/成員級 scope/房間成員
  │     （若此前未收到 MemberJoin）
  │
  ├── 消息寫入 lineGroup 聊天室
  │     senderId = 實際成員聯繫人 ID（非 chat.code）
  │     senderName = 聯繫人名稱
  │
  └── 不創建 ServiceSession，不走機器人流程
```

### 4.4 Leave（LINE OA 被移出群）

```
Gateway ──→ aile-service-tenant
  │
  ├── 停用 lineGroup 聊天室（軟刪除）
  ├── 停用群級 scope
  └── 批量停用房間成員關係
```

---

## 5. 發送規則詳解

### 5.1 群級路由

LINE 群組消息外發使用 **群級 scope**（`scopeId = chat.code`），而非成員級 scope。

```
對外發送目標：scopeId = chat.code
路由方式：GatewayFeign → Gateway 群級路由
發送次數：一條內部消息 → 一次外部發送
```

### 5.2 發送者展示

| 展示元素 | 取值來源 |
|----------|----------|
| senderId | 實際成員對應的聯繫人 ID |
| senderName | 成員聯繫人名稱 |
| senderAvatar | 成員聯繫人頭像 |

**禁止**使用 `chat.code` 或群級 scope 作為消息發送者。

### 5.3 與 Services Room 的關鍵差異

| 維度 | Services Room | LineGroup Room |
|------|---------------|----------------|
| 會話模型 | 每客戶一個 ServiceSession | 無 ServiceSession |
| 外發目標 | 成員級 scope（單個客戶） | 群級 scope（整個群） |
| 機器人流程 | 支援（歡迎語/自動接待/超時） | 不支援 |
| 消息發送者 | 客戶本人 | 群內實際發言人 |
| 房間類型 | `services` | `lineGroup` |

---

## 6. 冪等性與一致性

- 同一 `serviceNumberId + chat.code` 只允許一個啟用中的 lineGroup
- 同一成員在同一 lineGroup 中只允許一條啟用中的成員關係
- 事件亂序時採取「補建優先、刪除冪等」策略
- 處理順序：先保證群級資料 → 再保證成員級資料 → 最後寫入消息

---

## 7. 關鍵檔案索引

| 層級 | 檔案 | 說明 |
|------|------|------|
| API Model | `aile-api/aile-room-api/.../constant/RoomType.java` | `LineGroup` 常量 |
| API Model | `aile-api/aile-room-api/.../dto/room/LineGroupRoomCreateDto.java` | 建立聊天室 DTO |
| API Model | `aile-api/aile-room-api/.../dto/room/LineGroupRoomInfoSyncDto.java` | 群資訊同步 DTO |
| API Model | `aile-api/aile-room-api/.../dto/room/LineGroupRoomListDto.java` | 列表查詢 DTO |
| API Model | `aile-api/aile-tenant-api/.../model/servicenumber/LineGroupContactRelationModel.java` | 群客戶關聯 |
| API Model | `aile-api/aile-tenant-api/.../dto/servicenumber/LineGroupContactRelationListDto.java` | 關聯查詢 DTO |
| Room 服務 | `aile-service/aile-service-room/.../factory/strategy/impl/LineGroupRoomImpl.java` | 群組聊天室策略 |
| Room 服務 | `aile-service/aile-service-room/.../util/LineGroupUnreadWrite.java` | 未讀數寫入 |
| Room 服務 | `aile-service/aile-service-room/.../util/LineGroupMemberRoomRedisUtil.java` | 成員房間 Redis |
| Room 服務 | `aile-service/aile-service-room/.../util/LineGroupMemberUnread.java` | 成員未讀 |
| Tenant 服務 | `aile-service/aile-service-tenant/.../gateway/factory/strategy/impl/MemberJoinEventImpl.java` | MemberJoin 事件 |
| Tenant 服務 | `aile-service/aile-service-tenant/.../gateway/factory/strategy/impl/MemberLeaveEventImpl.java` | MemberLeave 事件 |
| Tenant 服務 | `aile-service/aile-service-tenant/.../service/servicenumber/impl/LineGroupContactRelationServiceImpl.java` | 關聯服務實現 |
| Tenant 服務 | `aile-service/aile-service-tenant/.../controller/servicenumber/LineGroupContactRelationController.java` | 關聯 API |
| Saga | `aile-service/aile-service-tenant/.../saga/step/MergeLineGroupContactRelationStep.java` | Saga 合併步驟 |

---

## 8. 驗收條件

### 功能驗收

- [x] `Join` 到達後建立 lineGroup 聊天室，服務號自動加入為成員
- [x] 聊天室資料可查到 LINE 群組 `chat.code`
- [x] `MemberJoin` 到達後為新成員建立聯繫人、scope 和房間成員關係
- [x] `Messaging` 到達後群消息進入 lineGroup，展示真實發送者
- [x] `MemberLeave` 到達後停用成員關係，保留聯繫人資料
- [x] `Leave` 到達後停用 lineGroup，保留歷史消息
- [x] 從 lineGroup 回發消息只向群組發送一次

### 資料驗收

- [x] 群級路由和成員級身份可區分查詢
- [x] 群級外部 ID 與成員級 scopeId 不混用
- [x] 同一 LINE 用戶可出現在多個 lineGroup 中，聯繫人不重複

---

## Changelog

| 日期 | 版本 | 變更內容 |
|------|------|----------|
| 2026-03-27 | v1.0 | 初始 Line 群組聊天室細規格 |
| 2026-06-13 | v2.0 | 結合代碼實現補充完整規格（資料模型、服務實現、事件流程、檔案索引） |
