# 乐读派 AI 绘本创作系统 — 企业对接 Demo (Java Spring Boot)

> 配合《AI绘本创作系统_企业后端集成指南_V4.0.pdf》使用。
> 本 Demo 实现了企业对接所需的**全部功能**，替换4个配置后可直接运行。

## 两种集成方式

乐读派提供两种集成方式，企业可根据自身需求选择：

| | 方式一：dist包交付（iframe嵌入） | 方式二：源码交付（企业自集成） |
|---|---|---|
| **交付物** | dist/ 编译产物（不含源码） | src/ 源码（23文件/5000行） |
| **企业能否改UI** | 不能（只改config.js品牌） | 可以改任何页面 |
| **企业前端改动** | ~50行JS（iframe+postMessage） | ~20行（路由注册+config） |
| **无iframe体验** | 否（iframe有视觉边界） | 是（创作页面是企业应用的一部分） |
| **我方更新** | 给新dist包，替换部署 | 给源码更新，企业合并代码 |
| **对接周期** | 7-10天 | 4-7天（需Vue开发能力） |
| **推荐场景** | 快速上线/前端能力弱 | UI定制/追求无缝体验 |

### 方式一用法（iframe嵌入）

按本文档后续章节操作：部署dist包 → 改config.js → 实现后端接口 → iframe嵌入 + postMessage通信。

### 方式二用法（源码集成，仅3步）

```bash
# Step 1: 复制源码到企业项目
cp -r lesingle-aicreate-client/src/ your-project/src/modules/lesingle/

# Step 2: 安装额外依赖（vue/axios/vue-router企业项目已有则跳过）
npm install ali-oss @stomp/stompjs crypto-js

# Step 3: 注册路由（在企业router中，约10行）
```

```javascript
import { routes } from '@/modules/lesingle/router'
routes.forEach(r => router.addRoute(r))

// 入口跳转（企业页面中）
router.push('/?token=sess_xxx&orgId=ORG001&phone=138xxx')
```

> **方式二不需要改任何创作页面代码**。企业只需改config.js（服务地址）和注册路由。
> 所有创作交互（上传→识别→画风→故事→创作→编目→配音→阅读）原封不动使用。
> 无第三方UI库依赖，不会与企业现有UI框架冲突。

---

## 架构说明

```
企业系统（本Demo, 端口9090）
├── 企业前端 (/enterprise-sim.html)
│   ├── [广场] [创作] [作品] [我的] 四模块
│   ├── 创作模块: iframe嵌入乐读派H5 (3001端口)
│   └── 作品模块: 调B3查作品列表，点击跳转编目/配音
├── 企业后端
│   ├── /leai-auth        — 统一认证入口（首次+token失效回调）
│   ├── /api/refresh-token — iframe内token刷新
│   ├── /webhook/leai      — 接收Webhook回调（状态变更推送）
│   └── B3定时对账          — 每30分钟兜底同步（补偿Webhook遗漏）
└── 企业数据库
    └── TODO: 替换Demo中的内存Map为你的MySQL/PostgreSQL
```

## 企业需要开发的接口

| 接口 | 方法 | 功能 | 必要性 |
|------|------|------|--------|
| `/leai-auth` | GET | 认证入口（换token+重定向H5） | **必须** |
| `/api/refresh-token` | GET | iframe内token刷新（返回JSON） | **必须**（iframe模式） |
| `/webhook/leai` | POST | Webhook接收+签名验证+数据同步 | **强烈推荐** |
| B3定时对账 | 定时 | 兜底同步（补偿Webhook遗漏） | **强烈推荐** |

## 快速运行（3步）

### Step 1: 修改4个配置

打开 `src/main/java/com/example/leaidemo/LeaiDemoApplication.java`，修改顶部常量：

```java
private static final String ORG_ID     = "你的机构ID";          // 管理后台 → 机构管理 → 机构ID
private static final String APP_SECRET = "你的机构密钥";        // 管理后台 → 机构管理 → 机构密钥
private static final String LEAI_API_URL = "https://你的API域名"; // 乐读派后端API地址
private static final String LEAI_H5_URL  = "https://你的H5域名"; // 乐读派H5前端地址
```

### Step 2: 运行

```bash
# Windows
start.bat

# 或手动
mvnw.cmd clean package -DskipTests
java -jar target/leai-enterprise-demo-1.0.0.jar
```

### Step 3: 管理后台配置

