← 返回项目档案

项目复盘 · 2019

智库官网 2.0:把报告、新闻与机构合作变成可维护内容

一套由 Nuxt 前台、Vue 管理后台和 Go API 组成的机构官网,将报告、新闻、服务、团队与合作信息从静态页面迁移到可持续维护的内容平台。

内容平台机构官网NuxtGo

研究机构的官网不只是品牌页面。报告需要按时间发布,新闻需要持续更新,合作案例和团队信息会变化,旧内容还要保持稳定链接。如果每次修改都依赖研发重新部署,内容很快就会陈旧。

智库官网 2.0 把公开站点、内容管理和服务接口拆成三个仓库,使编辑者能够维护新闻、报告、页面和机构信息,访问者则通过适配桌面与移动端的前台浏览内容。

从内部资料到公开内容的发布链
内部知识库内容筛选脱敏与事实审核管理后台草稿
管理后台草稿发布审核Go 内容接口对象与内容存储
Go 内容接口Nuxt 公开站点桌面与移动访问
发布审核变更记录撤回或修订Go 内容接口

前台围绕机构内容组织

Nuxt 前台包含首页、关于、顾问、新闻、新闻详情和报告等页面,并把 Banner、新闻、报告、服务、团队与合作伙伴拆成可复用组件。提交记录显示,首版在 2019 年 7 月集中完成,随后持续修正文案、图片、排序、新闻时间、移动端合作伙伴展示和二维码尺寸。

这种信息架构把高频更新的新闻和报告与相对稳定的机构介绍分开。服务和合作伙伴承担业务说明,团队与顾问页面建立专业身份,联系信息提供行动入口。对研究机构而言,报告详情还应明确发布日期、作者化名或机构署名、版本、摘要、下载格式和引用方式。

前台仓库一直有修复记录延续到 2022 年,说明网站并非一次性交付后立即废弃。但提交信息较简略,缺少版本标签和自动化测试记录,无法仅凭更新日期判断每次线上发布内容。

管理后台覆盖主要内容对象

Vue 管理后台提供登录,以及团队、文章、联系、业务范围、合作伙伴、报告等管理页面。编辑器支持富文本,图片可以上传和裁剪。代码中的服务模块与页面结构一一对应,说明后台不是只有空壳界面。

内容管理的关键是权限和发布流程。新闻编辑者、报告审核者和管理员不应共享同一权限;富文本与外部链接需要过滤,图片上传要限制格式和大小,重要内容最好经过草稿、审核、发布和撤回状态。

现有代码能够证明基本增删改、资源上传、会话入口和变更记录读取,但没有足够证据确认多角色审批、记录不可篡改与版本回滚均已实现。管理后台的一个后期提交还涉及“权重”,推测用于排序,但具体业务规则需要进一步版本文档支持。

Go 服务把内容与展示解耦

后端仓库以独立服务模块管理管理员、Banner、联系信息、合作、文件、成员、新闻、页面、合作伙伴、报告和会话,并分别生成公开应用与管理应用入口。它还接入对象存储处理图片等文件。

这种结构允许前台替换而不迁移内容,也让管理端和公开端使用不同接口边界。后端提交记录包括页面、二维码、发布时间、跳转链接、内联图片、Banner、新闻简介和排序等迭代,与前后台功能能够相互印证。

更具体地看,仓库中的两份 OpenAPI 3.0 契约把边界写得相当清楚。公开接口主要是读取:新闻、报告、合作案例、成员、合作伙伴和 Banner 都提供列表、数量与单条详情,列表统一接受 offsetlimit、时间区间、标签或排序参数;管理接口则在同一资源模型上增加创建、修改、删除、文件上传、登录检查、登录、退出和认证信息更新。前台不需要知道数据库结构,只依赖稳定的查询契约;后台也不必把管理能力混进公开页面。

这套接口还有一个容易被页面截图忽略的设计:主要内容对象都存在 recordrecord/count 查询,联系信息和页面这种单例资源也有独立记录接口。这能确认系统至少把“当前内容”和“变更记录”作为不同读取模型,而不只是最后一次覆盖保存。不过,接口存在不等于完整审计成立:现有证据还不能确认记录是否不可篡改、是否保存操作者与差异、保留多久、能否一键回滚。公开契约甚至暴露了 Banner 的记录查询,而其他记录主要位于管理契约中,这一不一致也应在权限审计时重点复查。

