---
doc_id: aile-point-acquisition-design-spec
title: Aile 點獲取體系設計規格（開發參考）
description: Aile 點獲取體系的階段邊界、架構、Backend、Admin、AileApp 與活動引擎設計規格，供開發團隊在正式規格發布前參考。
slug: /developers/aile-point-acquisition-design-spec
product: Aile
category: development-reference
audience:
  - developer
  - product
visibility: public
status: published
version: 2.0.0
owner: aile-platform
updated_at: 2026-06-12
tags:
  - Aile
  - 點數體系
  - 設計規格
  - 開發參考
rendered_html: /rendered/developers/aile-point-acquisition-design-spec/
download: true
sidebar_position: 1
---

# Aile 點獲取體系 — 完整設計規格（統整修訂版 v2.0）

版本：v2.0（統整修訂版）

日期：2026-06-07

來源整合：技術設計/活動引擎/管理臺統整方案、開發規格 v1.0、使用者故事 v1.0

狀態：評審稿（已對齊三份文件衝突並給出修訂結論）

> [!NOTE]
> 本文件定位為開發參考用的臨時設計稿，協助團隊在正式規格文件發布前對齊設計邊界；待正式規格建立後，可由正式文件取代並移除此參考稿。

## 0. 本版說明

<aside>
🧭

本檔案將三份既有材料統整為**單一事實來源（Single Source of Truth）**：技術設計統整方案（架構/活動引擎/管理臺程式碼級設計）、開發規格 v1.0（邊界/階段/欄位約定）、使用者故事 v1.0（按端拆解）。文末第 12 節集中列出**三份文件之間的衝突點與修訂結論**，正文已按修訂結論統一口徑。

</aside>

## 1. 背景與目標

現有 Aile 配額體系已覆蓋“扣點側”能力（超額扣點、套餐/延長包購買、欠費處理、AIPool 繫結與授權），但“獲點側”尚未形成完整閉環，導致運營補償、使用者自購、活動激勵與點數收支審計分散。

本規格目標：建立 Aile 點獲取體系統一開發邊界。

1. Backend 提供**統一給點底座**，封裝 AIPool 累點能力、冪等、審計與狀態流轉。
2. Admin 支援 Aile 官方管理員對個人租戶給點，並在個人租戶後臺提供 Aile 點內嵌購買體驗。
3. AileApp 在後續階段提供購買入口、收支明細與活動中心。
4. 活動給點系統作為獨立複雜能力，先定義事件、規則、預算與審計邊界，避免首期過度實現。

## 2. 資訊來源與現有 Backend 對齊

### 2.1 資訊來源

- 技術設計 / 活動引擎 / 管理臺統整方案。
- Aile 點獲取體系開發規格 v1.0。
- Aile 點獲取體系使用者故事 v1.0（按端分組）。
- Backend 只讀參考倉庫：`springcloud-aile`。

### 2.2 現有可複用基礎（只讀檢查）

- `aile-service-tenant` 已存在配額/點數模組，主包路徑 `com.aile.service.tenant.service.quota`。
- 已存在 Mongo 審計模型 `TenantPointAuditModel`，集合 `aile.tenant.point.audit`，欄位覆蓋 `eventId`、`tenantId`、`openId`、`auditScene`、`sourceBizId`、`ailePointsChange`、`status`、`outTraceId` 等。
- 已存在 `TenantPointAuditServiceImpl`，支援審計寫入、列表查詢、成功/處理中/失敗狀態更新（當前統計口徑偏扣點側）。
- 已存在 `TenantAipoolDeductClient.increasePoint(...)`，可呼叫 AIPool 累點介面，需產品化封裝。
- 已存在 `TenantPointAuditController`，路徑 `/point/audit/v1/list`，作為 Admin 審計列表基礎。
- 已存在 Pub/Sub、Redis、MongoTemplate、BaseMongoService 等基礎設施。

**結論（DRY）**：不新建獨立賬務底座，在現有 quota/point audit 能力上擴充套件。給點執行邏輯收斂到新的 `TenantPointGrantService`，審計讀寫仍復用 `TenantPointAuditService`。

## 3. 範圍與階段

### 3.1 Phase 1：優先閉環

包含：

- 統一給點基礎服務：冪等、審計、AIPool 累點呼叫、狀態更新。
- Aile 官方管理員給點：Admin 租戶列表入口、直接給點、原因與備註、操作記錄、防重複提交。
- Admin 個人租戶內嵌購買：在“我的配額/點數”區域提供購買入口，接入 AIPool 支付/購買能力。
- 基礎給點/扣點收支審計：按租戶、場景、狀態、時間查詢。

