书籍和听书数据连接功能说明.md 8.8 KB

书籍和听书数据连接功能说明

✅ 已完成的功能

1. 后端接口完善

书籍详情接口

  • 接口: GET /api/book/{id}
  • 功能: 根据ID获取书籍详情,返回完整的书籍信息
  • 返回字段:
    • brief - 简介
    • desc - 描述
    • introduction - 详细介绍
    • 其他字段:标题、作者、封面、价格等

听书详情接口

  • 接口: GET /api/audiobook/{id}
  • 功能: 根据ID获取听书详情,返回完整的听书信息和章节列表
  • 返回字段:
    • brief - 简介
    • desc - 描述
    • introduction - 详细介绍
    • 其他字段:标题、作者、主播、封面、章节列表等

2. 前端页面更新

书籍详情页(book-detail.vue)

  • ✅ 从后端API获取完整的书籍详情数据
  • ✅ 显示书籍的简介(brief)
  • ✅ 显示书籍的描述(desc)
  • ✅ 显示书籍的详细介绍(introduction)
  • ✅ 移除硬编码数据,全部从数据库获取
  • ✅ 添加加载状态提示
  • ✅ 处理数据为空的情况

听书详情页(listen-detail.vue)

  • ✅ 从后端API获取完整的听书详情数据
  • ✅ 显示听书的简介(brief)
  • ✅ 显示听书的描述(desc)
  • ✅ 显示听书的详细介绍(introduction)
  • ✅ 确保所有数据从数据库获取
  • ✅ 处理数据为空的情况

3. 数据字段映射

书籍表(books)

  • brief - 简介(简短描述)
  • desc - 描述(中等长度描述)
  • introduction - 详细介绍(完整描述)

听书表(audiobooks)

  • brief - 简介(简短描述)
  • desc - 描述(中等长度描述)
  • introduction - 详细介绍(完整描述)

4. 数据展示逻辑

书籍详情页

  • 头部信息: 显示 briefdesc(优先显示brief)
  • 简介部分: 显示 introductiondescbrief(优先显示introduction)
  • 数据回退: 如果某个字段为空,自动使用其他字段作为回退

听书详情页

  • 头部信息: 显示 descbriefintroduction(优先显示desc)
  • 数据回退: 如果某个字段为空,自动使用其他字段作为回退

🔧 实现细节

1. 后端实现

BookService.getBookById()

public BookVO getBookById(Integer id) {
    Book book = bookMapper.selectById(id);
    if (book == null) {
        throw new RuntimeException("书籍不存在");
    }
    return convertToVO(book);
}

AudiobookService.getAudiobookDetail()

public AudiobookDetailVO getAudiobookDetail(Integer id, Integer userId) {
    Audiobook audiobook = audiobookMapper.selectById(id);
    if (audiobook == null) {
        throw new RuntimeException("听书不存在");
    }
    // 增加浏览次数
    audiobookMapper.incrementViewCount(id);
    // 获取章节列表
    List<AudiobookChapter> chapters = chapterMapper.selectByAudiobookId(id);
    // 转换为VO
    AudiobookDetailVO detailVO = new AudiobookDetailVO();
    detailVO.setAudiobook(convertToVO(audiobook));
    // ... 其他处理
    return detailVO;
}

2. 前端实现

书籍详情页数据加载

async loadBookDetail(bookId) {
    try {
        this.isLoading = true
        const res = await getBookById(bookId)
        
        if (res && res.code === 200 && res.data) {
            const book = res.data
            this.bookInfo = {
                id: book.id,
                title: book.title || '',
                image: book.image || book.cover || '',
                brief: book.brief || '',
                desc: book.desc || book.brief || '',
                author: book.author || '',
                isFree: book.isFree || false,
                price: book.price || 0,
                introduction: book.introduction || book.desc || book.brief || ''
            }
        }
    } catch (e) {
        console.error('加载书籍详情失败:', e)
    } finally {
        this.isLoading = false
    }
}

听书详情页数据加载

async loadAudiobookDetail() {
    try {
        this.isLoading = true
        const userId = this.userInfo ? this.userInfo.id : null
        const res = await getAudiobookDetail(this.audiobookId, userId)
        
        if (res && res.code === 200 && res.data) {
            const data = res.data
            if (data.audiobook) {
                const audiobook = data.audiobook
                this.bookInfo = {
                    id: audiobook.id,
                    title: audiobook.title || '',
                    desc: audiobook.desc || audiobook.brief || '',
                    brief: audiobook.brief || audiobook.desc || '',
                    introduction: audiobook.introduction || audiobook.desc || audiobook.brief || '',
                    author: audiobook.author || '',
                    image: audiobook.image || audiobook.cover || '',
                    narrator: audiobook.narrator || ''
                }
            }
        }
    } catch (e) {
        console.error('加载听书详情失败:', e)
    } finally {
        this.isLoading = false
    }
}

