鸿蒙跨端故事接龙应用开发指南 原创

进修的泡芙
发布于 2025-6-22 16:58
浏览
0收藏

鸿蒙跨端故事接龙应用开发指南

一、项目概述

本文基于HarmonyOS的分布式能力和AI自然语言处理技术,开发一款多用户协作的故事接龙应用。系统允许用户开头或续写故事,AI智能生成后续情节,并通过分布式数据同步实现多设备间的实时故事创作共享,借鉴了《鸿蒙跨端U同步》中多设备数据同步的技术原理。

二、系统架构

±--------------------+ ±--------------------+ ±--------------------+
用户设备A <-----> 分布式数据总线 <-----> 用户设备B
(手机/平板) (Distributed Bus) (手机/平板)
±---------±---------+ ±---------±---------+ ±---------±---------+

±---------v----------+ ±---------v----------+ ±---------v----------+
故事创作模块 AI续写模块 实时同步模块
(Story Creation) (AI Generation) (Real-time Sync)

±--------------------+ ±--------------------+ ±--------------------+

三、核心代码实现
故事管理服务

// src/main/ets/service/StoryService.ts
import { distributedData } from ‘@ohos.data.distributedData’;
import { BusinessError } from ‘@ohos.base’;
import { http } from ‘@ohos.net.http’;
import { ai } from ‘@ohos.ai’;