不包含：審批流；Aile 側給點額度上限配置；AilePro 聊天介面快捷給點；AileApp 管理員給點；完整活動中心與複雜活動規則配置。

### 3.2 Phase 2：體驗增強

- AileApp 點數購買入口與超額購買引導、點數收支明細、到賬通知與餘額重新整理。
- Admin 報表增強：按給點來源、原因、時間統計。

### 3.3 Phase 3：活動給點系統

- 活動配置、規則、狀態機、預算控制。
- 使用者行為事件消費、Redis/Lua 原子預算扣減與防刷。
- 活動參與記錄、補發、失敗重試。
- AileApp 活動中心與獎勵領取狀態展示。

## 4. 角色與業務物件

| 角色/物件 | 定義 |
| --- | --- |
| Aile 官方管理員 | Aile 官方租戶下具備後臺管理許可權的運營角色，可對個人租戶執行給點。Phase 1 不做更細粒度審批與額度分級。角色編碼見第 13 節待確認。 |
| 商務人士 / 個人租戶 | 一個自然人對應的個人租戶，在 AIPool 的 Aile 租戶下擁有 Aile 點賬戶。 |
| AIPool 點數中心 | Aile 點真實賬務系統，負責點數增加、扣減、支付、餘額與交易流水（賬務真相）。 |
| `openId` | AIPool 累點/扣點目標使用者標識，給點必須明確到該標識。 |
| `eventId` | Aile 側全域性冪等鍵，同一事件重複提交不得重複給點。 |
| `TenantPointAudit` | Aile 側審計底稿，用於冪等、狀態追蹤、排障與報表。 |
| `SYSTEM_GLOBAL_TENANT` | 平臺級行為（如平臺級活動）的全域性租戶常量。**不可用於管理員給點的 tenantId**——管理員給點的 `tenantId` 必須為目標個人租戶（見第 12 節衝突 C8）。 |

## 5. 總體架構

```mermaid
flowchart TD
    A[Admin 手動給點] --> B[Backend 統一給點服務 TenantPointGrantService]
    C[使用者自購支付成功] --> B
    D[活動引擎給點事件] --> B
    B --> E[寫入 TenantPointAudit: Pending]
    E --> F[markProcessing]
    F --> G[呼叫 TenantAipoolDeductClient.increasePoint]
    G --> H[AIPool 點數中心]
    H --> I["AIPool 返回結果"]
    I -->|成功| J[更新審計: Success + outTraceId]
    I -->|業務拒絕| K[更新審計: Failed + failReason]
    I -->|超時/通訊異常| L[保留 Processing + 記錄原因, 交由補單 Job/手動重試]
    J --> M[Admin/App 查詢收支明細]
    K --> N[Admin 排障與重試]
    L --> N
```

活動給點擴充套件架構：

```mermaid
flowchart TD
    U[使用者行為: 註冊/開戶/加秘書/邀請] --> P1[GCP Pub/Sub: User Event Topic]
    P1 --> AE[活動規則引擎 aipool-activity-service*]
    AE --> R[三階段規則匹配]
    R --> LUA[Redis Lua 原子預算扣減]
    LUA -->|預算充足| P2[釋出 tenant-point-grant 事件]
    LUA -->|預算不足| EX[活動狀態 Exhausted + 告警]
    P2 --> B[Backend 統一給點服務]
```

<aside>
⚠️

`aipool-activity-service`：活動引擎服務歸屬**已決議為獨立服務 `aipool-activity-service`**（見第 13 節決議 Q8）。

</aside>

## 6. Backend 規格

### 6.1 統一給點服務 `TenantPointGrantService`

新增業務語義明確的服務 `TenantPointGrantService`（實現類 `TenantPointGrantServiceImpl`），**不要把給點編排流程堆入 `TenantPointAuditServiceImpl`**。審計讀寫仍復用 `TenantPointAuditService`。

核心職責：

1. 接收統一給點命令。
2. 校驗 `eventId`、`openId`、點數、場景、來源業務 ID。
3. 先寫入 `TenantPointAuditModel`，狀態 `Pending`。
4. 呼叫 `TenantAipoolDeductClient.increasePoint(...)`。
5. 根據 AIPool 響應更新 `Success` / `Failed`，通訊異常保留 `Processing`。
6. 返回可追蹤結果：`eventId`、`status`、`outTraceId`、失敗原因。

### 6.2 統一給點命令欄位

