← xlucky的博客

移动软件开发-实验3

做了一个海大新闻网

文章目录
  1. 一、实验内容
  2. 1. 实验目的
  3. 2. 页面设计思路
  4. 3. 项目结构
  5. 4. 实验过程
  6. 二、问题总结与体会
  7. 1. 遇到的问题及解决方法
  8. 2. 实验体会

一、实验内容

1. 实验目的

本实验以中国海洋大学新闻网为主题,使用微信小程序原生框架实现校园新闻浏览应用。通过首页、栏目、新闻详情和个人中心等页面,练习 WXML 组件使用、WXSS 布局、页面跳转与参数传递、生命周期、事件绑定及本地存储。在新闻展示的基础上,增加搜索、排序、点赞、评论和收藏功能,形成完整的新闻阅读与互动流程。

此外,使用 Node.js、Express 和 Docker 搭建自建后端,把文章、互动记录和投稿数据保存到服务器,进一步练习网络请求、接口设计和数据持久化。本文对应改造前的新闻网版本,不包含后续图片社区的页面和功能。

2. 页面设计思路

小程序采用底部导航组织主要功能,用户可以在“首页”“栏目”“我的”之间切换。首页以轮播图和新闻卡片展示内容,栏目页按分类组织新闻,详情页承载阅读与互动,个人中心提供登录、资料设置和收藏入口。发布文章和投稿记录作为扩展页面,从个人中心进入。

页面或模块主要内容实现作用
首页轮播图、新闻列表、搜索框、最新/最热切换快速浏览和筛选已加载新闻
栏目页栏目分组与文章数量按主题进入分类列表
分类列表指定栏目下的文章展示同类新闻
新闻详情标题、日期、封面、正文与互动区阅读、点赞、评论和收藏
我的微信登录、头像昵称、收藏列表维护个人资料与阅读记录
发布文章栏目、标题、摘要、正文、封面地址向后端提交用户投稿
我的投稿投稿记录、审核状态、重审操作查询投稿处理进度

页面使用蓝白配色、背景图和圆角卡片。布局通过 rpx 与 Flex 完成,背景作为独立 image 组件置于内容下方,避免影响文字与按钮操作。

3. 项目结构

miniprogram3/
├── app.js                         # 登录与全局身份状态
├── app.json                       # 页面注册、窗口与底部导航
├── app.wxss                       # 背景和新闻列表公共样式
├── images/
│   ├── bg.png                     # 本地背景
│   ├── swiper/                    # 三张本地轮播图
│   └── demo4_file/images/         # 导航图标
├── pages/
│   ├── index/                     # 首页
│   ├── columns/                   # 栏目导航
│   ├── list/                      # 分类列表
│   ├── detail/                    # 新闻详情与互动
│   ├── my/                        # 登录、资料、收藏
│   ├── publish/                   # 发布文章
│   └── submissions/               # 投稿记录
├── utils/
│   ├── api.js                     # 网络请求封装
│   ├── config.js                  # API 与静态资源地址
│   ├── columns.js                 # 栏目配置
│   └── common.js                  # 示例新闻和本地降级数据
├── server/
│   ├── server.js                  # Express 路由
│   ├── article-store.js           # 文章存储与批量管理
│   ├── submission-store.js        # 投稿持久化
│   ├── ai-reviewer.js             # AI 审核适配
│   ├── data/news.json             # 初始示例文章
│   ├── Dockerfile
│   └── docker-compose.yml
├── project.config.json
└── 实验报告.md

4. 实验过程

4.1 配置页面和底部导航

在 app.json 中注册各个页面,并使用 tabBar 设置首页、栏目和我的三个主入口。主入口之间通过底部导航切换,新闻详情等二级页面使用 wx.navigateTo 打开。这样既保留了主页面状态,又可以通过返回操作继续浏览列表。

新闻卡片通过 data-id 携带文章编号,点击时读取事件中的参数:

goToDetail: function (e) {
  let id = e.currentTarget.dataset.id
  wx.navigateTo({
    url: '../detail/detail?id=' + encodeURIComponent(id)
  })
}

使用文章 ID 而不是列表下标作为跳转参数,可以避免排序和筛选后打开错误的新闻。

4.2 实现新闻列表、轮播和搜索

首页在 onShow 中刷新新闻列表,使用户从详情页返回后可以重新获取互动状态。网络请求集中封装在 utils/api.js,页面只负责调用接口和更新展示数据。

为减少一次传输和渲染的数据量,首页只请求最新 10 篇新闻:

api.getNewsList(openid, '', 10).then(list => {
  that.setData({
    newsList: that.decorate(list || []),
    loading: false
  })
  that.applyFilter()
})

newsList 保存接口返回的原始列表,displayList 保存搜索、排序后的结果。搜索将输入内容转换为小写后,与新闻标题和正文匹配;“最新”按发布日期倒序,“最热”按点赞数倒序。需要注意,首页搜索和排序的范围是当前加载的新闻,不是后台全部文章。

轮播区域使用 swiper 和 swiper-item,三张轮播图使用本地路径。新闻列表图片则使用后端地址,避免把大量新闻资源放入小程序包内。请求失败时,页面从 common.js 获取内置示例新闻,保留基本浏览能力。

4.3 增加栏目字段与分类页面

文章通过 column 字段记录所属栏目,utils/columns.js 维护栏目 ID、名称与分组信息。栏目页读取后端统计数量,用户点击栏目后,将栏目标识传给列表页,由列表页查询对应新闻。

这种组织方式把栏目配置与页面渲染分开:后端保存稳定的栏目 ID,前端显示中文名称。后续增加栏目时,可以复用现有列表和详情页,无需为每个栏目建立独立页面。

4.4 实现详情页点赞、评论和收藏

详情页根据文章 ID 请求正文和评论。后端使用 openid 标识用户,并为文章附加当前用户是否点赞、是否收藏,以及点赞和评论数量。

操作数据处理方式
点赞/取消点赞按文章记录用户标识,重复点击切换状态
发表评论检查身份、文章存在性及内容长度,写入评论列表
删除评论同时匹配评论 ID 与用户标识,仅删除本人评论
收藏/取消收藏按用户分组保存收藏文章
查看收藏在个人中心请求当前用户的收藏列表

互动数据存放在 db.json,并在修改后写入磁盘。部分页面还提供本地缓存降级逻辑,但本地降级数据不能视为已经同步到服务器。

4.5 微信登录与个人资料保存

用户点击登录按钮后,小程序调用 wx.login 获取临时 code,发送给 /api/login,由后端调用微信 code2Session 换取 openid。AppSecret 通过后端环境变量提供,不放在小程序端。

为了方便演示,未配置微信密钥且允许调试登录时,后端根据持久化的 clientId 生成稳定调试身份。调试身份和真实微信认证是两种不同模式,页面会给出相应提示。

头像和昵称由用户主动设置,并保存在本机 userInfo 中。退出时只删除身份状态:

logout() {
  wx.removeStorageSync('isMockLogin')
  wx.removeStorageSync('openid')
  this.restoreSession()
}

因此,同一设备重新登录后可以继续使用之前的头像和昵称。本版本的个人资料保留属于本机缓存,并不代表头像昵称已实现跨设备同步。

4.6 搭建后端与文章管理接口

Express 后端按职责划分文章、互动和投稿数据。首次运行时从 news.json 初始化文章仓库,后续以 articles.json 为正式文章数据源。

文章管理支持新增、更新、删除、批量导入和批量删除,管理接口使用 X-Admin-Key 请求头。批量导入时先校验整批数据,再写入文件,降低部分成功、部分失败带来的管理困难。字段映射时需要统一标题、正文、封面和栏目等字段名称。

仓库提交的是源代码及初始示例数据,不包含服务器运行时完整文章库、用户互动数据、投稿记录或密钥。

4.7 实现投稿与后端审核