📊 数据流程

书籍详情数据流程

  1. 前端调用 getBookById(bookId)
  2. 后端 BookController.getBookById() 接收请求
  3. 后端 BookService.getBookById() 查询数据库
  4. 后端 BookMapper.selectById() 执行SQL查询
  5. 数据库返回书籍数据(包含brief、desc、introduction)
  6. 后端转换为 BookVO 并返回
  7. 前端接收数据并更新页面显示

听书详情数据流程

  1. 前端调用 getAudiobookDetail(audiobookId, userId)
  2. 后端 AudiobookController.getAudiobookDetail() 接收请求
  3. 后端 AudiobookService.getAudiobookDetail() 查询数据库
  4. 后端 AudiobookMapper.selectById() 执行SQL查询
  5. 数据库返回听书数据(包含brief、desc、introduction)
  6. 后端查询章节列表
  7. 后端转换为 AudiobookDetailVO 并返回
  8. 前端接收数据并更新页面显示

🎯 功能特点

1. 数据完整性

  • ✅ 所有数据从数据库获取
  • ✅ 支持brief、desc、introduction三个字段
  • ✅ 自动处理字段为空的情况
  • ✅ 提供数据回退机制

2. 用户体验

  • ✅ 添加加载状态提示
  • ✅ 处理数据加载失败的情况
  • ✅ 显示友好的错误提示
  • ✅ 支持数据为空时的显示

3. 数据一致性

  • ✅ 前后端数据字段一致
  • ✅ 数据库字段与VO字段映射正确
  • ✅ 支持cover和image字段兼容
  • ✅ 处理数据格式转换

📝 数据库字段说明

书籍表(books)

  • brief - VARCHAR - 简介(简短描述,用于列表展示)
  • desc - TEXT - 描述(中等长度描述,用于详情页头部)
  • introduction - TEXT - 详细介绍(完整描述,用于详情页简介部分)

听书表(audiobooks)

  • brief - VARCHAR - 简介(简短描述,用于列表展示)
  • desc - TEXT - 描述(中等长度描述,用于详情页头部)
  • introduction - TEXT - 详细介绍(完整描述,用于详情页简介部分)

🔍 测试建议

1. 测试书籍详情页

  • 测试有完整数据的书籍
  • 测试只有brief的书籍
  • 测试只有desc的书籍
  • 测试只有introduction的书籍
  • 测试所有字段都为空的情况
  • 测试不存在的书籍ID

2. 测试听书详情页

  • 测试有完整数据的听书
  • 测试只有brief的听书
  • 测试只有desc的听书
  • 测试只有introduction的听书
  • 测试所有字段都为空的情况
  • 测试不存在的听书ID

3. 测试数据展示

  • 验证brief、desc、introduction正确显示
  • 验证数据回退机制正常工作
  • 验证加载状态正确显示
  • 验证错误处理正常工作

🚀 使用说明

1. 书籍详情页

  • 访问 /pages/book-detail/book-detail?bookId=1
  • 页面会自动从后端获取书籍详情
  • 显示书籍的完整信息(标题、作者、简介、描述、详细介绍等)

2. 听书详情页

  • 访问 /pages/listen-detail/listen-detail?audiobookId=1
  • 页面会自动从后端获取听书详情
  • 显示听书的完整信息(标题、作者、主播、简介、描述、详细介绍、章节列表等)

📌 注意事项

  1. 数据优先级:

    • 书籍详情页:introduction > desc > brief
    • 听书详情页:desc > brief > introduction
  2. 字段兼容性:

    • 支持cover和image字段互用
    • 如果image为空,使用cover
    • 如果cover为空,使用image
  3. 数据回退:

    • 如果某个字段为空,自动使用其他字段作为回退
    • 确保用户始终能看到内容
  4. 错误处理:

    • 处理网络请求失败的情况
    • 处理数据格式不正确的情况
    • 处理数据为空的情况

🎉 完成状态

  • ✅ 后端接口完善
  • ✅ 前端页面更新
  • ✅ 数据字段映射
  • ✅ 数据展示逻辑
  • ✅ 错误处理
  • ✅ 加载状态
  • ✅ 数据回退机制

所有功能已完成,可以开始测试!