| 欄位 | 說明 |
| --- | --- |
| `eventId` | 全域性冪等鍵，規則 `GRANT_{Scene}_{businessId}`。管理員給點 `businessId=ADM_TASK_{taskId}`；活動給點 `businessId={campaignId}_{participantId}`；購買到賬 `businessId={orderId}`。 |
| `tenantId` | 目標租戶 ID。**管理員給點/使用者自購**：目標個人租戶 ID。**平臺級活動**：`SYSTEM_GLOBAL_TENANT`。常量統一管理，不硬編碼散落。 |
| `accountId` | 可選，Aile 賬號 ID，用於排障和前端展示。 |
| `openId` | 必填，AIPool 累點目標。 |
| `amount` | 必填，正整數。審計 `ailePointsChange` 為正數。 |
| `auditScene` | `AdminGrant`、`ActivityGrant`、`UserPurchase`、`OrderCompensation` 等。 |
| `sourceBizId` | 來源業務主鍵（管理員任務 ID、活動參與記錄 ID、訂單 ID）。 |
| `operatorId` | 管理員給點必填；系統/活動給點使用系統操作者。 |
| `reasonCode` | 給點原因列舉（見 6.7）。 |
| `remark` | 人可讀說明。原因為“其他”時必填。 |

### 6.3 狀態機（統一口徑）

```mermaid
flowchart TD
    Start["開始"] --> Pending
    Pending --> Processing
    Processing --> Success
    Processing --> Failed
    Processing -->|通訊異常保留, 補單Job重試| Processing
    Failed -->|手動重試/補發| Processing
    Success --> End["結束"]
```

點數審計狀態列舉統一為：`Pending`、`Processing`、`Success`、`Failed`（PascalCase）。

冪等要求：

- `eventId` 必須唯一（Mongo 唯一約束）。
- 收到相同 `eventId`：
    - 已 `Success`：直接返回既有成功結果，不再呼叫 AIPool。
    - `Processing`：返回處理中，避免併發重複呼叫。
    - `Failed`：僅允許透過明確“重試/補發”動作再次推進。
- 索引約束：現有 `@Indexed(unique = true)` 與倉庫 AGENTS 的組合索引規範存在風格差異，正式開發時評估是否遷移為 `@CompoundIndex`。

### 6.4 MongoDB `tenant_point_audit` 推薦結構

```json
{
  "bsonType": "object",
  "required": ["eventId", "tenantId", "openId", "auditScene", "sourceBizId", "ailePointsChange", "status", "occurredAt"],
  "properties": {
    "id": { "bsonType": "objectId" },
    "eventId": { "bsonType": "string", "description": "全域性唯一業務冪等鍵, 規則 GRANT_{Scene}_{businessId}; 唯一索引保證資料庫層防刷" },
    "tenantId": { "bsonType": "string", "description": "管理員給點/自購=目標個人租戶; 平臺級活動=SYSTEM_GLOBAL_TENANT" },
    "openId": { "bsonType": "string" },
    "auditScene": { "bsonType": "string", "enum": ["QuotaDeduct", "OrderPaid", "OrderCompensation", "ActivityGrant", "AdminGrant", "UserPurchase"] },
    "sourceBizId": { "bsonType": "string", "description": "關聯業務主鍵: 訂單ID/客訴單ID/被邀請使用者ID/加祕書事件ID" },
    "ailePointsChange": { "bsonType": "int", "description": "正數為給點, 負數為扣點" },
    "status": { "bsonType": "string", "enum": ["Pending", "Processing", "Success", "Failed"] },
    "occurredAt": { "bsonType": "long", "description": "毫秒時間戳" },
    "operatorId": { "bsonType": "string", "description": "管理員給點操作者" },
    "reasonCode": { "bsonType": "string" },
    "aipoolResponseCode": { "bsonType": "string" },
    "aipoolResponseMessage": { "bsonType": "string" },
    "outTraceId": { "bsonType": "string", "description": "AIPool 交易流水號, 跨系統對賬" },
    "remark": { "bsonType": "string" }
  }
}
```

### 6.5 核心給點服務實作（修訂版）

<aside>
📝

相對統整方案原始碼做了兩處修訂：①通訊異常分支不再立即 `markFailed`，改為保留 `Processing` 交由補單 Job/手動重試（與第 5 節架構、6.3 狀態機一致）；②`expireTime`/`pointType` 不硬編碼，改為讀取配置（見衝突 C6、C12）。

</aside>

