| 1 | # 平台交互模块 (plats) |
| 2 | |
| 3 | 平台交互模块提供统一的接口来实现各平台的点赞、评论、收藏、首页列表获取等交互功能。 |
| 4 | |
| 5 | ## 目录结构 |
| 6 | |
| 7 | ``` |
| 8 | plats/ |
| 9 | ├── types.ts # 统一类型定义 |
| 10 | ├── manager.ts # 平台管理器 |
| 11 | ├── index.ts # 模块导出 |
| 12 | ├── README.md # 本文档 |
| 13 | ├── demo/ # 示例数据(用于开发参考) |
| 14 | ├── douyin/ # 抖音平台 |
| 15 | │ ├── index.ts # 主类和导出入口 |
| 16 | │ ├── types.ts # 抖音特定类型定义 |
| 17 | │ └── homeFeed.ts # 首页列表功能模块 |
| 18 | └── xhs/ # 小红书平台 |
| 19 | ├── index.ts # 主类和导出入口 |
| 20 | ├── types.ts # 小红书特定类型定义 |
| 21 | └── homeFeed.ts # 首页列表功能模块 |
| 22 | ``` |
| 23 | |
| 24 | ## 核心设计原则 |
| 25 | |
| 26 | 1. **统一接口**:所有平台实现 `IPlatformInteraction` 接口,对外隐藏实现细节 |
| 27 | 2. **模块化设计**:每个平台的功能拆分为独立模块,便于维护和扩展 |
| 28 | 3. **策略模式**:每个平台可自由选择 API 或自动化方案 |
| 29 | 4. **幂等操作**:操作前检测当前状态,避免重复操作 |
| 30 | 5. **易于扩展**:添加新平台或新功能只需实现对应模块 |
| 31 | |
| 32 | ## 接口定义 |
| 33 | |
| 34 | ```typescript |
| 35 | interface IPlatformInteraction { |
| 36 | readonly platformType: PlatType |
| 37 | |
| 38 | // 基础交互 |
| 39 | likeWork(workId: string, isLike: boolean): Promise<LikeResult> |
| 40 | commentWork(params: CommentParams): Promise<CommentResult> |
| 41 | favoriteWork(workId: string, isFavorite: boolean): Promise<FavoriteResult> |
| 42 | |
| 43 | // 可选功能 |
| 44 | sendDirectMessage?(params: DirectMessageParams): Promise<DirectMessageResult> |
| 45 | |
| 46 | // 列表获取 |
| 47 | getHomeFeedList(params: HomeFeedListParams): Promise<HomeFeedListResult> |
| 48 | } |
| 49 | ``` |
| 50 | |
| 51 | ## 平台功能支持 |
| 52 | |
| 53 | | 平台 | 点赞 | 评论 | 收藏 | 私信 | 首页列表 | |
| 54 | | ------ | ------ | ------ | ------ | ------ | -------- | |
| 55 | | 小红书 | API | API | API | - | API | |
| 56 | | 抖音 | 自动化 | 自动化 | 自动化 | 自动化 | API | |
| 57 | |
| 58 | ### 策略选择依据 |
| 59 | |
| 60 | - **API 方案**:性能好、速度快,但可能受到风控限制 |
| 61 | - **自动化方案**:模拟真实用户操作,风控较低,但速度稍慢 |
| 62 | |
| 63 | ## 使用方式 |
| 64 | |
| 65 | ### 方式一:通过管理器调用(推荐) |
| 66 | |
| 67 | ```typescript |
| 68 | import { platformManager } from '@/store/plugin/plats' |
| 69 | import { PlatType } from '@/app/config/platConfig' |
| 70 | |
| 71 | // 点赞 |
| 72 | await platformManager.likeWork(PlatType.Douyin, workId, true) |
| 73 | |
| 74 | // 评论 |
| 75 | await platformManager.commentWork(PlatType.Xhs, { workId, content: '评论内容' }) |
| 76 | |
| 77 | // 收藏 |
| 78 | await platformManager.favoriteWork(PlatType.Douyin, workId, true) |
| 79 | |
| 80 | // 获取首页列表 |
| 81 | const result = await platformManager.getHomeFeedList(PlatType.Xhs, { page: 1, size: 20 }) |
| 82 | if (result.success) { |
| 83 | console.log('作品列表:', result.items) |
| 84 | } |
| 85 | ``` |
| 86 | |
| 87 | ### 方式二:直接使用平台实例 |
| 88 | |
| 89 | ```typescript |
| 90 | import { douyinInteraction, xhsInteraction } from '@/store/plugin/plats' |
| 91 | |
| 92 | await douyinInteraction.likeWork(workId, true) |
| 93 | await xhsInteraction.commentWork({ workId, content: '评论内容' }) |
| 94 | |
| 95 | // 获取首页列表 |
| 96 | const result = await xhsInteraction.getHomeFeedList({ page: 1, size: 20 }) |
| 97 | ``` |
| 98 | |
| 99 | ## 首页列表返回格式 |
| 100 | |
| 101 | ```typescript |
| 102 | interface HomeFeedItem { |
| 103 | workId: string // 作品ID |
| 104 | thumbnail: string // 缩略图URL |
| 105 | title: string // 作品标题 |
| 106 | authorAvatar: string // 作者头像 |
| 107 | authorName: string // 作者名称 |
| 108 | authorId: string // 作者ID |
| 109 | likeCount: string // 点赞数(可能含"万") |
| 110 | isVideo: boolean // 是否为视频 |
| 111 | videoDuration?: number // 视频时长(秒) |
| 112 | origin: any // 平台原始数据 |
| 113 | } |
| 114 | ``` |
| 115 | |
| 116 | ## 扩展新平台 |
| 117 | |
| 118 | ### 步骤 1:创建平台目录结构 |
| 119 | |
| 120 | ``` |
| 121 | plats/kuaishou/ |
| 122 | ├── index.ts # 主类和导出入口 |
| 123 | ├── types.ts # 快手特定类型定义 |
| 124 | └── homeFeed.ts # 首页列表功能模块 |
| 125 | ``` |
| 126 | |
| 127 | ### 步骤 2:定义平台特定类型 |
| 128 | |
| 129 | ```typescript |
| 130 | // plats/kuaishou/types.ts |
| 131 | |
| 132 | /** 快手首页列表响应类型 */ |
| 133 | export interface KuaishouHomeFeedResponse { |
| 134 | success: boolean |
| 135 | data?: { |
| 136 | items: KuaishouHomeFeedItem[] |
| 137 | cursor?: string |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /** 快手首页列表项 */ |
| 142 | export interface KuaishouHomeFeedItem { |
| 143 | id: string |
| 144 | // ... 其他字段 |
| 145 | } |
| 146 | ``` |
| 147 | |
| 148 | ### 步骤 3:实现功能模块 |
| 149 | |
| 150 | ```typescript |
| 151 | // plats/kuaishou/homeFeed.ts |
| 152 | import type { HomeFeedListParams, HomeFeedListResult } from '../types' |
| 153 | |
| 154 | // 游标管理器 |
| 155 | class HomeFeedCursorManager { |
| 156 | /* ... */ |
| 157 | } |
| 158 | export const homeFeedCursor = new HomeFeedCursorManager() |
| 159 | |
| 160 | // 数据转换 |
| 161 | export function transformToHomeFeedItem(item: KuaishouHomeFeedItem): HomeFeedItem { |
| 162 | return { |
| 163 | /* 转换逻辑 */ |
| 164 | } |
| 165 | } |
| 166 | |
| 167 | // 获取列表 |
| 168 | export async function getHomeFeedList(params: HomeFeedListParams): Promise<HomeFeedListResult> { |
| 169 | // 实现逻辑 |
| 170 | } |
| 171 | ``` |
| 172 | |
| 173 | ### 步骤 4:创建主类 |
| 174 | |
| 175 | ```typescript |
| 176 | // plats/kuaishou/index.ts |
| 177 | import { PlatType } from '@/app/config/platConfig' |
| 178 | import type { IPlatformInteraction, ... } from '../types' |
| 179 | import { getHomeFeedList, homeFeedCursor } from './homeFeed' |
| 180 | |
| 181 | class KuaishouPlatformInteraction implements IPlatformInteraction { |
| 182 | readonly platformType = PlatType.KWAI |
| 183 | |
| 184 | resetHomeFeedCursor(): void { |
| 185 | homeFeedCursor.reset() |
| 186 | } |
| 187 | |
| 188 | async likeWork(workId: string, isLike: boolean): Promise<LikeResult> { |
| 189 | // 实现点赞逻辑 |
| 190 | } |
| 191 | |
| 192 | async commentWork(params: CommentParams): Promise<CommentResult> { |
| 193 | // 实现评论逻辑 |
| 194 | } |
| 195 | |
| 196 | async favoriteWork(workId: string, isFavorite: boolean): Promise<FavoriteResult> { |
| 197 | // 实现收藏逻辑 |
| 198 | } |
| 199 | |
| 200 | async getHomeFeedList(params: HomeFeedListParams): Promise<HomeFeedListResult> { |
| 201 | return getHomeFeedList(params) |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | export const kuaishouInteraction = new KuaishouPlatformInteraction() |
| 206 | |
| 207 | // 导出类型 |
| 208 | export type { KuaishouHomeFeedItem, KuaishouHomeFeedResponse } from './types' |
| 209 | ``` |
| 210 | |
| 211 | ### 步骤 5:更新类型定义 |
| 212 | |
| 213 | ```typescript |
| 214 | // plats/types.ts |
| 215 | export type SupportedPlatformType = PlatType.Xhs | PlatType.Douyin | PlatType.KWAI |
| 216 | ``` |
| 217 | |
| 218 | ### 步骤 6:注册到管理器 |
| 219 | |
| 220 | ```typescript |
| 221 | // plats/manager.ts |
| 222 | import { kuaishouInteraction } from './kuaishou' |
| 223 | |
| 224 | constructor() { |
| 225 | this.register(xhsInteraction) |
| 226 | this.register(douyinInteraction) |
| 227 | this.register(kuaishouInteraction) // 新增 |
| 228 | } |
| 229 | ``` |
| 230 | |
| 231 | ### 步骤 7:导出 |
| 232 | |
| 233 | ```typescript |
| 234 | // plats/index.ts |
| 235 | export { kuaishouInteraction } from './kuaishou' |
| 236 | export type { KuaishouHomeFeedItem, KuaishouHomeFeedResponse } from './kuaishou' |
| 237 | ``` |
| 238 | |
| 239 | ## 添加新功能模块 |
| 240 | |
| 241 | 以添加「搜索」功能为例: |
| 242 | |
| 243 | ### 步骤 1:在 types.ts 添加统一类型 |
| 244 | |
| 245 | ```typescript |
| 246 | // plats/types.ts |
| 247 | export interface SearchParams { |
| 248 | keyword: string |
| 249 | page: number |
| 250 | size: number |
| 251 | } |
| 252 | |
| 253 | export interface SearchResult extends BaseResult { |
| 254 | items: HomeFeedItem[] |
| 255 | hasMore: boolean |
| 256 | } |
| 257 | ``` |
| 258 | |
| 259 | ### 步骤 2:更新接口定义 |
| 260 | |
| 261 | ```typescript |
| 262 | // plats/types.ts |
| 263 | export interface IPlatformInteraction { |
| 264 | // ... 现有方法 |
| 265 | search?(params: SearchParams): Promise<SearchResult> |
| 266 | } |
| 267 | ``` |
| 268 | |
| 269 | ### 步骤 3:在平台目录创建功能模块 |
| 270 | |
| 271 | ```typescript |
| 272 | // plats/xhs/search.ts |
| 273 | export async function search(params: SearchParams): Promise<SearchResult> { |
| 274 | // 实现搜索逻辑 |
| 275 | } |
| 276 | ``` |
| 277 | |
| 278 | ### 步骤 4:在主类中引入 |
| 279 | |
| 280 | ```typescript |
| 281 | // plats/xhs/index.ts |
| 282 | import { search } from './search' |
| 283 | |
| 284 | class XhsPlatformInteraction { |
| 285 | async search(params: SearchParams): Promise<SearchResult> { |
| 286 | return search(params) |
| 287 | } |
| 288 | } |
| 289 | ``` |
| 290 | |
| 291 | ## 插件端实现 |
| 292 | |
| 293 | 如果需要使用自动化方案,还需要在插件端实现对应的服务: |
| 294 | |
| 295 | 1. 创建 `XxxInteractionService.ts` |
| 296 | 2. 更新 `homeInject/types.ts` 添加消息类型 |
| 297 | 3. 更新 `homeInject/constants.ts` 添加 Action |
| 298 | 4. 更新 `homeInject/WebAPI.ts` 暴露方法 |
| 299 | 5. 更新 `content_script_home.tsx` 转发消息 |
| 300 | 6. 更新 `background.ts` 和 `HomeInjectBackgroundHandler.ts` 处理消息 |
| 301 |