移动软件开发-实验3
做了一个海大新闻网
一、实验内容
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. 遇到的问题及解决方法
-
首页数据过多,影响加载和点击响应。 当接口一次返回全部文章及正文时,网络传输和
setData数据量都会增加。解决方法是在接口中增加数量限制,首页只请求最新 10 篇,并按需访问分类和详情页。 -
背景层可能遮挡内容点击。 全屏背景采用固定定位,若层级处理不当可能覆盖按钮。将背景设置为
z-index: 0和pointer-events: none,内容区域设置更高层级,使背景只负责展示。 -
轮播图在真机上显示异常。 开发者工具与真机的网络环境和组件表现存在差异。排查时检查图片地址与轮播结构,并将三张图片临时保留在本地,减少对远程资源的依赖。不能仅凭其他图片能够显示就认定轮播图片也一定正常。
-
退出登录后再次要求设置头像昵称。 身份状态和用户资料如果一起删除,会造成重复填写。将
openid与userInfo分开管理,退出仅清除身份信息,保留本机资料。 -
新增文章的字段与原程序不一致。 不同来源的数据可能使用不同字段名。导入前统一转换为
title、content、poster和column等字段,并验证必填内容,避免列表显示正常而详情缺失。 -
后端重建后数据需要保留。 镜像中的数据不适合保存运行时修改。通过 Docker volume 持久化文章、互动和投稿文件,部署更新时继续挂载原数据目录。
-
AI 调用失败不能等同于审核通过。 模型可能超时、返回空内容或非预期 JSON。通过结果校验和独立状态记录,把失败或待复核情况反馈给用户,允许后续重新处理。
2. 实验体会
通过本实验,我把小程序开发从单页静态布局扩展到多页面应用,熟悉了生命周期、页面参数、列表渲染、事件处理、网络请求与本地存储之间的配合。首页、栏目和详情使用同一套文章字段后,页面之间更容易衔接,新增功能也能复用已有接口。
在调试过程中,我认识到界面卡顿和点击异常不一定只是样式问题,也可能来自返回数据过多或背景层遮挡。开发者工具预览、真机表现和服务器接口需要分别检查,才能找到问题发生的位置。
后端开发使我进一步理解了持久化、批量校验和异步任务。文章、互动与投稿分开存储后,职责更加清楚;而保存备份、记录版本和排除密钥,则让实验更容易整理、复现和提交。此次报告基于改造前快照整理,保留新闻实验的实现过程,也避免与后续图片社区混淆。