```java
@Slf4j
@Service
public class TenantPointGrantServiceImpl implements TenantPointGrantService {

    @Resource private TenantPointAuditService tenantPointAuditService;
    @Resource private TenantAipoolDeductClient tenantAipoolDeductClient;
    @Resource private AilePointGrantProperties grantProperties; // 有效期/pointType 配置化

    public PointGrantResult grantPoints(PointGrantCommand cmd) {
        // 1. 生成 eventId
        String eventId = "GRANT_" + cmd.getScene().name() + "_" + cmd.getBusinessId();

        // 2. 幂等检索
        TenantPointAuditModel existing = tenantPointAuditService.findAuditByEventId(eventId);
        if (existing != null) {
            return PointGrantResult.from(existing); // Success 直接返回; Processing/Failed 按 6.3 处理
        }

        // 3. 写入 Pending
        TenantPointAuditModel audit = TenantPointAuditModel.superBuilder()
                .eventId(eventId)
                .tenantId(cmd.getTenantId()) // 管理员给点=目标个人租户; 平台级活动=SYSTEM_GLOBAL_TENANT
                .openId(cmd.getOpenId())
                .auditScene(cmd.getScene())
                .sourceBizId(cmd.getBusinessId())
                .operatorId(cmd.getOperatorId())
                .reasonCode(cmd.getReasonCode())
                .ailePointsChange(cmd.getAmount())
                .status(TenantPointAuditStatus.Pending)
                .occurredAt(System.currentTimeMillis())
                .remark(cmd.getRemark())
                .build();
        tenantPointAuditService.insert(audit);

        // 4. 组装请求 (有效期/pointType 配置化)
        TenantAipoolPointIncreaseRequest request = TenantAipoolPointIncreaseRequest.builder()
                .openId(cmd.getOpenId())
                .point(cmd.getAmount())
                .code(eventId) // AIPool 侧强幂等键
                .reason(cmd.getRemark())
                .fromSystem(cmd.getScene().getFromSystem()) // AILE_ADMIN_GRANT / AILE_USER_PURCHASE / AILE_ACTIVITY_GRANT
                .fromInfo(cmd.getScene().name() + ":" + cmd.getBusinessId())
                .startTime(System.currentTimeMillis())
                .expireTime(grantProperties.resolveExpireTime(cmd.getScene())) // 不硬编码, 按场景配置
                .pointType(grantProperties.resolvePointType(cmd.getScene()))
                .build();

        try {
            tenantPointAuditService.markProcessing(eventId, cmd.getAmount());
            TenantAipoolDeductResult result = tenantAipoolDeductClient.increasePoint(request);
            if (result.isSuccess()) {
                tenantPointAuditService.markGrantSuccess(eventId, cmd.getAmount(),
                        result.getResponseCode(), result.getResponseMessage(), result.getOutTraceId());
                return PointGrantResult.success(eventId, result.getOutTraceId());
            } else {
                // 下游业务拒绝(余额超限/黑名单等): 明确失败
                tenantPointAuditService.markFailed(eventId, cmd.getAmount(), result.getResponseMessage());
                return PointGrantResult.failed(eventId, result.getResponseMessage());
            }
        } catch (Exception ex) {
            // 通信超时/异常: 保留 Processing, 交由补单 Job 或手动重试, 不静默失败
            log.error("[PointGrant] Communication error. eventId={}", eventId, ex);
            return PointGrantResult.processing(eventId, "S2S Communication Error: " + ex.getMessage());
        }
    }
}
```

### 6.6 AIPool 累點呼叫產品化

複用 `TenantAipoolDeductClient.increasePoint(...)`，產品化補齊：

- `fromSystem`：區分 `AILE_ADMIN_GRANT`、`AILE_USER_PURCHASE`、`AILE_ACTIVITY_GRANT`。
- `fromInfo`：統一 `{scene}:{sourceBizId}`。
- `code`：使用 `eventId`，與 AIPool 冪等鍵對齊。
- `reason`：原因列舉 + 備註組合。
- `startTime` / `expireTime`：**配置化**，預設 365 天（1 年），可按場景（活動/管理員/購買）分別覆蓋，不硬編碼（見決議 Q2）。
- `pointType`：固定為發行租戶**封閉點**，首期不支援其他點種（見決議 Q3）。
- `realIp`：遵循現有配置兜底與校驗機制。
- S2S 安全：`TenantAipoolPointRequestInterceptor` 自動注入 `aile_api_key`、`aile_api_secret`、時間戳與 SHA-256 簽名，經 API Gateway 校驗。AIPool 端以 `code` 為分散式防重鍵加 Redis 鎖，重複呼叫直接返回原成功流水。

### 6.7 管理員給點服務

業務規則：