发布页收集标题、栏目、摘要、正文和封面地址,向 /api/submissions 提交。后端先保存投稿,再返回 HTTP 202,并通过异步队列执行审核,避免页面持续等待模型响应。

投稿状态包含 pending、reviewing、approved、rejected、needs_review 和 review_failed。审核通过后,后端将投稿转换为正式文章;需要复核或调用失败时保留投稿记录,方便重新审核或人工处理。

本次备份中的 ai-reviewer.js 使用 mimo-v2.5 和兼容 chat/completions 的接口,要求返回包含结论和原因的 JSON。未配置密钥时返回待复核结果;该备份不是“等待 10 秒自动通过”的模拟审核版本。本次报告整理未调用真实 AI 服务验证审核结果。

4.8 Docker 部署与资源管理

后端使用 Node 22 Alpine 镜像,通过 Docker Compose 启动。数据目录映射到宿主机,使程序与运行数据分离:

ports:
  - "${HOST_PORT:-3000}:3000"
volumes:
  - ./data:/app/data

新闻封面等资源通过 /assets 提供,本地保留背景、导航图标和三张轮播图。project.config.json 将 server 排除出小程序包,后端 .env 和运行数据也通过忽略规则避免提交。

原服务器当前已部署后续图片社区。复现实验3时需要单独部署新闻网后端,并修改 utils/config.js 中的地址;不能直接认为备份里的旧 IP 仍提供新闻接口。若与现有服务同机运行,还需调整 Compose 容器名称和宿主机端口。

二、问题总结与体会

1. 遇到的问题及解决方法

  1. 首页数据过多,影响加载和点击响应。 当接口一次返回全部文章及正文时,网络传输和 setData 数据量都会增加。解决方法是在接口中增加数量限制,首页只请求最新 10 篇,并按需访问分类和详情页。

  2. 背景层可能遮挡内容点击。 全屏背景采用固定定位,若层级处理不当可能覆盖按钮。将背景设置为 z-index: 0 和 pointer-events: none,内容区域设置更高层级,使背景只负责展示。

  3. 轮播图在真机上显示异常。 开发者工具与真机的网络环境和组件表现存在差异。排查时检查图片地址与轮播结构,并将三张图片临时保留在本地,减少对远程资源的依赖。不能仅凭其他图片能够显示就认定轮播图片也一定正常。

  4. 退出登录后再次要求设置头像昵称。 身份状态和用户资料如果一起删除,会造成重复填写。将 openid 与 userInfo 分开管理,退出仅清除身份信息,保留本机资料。

  5. 新增文章的字段与原程序不一致。 不同来源的数据可能使用不同字段名。导入前统一转换为 title、content、poster 和 column 等字段,并验证必填内容,避免列表显示正常而详情缺失。

  6. 后端重建后数据需要保留。 镜像中的数据不适合保存运行时修改。通过 Docker volume 持久化文章、互动和投稿文件,部署更新时继续挂载原数据目录。

  7. AI 调用失败不能等同于审核通过。 模型可能超时、返回空内容或非预期 JSON。通过结果校验和独立状态记录,把失败或待复核情况反馈给用户,允许后续重新处理。

2. 实验体会

通过本实验,我把小程序开发从单页静态布局扩展到多页面应用,熟悉了生命周期、页面参数、列表渲染、事件处理、网络请求与本地存储之间的配合。首页、栏目和详情使用同一套文章字段后,页面之间更容易衔接,新增功能也能复用已有接口。

在调试过程中,我认识到界面卡顿和点击异常不一定只是样式问题,也可能来自返回数据过多或背景层遮挡。开发者工具预览、真机表现和服务器接口需要分别检查,才能找到问题发生的位置。

后端开发使我进一步理解了持久化、批量校验和异步任务。文章、互动与投稿分开存储后,职责更加清楚;而保存备份、记录版本和排除密钥,则让实验更容易整理、复现和提交。此次报告基于改造前快照整理,保留新闻实验的实现过程,也避免与后续图片社区混淆。