interface StorySegment {
author: string;
content: string;
timestamp: number;
isAI: boolean;
interface Story {

id: string;
title: string;
segments: StorySegment[];
participants: string[];
lastUpdated: number;
export class StoryService {

private static instance: StoryService;
private kvStore: distributedData.KVStore | null = null;
private readonly STORE_ID = ‘story_chain_store’;
private currentStories: Story[] = [];
private httpRequest = http.createHttp();
private aiTextGenerator: ai.TextGenerator | null = null;

private constructor() {
this.initKVStore();
this.initAITextGenerator();
public static getInstance(): StoryService {

if (!StoryService.instance) {
  StoryService.instance = new StoryService();

return StoryService.instance;

private async initKVStore(): Promise<void> {

try {
  const options: distributedData.KVManagerConfig = {
    bundleName: 'com.example.storychain',
    userInfo: {
      userId: '0',
      userType: distributedData.UserType.SAME_USER_ID

};

  const kvManager = distributedData.createKVManager(options);
  this.kvStore = await kvManager.getKVStore({
    storeId: this.STORE_ID,
    options: {
      createIfMissing: true,
      encrypt: false,
      backup: false,
      autoSync: true,
      kvStoreType: distributedData.KVStoreType.SINGLE_VERSION

});

  this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
    data.insertEntries.forEach((entry: distributedData.Entry) => {
      if (entry.key === 'stories') {
        this.notifyStoriesChange(entry.value.value as Story[]);

});

  });

catch (e) {

  console.error(Failed to initialize KVStore. Code: {e.code}, message: {e.message});

}

private async initAITextGenerator(): Promise<void> {
try {
this.aiTextGenerator = await ai.createTextGenerator();
catch (e) {

  console.error(Failed to initialize AI text generator. Code: {e.code}, message: {e.message});

}

public async createNewStory(title: string, firstSegment: string, author: string): Promise<Story> {
const newStory: Story = {
id: this.generateStoryId(),
title,
segments: [{
author,
content: firstSegment,
timestamp: Date.now(),
isAI: false
}],
participants: [author],
lastUpdated: Date.now()
};

this.currentStories.push(newStory);
await this.syncStories();
return newStory;

private generateStoryId(): string {

return story_{Date.now()}_{Math.floor(Math.random() * 10000)};

public async addSegment(storyId: string, content: string, author: string): Promise<Story | undefined> {

const story = this.currentStories.find(s => s.id === storyId);
if (!story) return undefined;

story.segments.push({
  author,
  content,
  timestamp: Date.now(),
  isAI: false
});

if (!story.participants.includes(author)) {
  story.participants.push(author);

story.lastUpdated = Date.now();

await this.syncStories();
return story;

public async generateAIContinuation(storyId: string): Promise<Story | undefined> {

const story = this.currentStories.find(s => s.id === storyId);
if (!story || !this.aiTextGenerator) return undefined;

try {
  // 获取最近3段内容作为AI生成上下文
  const recentSegments = story.segments.slice(-3).map(s => s.content).join('\n');
  
  // 设置AI生成参数
  const config: ai.TextGeneratorConfig = {
    prompt: 请根据以下故事内容续写一段情节:\n${recentSegments}\n,
    maxTokens: 200,
    temperature: 0.7,
    topP: 0.9
  };
  
  // 调用AI生成接口
  const result = await this.aiTextGenerator.generateText(config);
  
  if (result && result.text) {
    story.segments.push({
      author: 'AI助手',
      content: result.text.trim(),
      timestamp: Date.now(),
      isAI: true
    });
    
    story.lastUpdated = Date.now();
    await this.syncStories();
    return story;

} catch (e) {

  console.error(Failed to generate AI continuation. Code: {e.code}, message: {e.message});

return undefined;

private async syncStories(): Promise<void> {

if (this.kvStore) {
  try {
    await this.kvStore.put('stories', { value: this.currentStories });

catch (e) {

    console.error(Failed to sync stories. Code: {e.code}, message: {e.message});

}

private notifyStoriesChange(newStories: Story[]): void {

// 合并新旧故事,保留最新版本
const mergedStories = [...this.currentStories];

newStories.forEach(newStory => {
  const existingIndex = mergedStories.findIndex(s => s.id === newStory.id);
  
  if (existingIndex >= 0) {
    // 保留更新时间更近的故事
    if (newStory.lastUpdated > mergedStories[existingIndex].lastUpdated) {
      mergedStories[existingIndex] = newStory;

} else {

    mergedStories.push(newStory);

});

this.currentStories = mergedStories;
// 实际应用中这里应该通知UI更新
console.log('Stories updated:', this.currentStories);

public async getStories(): Promise<Story[]> {

if (!this.kvStore) return this.currentStories;

try {
  const entry = await this.kvStore.get('stories');
  return entry?.value || this.currentStories;

catch (e) {

  console.error(Failed to get stories. Code: {e.code}, message: {e.message});
  return this.currentStories;

}

public async getStoryById(id: string): Promise<Story | undefined> {
const stories = await this.getStories();
return stories.find(s => s.id === id);
public async destroy(): Promise<void> {

if (this.kvStore) {
  this.kvStore.off('dataChange');

if (this.aiTextGenerator) {

  this.aiTextGenerator.release();

}

故事创作组件

// src/main/ets/components/StoryCreation.ets
@Component
export struct StoryCreation {
private storyService = StoryService.getInstance();
@State currentStory: Story | null = null;
@State newSegmentText: string = ‘’;
@State showCreateDialog: boolean = false;
@State newStoryTitle: string = ‘’;
@State newStoryContent: string = ‘’;

aboutToAppear(): void {
this.loadStories();
private async loadStories(): Promise<void> {

const stories = await this.storyService.getStories();
if (stories.length > 0) {
  this.currentStory = stories[0];

}

build() {
Column() {
// 故事标题和创建按钮
Row() {
Text(this.currentStory ? this.currentStory.title : ‘暂无故事’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.layoutWeight(1);

    Button('新建故事')
      .type(ButtonType.Capsule)
      .backgroundColor('#FF4081')
      .fontColor('#FFFFFF')
      .onClick(() => {
        this.showCreateDialog = true;
      });

.width(‘100%’)

  .margin({ bottom: 20 });
  
  // 故事内容展示
  Scroll() {
    Column() {
      if (this.currentStory) {
        ForEach(this.currentStory.segments, (segment) => {
          this.buildStorySegment(segment);
        })

else {

        Text('选择一个故事或创建新故事开始接龙')
          .fontSize(16)
          .fontColor('#666666')
          .margin({ top: 50 });

}

    .width('100%')

.width(‘100%’)

  .height('60%')
  .margin({ bottom: 20 });
  
  // 续写输入区域
  if (this.currentStory) {
    TextArea({ text: this.newSegmentText, placeholder: '写下你的故事续写...' })
      .width('100%')
      .height(120)
      .onChange((value: string) => {
        this.newSegmentText = value;
      });
    
    Row() {
      Button('提交续写')
        .type(ButtonType.Capsule)
        .width('40%')
        .backgroundColor('#4CAF50')
        .fontColor('#FFFFFF')
        .onClick(() => {
          this.addStorySegment();
        });
      
      Button('AI续写')
        .type(ButtonType.Capsule)
        .width('40%')
        .backgroundColor('#2196F3')
        .fontColor('#FFFFFF')
        .margin({ left: 20 })
        .onClick(() => {
          this.generateAISegment();
        });

.width(‘100%’)

    .justifyContent(FlexAlign.Center)
    .margin({ top: 20 });

}

.width('100%')
.height('100%')
.padding(20)

// 新建故事对话框
if (this.showCreateDialog) {
  Dialog.show({
    title: '新建故事',
    content: this.buildCreateDialogContent(),
    confirm: {
      value: '创建',
      action: () => {
        this.createNewStory();
        this.showCreateDialog = false;

},

    cancel: () => {
      this.showCreateDialog = false;

});

}

@Builder
private buildStorySegment(segment: StorySegment) {
Column() {
Row() {
Text(segment.author)
.fontSize(14)
.fontColor(segment.isAI ? ‘#2196F3’ : ‘#FF4081’)
.fontWeight(FontWeight.Bold);

    Text(new Date(segment.timestamp).toLocaleTimeString())
      .fontSize(12)
      .fontColor('#666666')
      .margin({ left: 10 });

.width(‘100%’)

  .justifyContent(FlexAlign.Start)
  .margin({ bottom: 5 });
  
  Text(segment.content)
    .fontSize(16)
    .textAlign(TextAlign.Start)
    .margin({ bottom: 20 });

.width(‘100%’)

.padding(10)
.backgroundColor('#FFFFFF')
.borderRadius(10)
.margin({ bottom: 10 });

@Builder

private buildCreateDialogContent() {
Column() {
TextInput({ placeholder: ‘故事标题’ })
.width(‘100%’)
.margin({ bottom: 15 })
.onChange((value: string) => {
this.newStoryTitle = value;
});

  TextInput({ placeholder: '故事开头', text: this.newStoryContent })
    .width('100%')
    .height(120)
    .onChange((value: string) => {
      this.newStoryContent = value;
    });

.width(‘100%’)

.padding(10)

private async createNewStory(): Promise<void> {

if (this.newStoryTitle && this.newStoryContent) {
  const newStory = await this.storyService.createNewStory(
    this.newStoryTitle,
    this.newStoryContent,
    '当前用户' // 实际应用中应使用真实用户名
  );
  
  this.currentStory = newStory;
  this.newStoryTitle = '';
  this.newStoryContent = '';

}

private async addStorySegment(): Promise<void> {
if (this.currentStory && this.newSegmentText) {
const updatedStory = await this.storyService.addSegment(
this.currentStory.id,
this.newSegmentText,
‘当前用户’ // 实际应用中应使用真实用户名
);

  if (updatedStory) {
    this.currentStory = updatedStory;
    this.newSegmentText = '';

}

private async generateAISegment(): Promise<void> {

if (this.currentStory) {
  const updatedStory = await this.storyService.generateAIContinuation(this.currentStory.id);
  if (updatedStory) {
    this.currentStory = updatedStory;

}

}

故事列表组件

// src/main/ets/components/StoryList.ets
@Component
export struct StoryList {
private storyService = StoryService.getInstance();
@State stories: Story[] = [];
@State selectedStoryId: string | null = null;

aboutToAppear(): void {
this.loadStories();
private async loadStories(): Promise<void> {

this.stories = await this.storyService.getStories();
if (this.stories.length > 0 && !this.selectedStoryId) {
  this.selectedStoryId = this.stories[0].id;

}

build() {
Column() {
// 故事列表标题
Text(‘故事列表’)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });

  // 故事列表
  List({ space: 10 }) {
    ForEach(this.stories, (story) => {
      ListItem() {
        this.buildStoryItem(story);

})

.width(‘100%’)

  .layoutWeight(1);

.width(‘100%’)

.height('100%')
.padding(20);

@Builder

private buildStoryItem(story: Story) {
Column() {
Row() {
Text(story.title)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.layoutWeight(1);

    Text(${story.participants.length}人参与)
      .fontSize(14)
      .fontColor('#666666');

.width(‘100%’)

  .margin({ bottom: 5 });
  
  Text(story.segments[0].content.length > 50 ? 
       ${story.segments[0].content.substring(0, 50)}... : 
       story.segments[0].content)
    .fontSize(14)
    .fontColor('#666666')
    .margin({ bottom: 5 });
  
  Text(最后更新: ${new Date(story.lastUpdated).toLocaleString()})
    .fontSize(12)
    .fontColor('#999999');

.width(‘100%’)

.padding(15)
.backgroundColor(this.selectedStoryId === story.id ? '#E3F2FD' : '#FFFFFF')
.borderRadius(10)
.onClick(() => {
  this.selectedStoryId = story.id;
  // 实际应用中应该通知父组件切换显示的故事
});

}

主界面实现

// src/main/ets/pages/StoryChainPage.ets
import { StoryService } from ‘…/service/StoryService’;
import { StoryCreation } from ‘…/components/StoryCreation’;
import { StoryList } from ‘…/components/StoryList’;

@Entry
@Component
struct StoryChainPage {
@State activeTab: number = 0;
@State deviceList: string[] = [];
private storyService = StoryService.getInstance();

build() {
Column() {
// 应用标题
Text(‘故事接龙’)
.fontSize(24)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });

  // 标签页
  Tabs({ barPosition: BarPosition.Start }) {
    TabContent() {
      // 故事创作标签页
      StoryCreation()

.tabBar(‘创作’);

    TabContent() {
      // 故事列表标签页
      StoryList()

.tabBar(‘故事’);

    TabContent() {
      // 设备管理标签页
      this.buildDevicesTab()

.tabBar(‘设备’);

.barWidth(‘100%’)

  .barHeight(50)
  .width('100%')
  .height('80%')

.width(‘100%’)

.height('100%')
.padding(20)
.onAppear(() => {
  // 模拟获取设备列表
  setTimeout(() => {
    this.deviceList = ['我的手机', '朋友的平板', '客厅电视'];
  }, 1000);
});

@Builder

private buildDevicesTab() {
Column() {
Text(‘已连接设备’)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 20 });

  if (this.deviceList.length > 0) {
    List({ space: 15 }) {
      ForEach(this.deviceList, (device) => {
        ListItem() {
          Row() {
            Image($r('app.media.ic_device'))
              .width(40)
              .height(40)
              .margin({ right: 15 });
            
            Text(device)
              .fontSize(16)
              .layoutWeight(1);
            
            if (device === '我的手机') {
              Text('主设备')
                .fontSize(14)
                .fontColor('#4CAF50');

}

          .width('100%')
          .padding(15)
          .backgroundColor('#FFFFFF')
          .borderRadius(10)

})

.width(‘100%’)

    .layoutWeight(1);

else {

    Text('没有连接的设备')
      .fontSize(16)
      .fontColor('#666666')
      .margin({ top: 50 });

Button(‘添加设备’)

    .type(ButtonType.Capsule)
    .width('80%')
    .margin({ top: 30 })
    .backgroundColor('#2196F3')
    .fontColor('#FFFFFF');

.width(‘100%’)

.height('100%')
.padding(10);

}

四、与游戏同步技术的结合点
分布式状态同步:借鉴游戏中多玩家状态同步机制,实现故事内容的跨设备实时同步

实时协作编辑:类似游戏中的实时互动,允许多用户同时参与故事创作

冲突解决策略:使用时间戳优先策略解决多设备同时编辑的冲突

设备角色分配:类似游戏中的主机/客户端角色,确定主编辑设备和从属设备

数据压缩传输:优化故事文本的传输效率,类似游戏中的网络优化

五、关键特性实现
AI故事续写:

  const config: ai.TextGeneratorConfig = {
 prompt: 请根据以下故事内容续写一段情节:\n${recentSegments}\n,
 maxTokens: 200,
 temperature: 0.7,
 topP: 0.9

};

const result = await this.aiTextGenerator.generateText(config);

故事版本同步:

  private notifyStoriesChange(newStories: Story[]): void {
 const mergedStories = [...this.currentStories];
 
 newStories.forEach(newStory => {
   const existingIndex = mergedStories.findIndex(s => s.id === newStory.id);
   
   if (existingIndex >= 0) {
     if (newStory.lastUpdated > mergedStories[existingIndex].lastUpdated) {
       mergedStories[existingIndex] = newStory;

} else {

     mergedStories.push(newStory);

});

 this.currentStories = mergedStories;

分布式数据存储:

  this.kvStore.on('dataChange', distributedData.SubscribeType.SUBSCRIBE_TYPE_REMOTE, (data) => {
 data.insertEntries.forEach((entry: distributedData.Entry) => {
   if (entry.key === 'stories') {
     this.notifyStoriesChange(entry.value.value as Story[]);

});

});

多用户参与记录:

  if (!story.participants.includes(author)) {
 story.participants.push(author);

六、性能优化策略
AI生成节流:

  private lastAIGenerationTime = 0;

private readonly AI_GEN_COOLDOWN = 10000; // 10秒冷却

public async generateAIContinuation(storyId: string): Promise<Story | undefined> {
const now = Date.now();
if (now - this.lastAIGenerationTime < this.AI_GEN_COOLDOWN) return;
this.lastAIGenerationTime = now;
// 生成逻辑…

批量更新同步:

  private syncTimer: number | null = null;

private scheduleSync(): void {
if (this.syncTimer) clearTimeout(this.syncTimer);
this.syncTimer = setTimeout(() => {
this.syncStories();
this.syncTimer = null;
}, 2000); // 2秒内多次更新只同步一次

本地缓存优先:

  public async getStories(): Promise<Story[]> {
 // 先返回本地缓存
 const cachedStories = this.currentStories;
 
 // 异步从分布式存储获取最新状态
 if (this.kvStore) {
   this.kvStore.get('stories').then((entry) => {
     if (entry?.value) {
       this.currentStories = entry.value;

});

return cachedStories;

资源释放管理:

  public async destroy(): Promise<void> {
 if (this.aiTextGenerator) {
   this.aiTextGenerator.release();

}

七、项目扩展方向
多语言支持:扩展支持多种语言的AI故事生成

插图生成:基于故事内容自动生成插图

语音朗读:将故事内容转换为语音朗读

故事出版:提供故事导出和分享功能

主题模板:提供不同类型的故事创作模板

八、总结

本故事接龙应用实现了以下核心功能:
多用户协作的故事创作与续写

AI智能生成故事后续情节

多设备间的实时故事同步

直观的故事创作和阅读界面

通过借鉴游戏中的多设备同步技术,我们构建了一个富有创意的协作创作工具。该项目展示了HarmonyOS在分布式协作和AI集成方面的强大能力,为开发者提供了社交创意类应用开发的参考方案。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
收藏
回复
举报
回复
    相关推荐