- 操作者必須是 Aile 官方管理員（角色編碼見 Q5）。
- 目標物件僅限個人租戶；團隊租戶不可作為 Phase 1 給點目標。
- `tenantId` 寫入**目標個人租戶 ID**（非 `SYSTEM_GLOBAL_TENANT`），保留 `operatorId` 為管理員標識。
- Phase 1 不設審批流程、不設 Aile 側給點額度上限；餘額是否不足由 AIPool 返回，Aile 記錄失敗原因。
- 給點原因列舉：客訴補償、服務關懷、使用者挽留、系統故障補償、運營激勵、其他；“其他”必須填寫備註。
- 防重複提交：前端獲取隨機 `taskId`，後端以 `ADM_TASK_{taskId}` 作為 `businessId`，依賴 Mongo `eventId` 唯一索引攔截重複點選。

介面（修訂示例，`tenantId` 取目標租戶）：

```java
@PostMapping("/admin/v1/users/{openId}/points/grant")
@PreAuthorize("hasRole('AILE_OFFICIAL_ADMIN')") // 角色编码待最终确认(Q5)
public ResponseEntity<AdminGrantResponse> manualGrantPoints(
        @PathVariable String openId,
        @Validated @RequestBody AdminGrantRequest body) {
    PointGrantCommand cmd = PointGrantCommand.builder()
            .tenantId(body.getTargetTenantId()) // 目标个人租户, 非 SYSTEM_GLOBAL_TENANT
            .openId(openId)
            .amount(body.getAmount())
            .scene(TenantPointAuditScene.AdminGrant)
            .businessId("ADM_TASK_" + body.getTaskId())
            .operatorId(getLoginAdminId())
            .reasonCode(body.getReasonCode())
            .remark("管理员[" + getLoginAdminEmail() + "]手动指派: " + body.getReason())
            .build();
    PointGrantResult r = tenantPointGrantService.grantPoints(cmd);
    return ResponseEntity.ok(AdminGrantResponse.from(r));
}
```

介面能力：查詢租戶列表（型別/關鍵字/狀態篩選）、發起給點、按 `eventId`/審計 ID 查詢結果、審計列表（複用/擴充套件 `/point/audit/v1/list`，增加時間範圍、操作人、來源業務 ID、正負點數篩選）。

### 6.8 使用者自購 Aile 點

業務定位：Aile 不替代 AIPool 支付能力，只提供內嵌購買入口與代理/回撥對接。套餐、價格、贈送比例由 **Aile Admin 配置維護**（非 AIPool 動態拉取，見決議 Q4）；賬務真相（餘額、交易流水）仍以 AIPool 為準，Aile 不維護獨立賬務。

Backend 支援：拉取可購買套餐/檔位；建立購買訂單或獲取 AIPool 支付引數；接收/同步支付結果；支付成功寫入 `TenantPointAudit`（`auditScene=UserPurchase`，以訂單 ID 冪等）；Admin/App 查詢餘額與購買記錄。

已決議（見第 13 節）：套餐由 Aile Admin 配置管理（Q4）；首期支付支援 **UUPon + 現金**（混合支付，Q9）；到賬以 **AIPool 回撥為主、Aile 輪詢兜底對賬**（Q5）。

### 6.9 活動給點服務（Phase 3）

邊界：活動引擎負責規則判斷、預算扣減、防刷與參與記錄；統一給點服務只接收“已判定透過”的給點事件；預算必須發點前原子扣減；活動參與記錄與 `TenantPointAudit` 透過 `sourceBizId`/`eventId` 關聯。

活動狀態機（統一口徑，見衝突 C3）：`DRAFT` → `ACTIVE` → `PAUSED` / `EXHAUSTED` / `ENDED` → `ARCHIVED`。

<aside>
🔧

**狀態命名統一**：採用大寫列舉，使用 `ENDED` 表示自然結束（替代統整方案的 `COMPLETED`），並保留開發規格中的 `ARCHIVED` 歸檔態。完整集合：`DRAFT / ACTIVE / PAUSED / EXHAUSTED / ENDED / ARCHIVED`。

</aside>

活動 Schema（`aile.campaign`）：

```json
{
  "title": "Campaign",
  "type": "object",
  "properties": {
    "campaignId": { "type": "string", "description": "CAMP_{SCOPE}_{YYYYMMDD}_{NAME}" },
    "title": { "type": "string" },
    "campaignScope": { "type": "string", "enum": ["PLATFORM", "TENANT"] },
    "tenantId": { "type": "string", "description": "PLATFORM 时写入 SYSTEM_GLOBAL_TENANT" },
    "status": { "type": "string", "enum": ["DRAFT", "ACTIVE", "PAUSED", "EXHAUSTED", "ENDED", "ARCHIVED"] },
    "startTime": { "type": "integer" },
    "endTime": { "type": "integer" },
    "totalBudget": { "type": "integer" },
    "distributedBudget": { "type": "integer", "default": 0 },
    "singleGrantAmount": { "type": "integer" },
    "rules": { "type": "array", "items": { "type": "object", "properties": {
      "ruleType": { "type": "string", "enum": ["EventTypeCondition", "UserEligibility", "FrequencyLimit"] },
      "operator": { "type": "string", "enum": ["EQUALS", "IN_LIST", "LESS_THAN_OR_EQUAL"] },
      "value": { "type": "string" }
    }}}
  }
}
```