服务端目录中同时存在由契约生成的路由、客户端和模型代码。这说明项目采用了偏 API-first 的协作方式,也解释了为什么后端文件数量较大:大量文件是生成物,不能直接当成手写业务复杂度。真正值得维护的是资源模型、校验规则、存储实现以及生成流程;契约变更后若没有兼容性检查,前台、后台与服务端仍可能同时编译成功却在运行时出现字段错位。

下面是主服务新闻模块的源码节选(已脱敏)。更新操作会读取旧值、写入新值,再把前后快照保存到独立记录集合;它证明“变更记录”不只是契约中的占位接口:

go
func (s *NewsService) record(news *NewsWithID, current *News) error {
    if news == nil {
        return nil
    }

    count, err := s.dbRecord.Find(
        bson.D{{"news_id", news.ID}},
    ).Count()
    if err != nil {
        return err
    }

    record := &NewsRecord{
        NewsID:      news.ID,
        Current:     current,
        CurrentTime: bson.Now(),
        Times:       count + 1,
        Recent:      &news.News,
        RecentTime:  news.UpdateTime,
    }
    return s.dbRecord.Insert(record)
}

这段实现保存修改前后的完整内容及时间,却仍不能证明强审计成立:计数后插入并非原子操作,并发修改可能得到相同序号;记录与主内容更新也没有共同事务;代码中未见操作者标识和回滚动作。另一个单提交的契约生成仓库还保留了空处理函数,它适合证明客户端与路由骨架,不应与这份 78 次提交的主服务实现混为一谈。

仓库同时提交了大量第三方依赖。固定依赖版本有助于构建复现,但会显著扩大审计范围;后续应使用依赖清单和自动漏洞扫描,明确哪些文件是自研代码,避免用仓库体积误判项目规模。

Confluence 是网站内容的上游目录

早期 wuzhen 空间保存机构产品、年度报告、人才榜、数据清单、人员和项目列表。它更像内容策划与内部资料索引,而官网承担经过筛选的公开发布。

两者之间需要明确发布边界。人员名单、未完成项目和内部数据不能因进入同一内容库就自动公开;报告与新闻则应在发布时复制必要元数据并保留来源页面。理想流程是内部资料经过审核形成公开内容,而不是让官网直接暴露 Confluence 结构。

项目留下的经验

  • 机构官网应把新闻、报告、服务、团队和合作伙伴建模为独立内容对象,而不是写死在页面中。
  • 管理端与公开端应使用不同权限边界,重要内容需要草稿、审核、发布、撤回与审计记录。
  • 报告页面应提供稳定链接、版本、发布日期、摘要和引用信息,方便长期传播。
  • 富文本、外链和图片上传必须做安全过滤、大小限制与资源生命周期管理。
  • 代码发布要绑定版本标签与变更清单,简短的“修复”提交不足以还原线上版本。
  • 内部知识库与公开官网之间必须有明确的内容分级和发布流程。
  • OpenAPI 生成物要和手写业务代码分开统计;评估系统规模时应看资源模型、领域规则和集成点,而不是只看文件数。
  • 变更记录接口只是审计能力的入口,验收还要验证操作者、时间、差异、不可篡改性、保留期限和回滚语义。

证据边界

该项目没有独立 Jira Key。Confluence 的 wuzhen 空间有 9 条内容记录,LW 空间有 2 条早期 wiki 记录。GitLab 保存三个主仓库:官网前台 34 次提交、管理后台 23 次提交、Go 服务 78 次提交;另有一个单提交的契约生成快照。本文进一步核对了公开与管理 OpenAPI 契约、主服务的资源实现和变更记录逻辑;代码结构、接口路径和提交历史能够相互验证主要内容功能。

现有资料缺少正式验收、访问指标、内容迁移清单和生产部署记录,因此本文确认官网平台被实现并持续维护,不推断其全部内容均已迁移、所有管理权限完善或各年份持续在线。公开总结也不引用内部人员名单中的真实姓名。