| 配置项 | 位置 | 填写内容 |
|-------|------|---------|
| Webhook URL | 机构管理 → 回调配置 | `https://你的域名/webhook/leai` |
| 认证回调URL | 机构管理 → 认证回调URL | `https://你的域名/leai-auth` |
| 事件订阅 | 机构管理 → 回调配置 → 事件订阅 | 全部勾选 |

## iframe 嵌入模式

企业C端通过 iframe 嵌入乐读派H5创作页面。

### iframe 加载方式

```
新建创作:
  iframe.src = "https://H5域名/?token=xxx&orgId=xxx&phone=xxx&embed=1"

续编目(status=3):
  iframe.src = "https://H5域名/edit-info/{workId}?token=xxx&orgId=xxx&phone=xxx&embed=1"

续配音(status=4):
  iframe.src = "https://H5域名/dubbing/{workId}?token=xxx&orgId=xxx&phone=xxx&embed=1"

查看已完成(status=5):
  iframe.src = "https://H5域名/read/{workId}?token=xxx&orgId=xxx&phone=xxx&embed=1&from=works"
```

### postMessage 消息协议

**H5 → 企业（需要监听）**:

| type | 说明 | payload |
|------|------|---------|
| READY | H5加载完毕 | {} |
| TOKEN_EXPIRED | token过期，请刷新 | { messageId } |
| WORK_CREATED | 创作已提交 | { workId } |
| WORK_COMPLETED | 创作完成 | { workId } |
| CREATION_ERROR | 创作失败 | { message } |
| NAVIGATE_BACK | 请求返回作品列表 | {} |

**企业 → H5（需要发送）**:

| type | 说明 | payload |
|------|------|---------|
| TOKEN_REFRESHED | 响应TOKEN_EXPIRED | { messageId, token, orgId, phone } |

### 企业前端核心JS（~30行）

```javascript
window.addEventListener('message', function(event) {
  var msg = event.data;
  if (!msg || msg.source !== 'leai-creation') return;
  
  if (msg.type === 'TOKEN_EXPIRED') {
    fetch('/api/refresh-token?phone=' + phone)
      .then(r => r.json())
      .then(data => {
        iframe.contentWindow.postMessage({
          source: 'leai-creation', version: 1,
          type: 'TOKEN_REFRESHED',
          payload: { messageId: msg.payload.messageId, token: data.token, orgId: orgId }
        }, '*');
      });
  }
  
  if (msg.type === 'WORK_COMPLETED') {
    refreshWorkList(); // 刷新企业作品列表
  }
  
  if (msg.type === 'NAVIGATE_BACK') {
    switchToWorksTab(); // 切回作品列表
  }
});
```

## H5 config.js 配置

H5的 `dist/config.js` 需要设置：

```javascript
authMode: "token",
prod: {
  apiBaseUrl: "https://你的API域名",
  wsBaseUrl:  "wss://你的API域名"
},
embedMode: "iframe",
parentOrigins: ["https://你的企业域名"]
```

## 数据同步说明

企业通过两种方式接收乐读派的作品数据：

### 方式1: Webhook 实时推送（主通道）
- 乐读派在作品状态变更时主动推送到企业的 `/webhook/leai`
- 需要验证 HMAC-SHA256 签名
- 按V4.0同步规则处理数据
- **Demo中的 syncWork() 使用内存Map存储，企业需替换为数据库操作（见代码中的TODO注释）**

### 方式2: B3 定时对账（兜底补偿）
- 每30分钟调用B3接口批量查询最近2小时的变更作品
- 补偿Webhook推送可能的遗漏
- **建议对账周期不低于30分钟，避免对乐读派API造成查询压力**
- Demo中已实现完整的B3+B2对账逻辑（见代码中的TODO注释）

### V4.0 同步规则（核心3行）

```java
if (remoteStatus == -1)         → 强制更新（FAILED）
if (remoteStatus == 2)          → 强制更新（PROCESSING进度变化）
if (remoteStatus > localStatus) → 全量更新（状态前进）
else                            → 忽略（旧数据/重复推送）
```

## 注意事项

1. **appSecret 绝不到达浏览器** — 仅在企业服务端使用
2. **H5 使用 history 路由** — iframe src URL格式正确带参数即可
3. **returnPath 必须原样传回** — `/leai-auth` 收到的 returnPath 不要修改
4. **Webhook 签名必须验证** — 生产环境不验证=安全漏洞
5. **status 是 INT 类型** — 取值 -1/1/2/3/4/5
6. **B3对账建议30分钟** — 过于频繁会增加API压力
7. **iframe需要allow属性** — `allow="camera;microphone"`（录音功能需要）
8. **JDK 1.8 兼容** — 代码无Java 9+语法