高併發預算控制（Redis Lua 原子扣減）：

```lua
-- KEYS[1]: campaign:CAMP_ID:budget
-- ARGV[1]: single grant amount
local key = KEYS[1]
local decrement = tonumber(ARGV[1])
local current = tonumber(redis.call('get', key) or "0")
if current >= decrement then
    redis.call('decrby', key, decrement)
    return 1
else
    return 0
end
```

返回 1 安全獲取併發布給點事件；返回 0 由活動服務釋出 alert 並非同步更新狀態為 `EXHAUSTED`，杜絕超發。給點失敗需進入補償/重試佇列，不靜默吞預算。

## 7. Admin 介面規格

### 7.1 官方管理員給點

入口：Aile 官方租戶 Admin 後臺租戶列表。能力：

- 租戶列表支援搜尋、租戶型別篩選、狀態篩選；僅個人租戶顯示“給點”。
- 給點彈窗：目標租戶資訊、點數輸入、原因下拉、備註、二次確認。
- 提交後展示成功/失敗/處理中；失敗顯示 `eventId`、AIPool 響應碼/資訊、失敗原因。
- 約束：點數為正整數；“其他”原因備註必填；提交按鈕 loading；前端禁用 + 後端冪等雙保護。

### 7.2 點數審計與報表

- 列表：按租戶、時間、場景、狀態、操作人、來源業務 ID 查詢；展示點數變化、場景、原因、狀態、AIPool trace、建立/更新時間；正數收入、負數支出明確區分。
- 統計：本日/本月給點總額、給點次數、按原因分佈、按場景分佈（管理員給點/購買充值/活動給點/扣點/補償）。

### 7.3 個人租戶內嵌購買

入口：個人租戶 Admin“我的配額/點數”區域。展示餘額、“立即購買”入口、檔位/價格/贈送/支付方式彈窗、支付狀態、成功後重新整理餘額、收支記錄出現“購買充值”。

### 7.4 活動管理臺（Phase 3）

- **活動配置與規則拼裝器**：基礎引數（標題、時間段、Scope、單次給點數量、總預算）+ 規則拼裝（事件型別、頻次限制如 `LIFETIME`、受眾過濾）。
- **運營指標與監控看板**：預算消耗比、發放即時速率、使用者漏斗（接收→判定透過→成功發放）。
- **參與記錄審計與一鍵補發**：列出 `campaign_record`，對 `Failed` 記錄一鍵 `Re-run`，底層 Unique Index + `code` 雙重冪等保證不重發。
- **追加預算（執行緒安全）**：對 `ACTIVE`/`EXHAUSTED` 活動追加預算，差額同步 `INCRBY` 至 Redis。

核心 API（節選）：

```yaml
paths:
  /admin/v1/campaigns:
    post: { summary: 创建行销活动(PLATFORM/TENANT) }
  /admin/v1/campaigns/{id}/budget:
    put: { summary: 追加活动预算(Thread-Safe, 同步 INCRBY Redis) }
  /admin/v1/campaigns/re-run:
    post: { summary: 一键补发(携带原 eventId, 强幂等) }
```

## 8. AileApp 規格

Phase 1 不強制開發。

- **Phase 2**：點數中心入口、Aile 點餘額、購買入口與超額購買引導、收支明細頁、購買/給點到賬通知。
- **Phase 3**：活動中心列表、活動詳情與規則說明、參與/獎勵領取狀態、獎勵到賬通知。

## 9. 非功能性要求

- 冪等：所有給點入口以 `eventId` 防重複（Aile + AIPool 雙重）。
- 審計：所有 Success/Failed/Processing 狀態必須落 `TenantPointAudit`。
- 安全：Admin 給點僅限 Aile 官方管理員；使用者自購僅允許當前個人租戶發起；S2S 簽名校驗。
- 可觀測：關鍵流程記錄 `eventId`、`tenantId`、`openId`、`sourceBizId`、`outTraceId`。
- 可回放：失敗記錄支援人工排查與明確重試策略。
- 一致性：AIPool 為賬務真相，Aile 側餘額展示以 AIPool 或可信同步結果為準。
- 相容：不破壞現有扣點鏈路與配額審計查詢口徑。

## 10. 驗收標準（Phase 1）

1. Backend 透過統一給點服務完成一次管理員給點，並寫入完整審計記錄（`tenantId` 為目標個人租戶）。
2. 同一 `eventId` 重複請求不重複呼叫 AIPool 或重複給點。
3. AIPool 成功返回時審計 `Success` 並記錄 `outTraceId`。
4. AIPool 業務拒絕時審計 `Failed` 並記錄失敗原因；通訊異常保留 `Processing` 可補單。
5. Admin 能在租戶列表篩選個人租戶併發起給點。
6. Admin 能查詢給點/扣點審計明細與基礎統計。
7. 個人租戶 Admin 能看到購買入口並完成 AIPool 購買/約定支付流程。
8. 購買成功後收支明細出現購買充值記錄。
9. 許可權不滿足者不能執行官方管理員給點。
10. 現有扣點、配額使用、欠費處理鏈路不受影響。

## 11. 設計原則應用

- **KISS**：Phase 1 只做給點、自購、基礎審計閉環，不引入審批流和複雜活動規則。
- **YAGNI**：AilePro 快捷給點、角色額度分級、完整活動中心延後。
- **DRY**：複用 `TenantPointAuditModel`、`TenantAipoolDeductClient`、quota 模組；給點編排收斂 `TenantPointGrantService`。
- **SOLID**：給點執行、審計查詢、AIPool 呼叫、活動規則判斷職責分離。

## 12. 三份文件衝突分析與修訂結論

<aside>
🔍

以下為三份原始文件之間發現的衝突/不一致，正文已按“修訂結論”統一。

</aside>

| 編號 | 衝突點 | 各文件表述 | 修訂結論 |
| --- | --- | --- | --- |
| C1 | 給點服務歸屬/命名 | 統整方案架構圖把給點入口標為 `TenantPointAuditServiceImpl`，但程式碼用 `TenantPointGrantServiceImpl`；開發規格明確要求新建 `TenantPointGrantService`，不堆入審計服務 | 統一：新建 `TenantPointGrantService(Impl)` 負責給點編排，審計讀寫複用 `TenantPointAuditService`。架構圖已更正。 |
| C2 | 通訊異常時的狀態處理 | 統整方案程式碼異常分支直接 `markFailed`，但其註釋寫“保留 Pending/Processing 以便補單重試”——自相矛盾 | 統一：業務拒絕→`Failed`；通訊超時/異常→保留 `Processing`，交補單 Job/手動重試。程式碼已修訂。 |
| C3 | 活動狀態列舉 | 開發規格：Draft/Active/Paused/Exhausted/Ended/Archived（6 態，首字母大寫）；統整方案：DRAFT/ACTIVE/PAUSED/EXHAUSTED/COMPLETED（5 態，無 Archived，用 COMPLETED）；使用者故事：建立/編輯/啟用/暫停/結束 | 統一：大寫列舉 `DRAFT/ACTIVE/PAUSED/EXHAUSTED/ENDED/ARCHIVED`；自然結束用 `ENDED`，保留 `ARCHIVED`。 |
| C4 | 點數審計狀態大小寫 | 兩份均用 Pending/Processing/Success/Failed（PascalCase），但活動態混用大寫——風格不一致 | 統一：點數審計態 PascalCase；活動態全大寫。兩套互不混用。 |
| C5 | auditScene 列舉集合 | 統整方案含 QuotaDeduct/OrderPaid/OrderCompensation/ActivityGrant/AdminGrant/UserPurchase；開發規格僅列舉部分 | 統一採用統整方案完整集合（見 6.4）。 |
| C6 | 點有效期 expireTime | 統整方案程式碼硬編碼 1 年；開發規格明確“不應硬編碼，需產品確認”，並列為待確認問題 | 統一：`expireTime` 配置化，按場景分別配置，預設值待產品確認（Q2）。 |
| C7 | pointType | 統整方案硬編碼 `pointType=1`(封閉點)；開發規格未提及 | 統一：`pointType` 配置化，預設值需產品確認（Q3）。 |
| C8 | 管理員給點的 tenantId | 使用者故事/開發規格：給點目標=個人租戶，`tenantId`=目標個人租戶 ID；統整方案管理員控制器傳 `SYSTEM_GLOBAL_TENANT` | **重要修訂**：管理員給點 `tenantId` 寫目標個人租戶 ID（用 `operatorId` 記錄管理員）；`SYSTEM_GLOBAL_TENANT` 僅用於平臺級活動。 |
| C9 | 許可權角色編碼 | 統整方案硬編碼 `hasRole('ADMIN')`；開發規格列為待確認 | 統一：佔位 `AILE_OFFICIAL_ADMIN`，最終編碼待確認（Q5）。 |
| C10 | 活動引擎服務歸屬 | 統整方案：獨立 `aipool-activity-service`；開發規格：待確認（springcloud-aile vs 獨立服務） | 已決議（Q8）：活動引擎獨立為 `aipool-activity-service`。 |
| C11 | 階段定位差異 | 統整方案完整呈現活動引擎/管理臺（易被讀作 Phase 1）；使用者故事/開發規格將活動給點列為 Phase 3 | 統一按 Phase 1/2/3 劃分（見第 3 節）；活動給點屬 Phase 3。 |
| C12 | eventId 活動場景格式 | 開發規格：`GRANT_ActivityGrant_{campaignId}_{participantId}`；統整方案：`GRANT_{Scene}_{businessId}`（單一 businessId） | 統一：`businessId={campaignId}_{participantId}`，套入通用規則 `GRANT_{Scene}_{businessId}`，兩者等價。 |

## 13. 關鍵決議與外部依賴

<aside>
✅

以下為依據使用者回填答覆（見 13.1）形成的設計決議。標記 ✅ 的已在正文落地；標記 ⏳ 的為外部依賴（需與 AIPool 協商確認），介面實現預留適配層。

</aside>

| 編號 | 問題 | 回填答覆 | 設計落地 / 狀態 |
| --- | --- | --- | --- |
| 1 | AIPool 累點介面契約/成功碼/冪等碼/錯誤碼 | 需與 AIPool 確認 | ⏳ 外部依賴：待 AIPool 提供正式欄位契約與狀態碼，封裝層預留適配與錯誤碼對映 |
| 2 | Aile 點預設有效期 | 可配置，預設 1 年 | ✅ `expireTime` 配置化，預設 365 天，可按活動/管理員/購買分別覆蓋 |
| 3 | `pointType` 取值 | 只有封閉點 | ✅ 固定為發行租戶**封閉點**，首期不支援其他點種 |
| 4 | 使用者自購套餐來源 | Aile Admin 配置管理 | ✅ 套餐/檔位/價格在 Aile Admin 配置維護，App/Admin 讀 Aile 配置展示（非 AIPool 動態拉取） |
| 5 | 購買到賬觸發方式 | AIPool 回撥 + Aile 輪詢 | ✅ 主：AIPool 支付成功回撥寫審計；輔：Aile 定時輪詢兜底對賬 |
| 6 | 官方管理員角色編碼 | Aile 官方管理員 | ✅ 角色＝Aile 官方管理員；佔位編碼 `AILE_OFFICIAL_ADMIN`，最終編碼值隨 Admin 許可權模型確定 |
| 7 | 個人租戶與 `openId` 對映 | 需與 AIPool 協商 | ⏳ 外部依賴：與 AIPool 確認個人租戶需提供哪些資訊，以唯一定位 AIPool 使用者並完成給點 |
| 8 | 活動給點服務歸屬 | 建議 aipool-activity-service | ✅ 已決議：活動引擎獨立為 `aipool-activity-service` |
| 9 | 支付方式 | 支援 UUPon + 現金 | ✅ 首期支援 UUPon + 現金（混合支付） |

### 13.1 原始待確認問題與回填答覆

1. AIPool 累點介面的正式欄位契約、成功碼、冪等碼與錯誤碼是否已最終確認。
    1. 需要和AIPool確認
2. （Q2）Aile 點預設有效期如何確定，是否按活動/管理員/購買三類分別配置。
    1. 暫定可以配置。 預設1年
3. （新增）`pointType` 預設值與各場景取值。
    1. 只有封閉點
4. 使用者自購套餐來源：AIPool 動態拉取還是 Aile Admin 配置展示。
    1. AileAdmin中配置管理
5. 購買支付成功後的到賬觸發方式：AIPool 回撥、Aile 輪詢，還是現有訂單同步機制。
    1. AIPool回撥+Aile輪詢
6. （Q5）Aile 官方管理員許可權在現有 Admin 許可權模型中的角色編碼。
    1. Aile 官方管理員
7. 個人租戶與 `openId` 的穩定對映來源，以及異常缺失時的處理方式。
    1. 需要和AIPool協商， 對於個人租戶， 要提供哪些資訊， 可以確認再AIPool中的一個人，並可以實現給點？ 
8. （Q7）活動給點服務歸屬：`springcloud-aile` 還是獨立 `aipool-activity-service`。
    1. 建議aipool-activity-service 
9. 支付方式是否首期必須支援 UUPon + 現金混合支付。
    1. 支援UUPON+現金
