news 2026/9/15 17:59:27

TREK Journey Journal 旅行日记功能完全指南:照片优先的跨行程叙事与公开分享

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TREK Journey Journal 旅行日记功能完全指南:照片优先的跨行程叙事与公开分享

TREK Journey Journal 旅行日记功能完全指南:照片优先的跨行程叙事与公开分享

【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK

Journey Journal 是 TREK 中一个照片优先的旅行日记(travel journal)模块:每本日记(journey)可关联一个或多个行程(trip),并按天沉淀文字、照片、心情与天气,最终还能通过只读分享链接对外展示。本文以 Journey-Journal.md 为主体,结合客户端组件与 Nest 服务端实现,完整讲解 Journey 的启用、创建、条目编辑、移动端时间线、地图视图、插件扩展与公开分享机制,读完即可上手使用并理解其底层数据与权限模型。

什么是 Journey

Journey 让你可以沿着行程计划(trip plan)同步书写一段叙事性的旅行记录。与行程规划中的日程(day plan)不同,Journey 的条目(entry)天然与具体日期绑定,可以包含:

  • 叙述性正文(Story),支持 Markdown 排版;
  • 照片(Photos);
  • 心情评级(Mood);
  • 天气状况(Weather);
  • 正反面评价卡片(Pros / Cons);
  • 自由标签(Tags)、地图定位(Location)与时刻(Time)。

完成后的日记可生成只读公开链接对外分享。在数据模型上,Journey 是横跨多个行程的独立实体:一条 Journey 通过journey_trips关联多个 Trip,并通过journey_entriesjourney_photosjourney_contributors分别承载条目、照片与协作者(见 server/src/services/journeyService.ts 中的查询与写入逻辑)。

启用 Journey(Admin 前提)

Journey 是一个可开关的 Addon,默认不启用。需要在管理后台的 Addons 页面中开启(参见 Admin-Addons.md)。服务端会为此做两层防护:

  • 添加守卫JourneyAddonGuard(server/src/nest/journey/journey-addon.guard.ts),未启用时所有/api/journeys请求直接返回 404;
  • JourneyService.journeyAddonEnabled()调用isAddonEnabled(ADDON_IDS.JOURNEY)检查(server/src/nest/journey/journey.service.ts)。

启用后,主导航会出现Journey入口。Journey 列表页以卡片形式展示你的全部日记,每张卡片包含封面图(cover image)以及条目数、照片数、地点数等统计——这些统计在服务端由listJourneys的子查询实时聚合(见 server/src/services/journeyService.ts)。

创建一本 Journey

在 Journey 列表页点击Create journey,只需填写:

字段说明
Title必填,日记标题;服务端会 trim 并拒绝空标题(400)
Subtitle可选副标题
Linked trips选择一个或多个已有行程

关键机制是行程关联:勾选行程后,TREK 会把该行程中已排入日程的地点(place)导入为条目的"位置锚点",并自动生成对应日期的骨架条目(skeleton entry)。从源码看,创建时逐个调用addTripToJourney(内部做canAccessTrip访问校验,防止把无权访问的行程拉进自己的日记造成跨租户泄露),随后syncTripPlaces为每个地点生成骨架条目,并继承首个真实关联行程的封面图(server/src/services/journeyService.ts)。

骨架条目是日记与行程之间的"活链接":

  • 行程地点的新增、调整日期/时间、删除都会通过onPlaceCreated/onPlaceUpdated/onPlaceDeletedreconcileTripSkeletons同步到日记(幂等,无变化时不产生写操作);
  • 骨架条目没有故事时被删除,一旦你为它补充了内容,它会被"提升"为正式条目(promoteSkeletonIfNeededupdateEntry中写入 story 即触发 type 提升);
  • 删除已填写内容的条目时,服务端会把它还原为骨架而不是彻底删除(deleteEntry)。

行程也可以在之后随时从日记设置(journal settings)中追加关联,对应的 API 为POST /api/journeys/:id/tripsDELETE /api/journeys/:id/trips/:tripId(server/src/nest/journey/journey.controller.ts)。

条目编辑器详解

每条目对应旅程中的一天,编辑器(client/src/components/Journey/JourneyDetailPageEntryEditor.tsx)提供以下字段:

标题与故事

  • Title:一天的短标题。
  • Story:自由文本,支持 Markdown。移动端卡片预览时会通过stripMarkdown去除 Markdown 符号再展示摘要(见 client/src/components/Journey/stripMarkdown.ts)。

心情(Mood)

原文档给出了四档心情。结合前端常量表 client/src/components/Journey/moodConfig.ts,实现中的完整取值如下(tired与文档表格略有出入,以源码为准):

IDLabelColor
amazingAmazingPink(#E8654A
goodGoodAmber(#EF9F27
neutralNeutralGrey(#94928C
tiredTiredBlue(#6B9BD2
roughRoughViolet(#9B8EC4

心情会以图标形式渲染在桌面端条目卡片与移动端时间线卡片上。

天气(Weather)

原文档列出六种天气,前端实现实际支持八种(多了 Snowy 与 Hot,见 moodConfig.ts):sunny(Sunny)、partly(Partly cloudy)、cloudy(Cloudy)、rainy(Rainy)、stormy(Stormy)、snowy(Snowy)、hot(Hot)、cold(Cold),每种都有对应的 Lucide 图标。

照片与 HEIC 自动转换

条目可附加多张照片,第一张照片会作为列表视图中的卡片缩略图。上传接口为POST /api/journeys/entries/:entryId/photos,服务端做了多重校验(server/src/nest/journey/journey.controller.ts):

  • 只接受image/*MIME 且拒绝 SVG;
  • 文件扩展名必须在系统允许列表内(getAllowedExtensions(),默认包含jpg,jpeg,png,gif,webp,heic等,见 server/src/services/fileService.ts);
  • 单个文件上限 20 MB,文件以crypto.randomUUID()重命名落盘到uploads/journey目录。

HEIC 兼容性说明:HEIC 是苹果独占格式,许多浏览器与平台无法识别。为此 TREK 在前端上传前用heic-to库将 HEIC/HEIF 自动转换为 JPEG(质量 0.92,文件名后缀同步改为.jpg),实现见 client/src/utils/convertHeic.ts。需要留意的是:这种转换可能导致内嵌元数据(EXIF,如 GPS 坐标、相机信息等)丢失。此外,日记还支持"图库"(gallery)概念——POST /api/journeys/:id/gallery/photos上传到日记级图库,POST /api/journeys/:id/gallery/video支持上传视频与封面帧(poster 强制存为.jpg,视频按MAX_VIDEO_SIZE限流并以 HTTP Range 流式播放,见 journey.controller.ts)。

Pros / Cons 评价卡片

可选的评价卡片:向Pros列表(大拇指朝上)或Cons列表(大拇指朝下)添加条目,用来总结旅途中的亮点与遗憾。它们在数据库中以 JSON 存储于条目的pros_cons字段,结构为{ "pros": string[], "cons": string[] }(服务端createEntry/updateEntry仅在数组非空时序列化,见 server/src/services/journeyService.ts)。

标签、位置与时间

  • Tags:自由形式的标签(如 "hidden gem"、"best meal")。前端对已知标签提供预设配色(见 moodConfig.ts 中的TAG_STYLES),服务端以 JSON 数组存储;
  • Location:将条目固定到地图上的一个位置(location_name+location_lat/location_lng);
  • Time:可选地记录一天中的某个时刻(entry_time)。

创建条目的 API 合约要求entry_date必填(否则 400),其余字段走 allow-list 白名单更新(journey.controller.ts、journeyService.ts)。完整的请求体校验定义可参考 shared/src/journey/journey.schema.ts(如journeyCreateRequestSchemajourneyShareLinkRequestSchema等 Zod 合约)。

移动端时间线

在手机上,条目以横向滚动的时间线卡片呈现:卡片显示首张照片(没有照片则显示定位占位图)、日期、天数编号、心情图标与天气图标。点击卡片会在模态面板中打开完整条目视图(相关组件见 client/src/components/Journey/MobileEntryCard.tsx 与 MobileEntryView.tsx)。天数编号的背景色取自 30 色轮换的DAY_COLORS常量(client/src/components/Journey/dayColors.ts),保证相邻天数视觉上可区分。

地图视图

Journey 详情页包含一张地图:桌面端地图位于右侧,移动端则是地图与时间线整合的视图(MobileMapTimeline),展示全部条目的定位点以及关联行程的地点。地图组件见 client/src/components/Journey/JourneyMap.tsx 与 JourneyDetailPageMapView.tsx。条目的location_namelocation_latlocation_lng在关联行程地点变化时也会被静默同步更新(onPlaceUpdated仅更新位置,不动用户已撰写的故事,见 journeyService.ts)。

通过插件扩展条目行

已安装的插件可以通过journalEntryProviderhook为日记条目卡片增加额外的信息行。插件返回形如{ label, value?, url? }的行数据,TREK 会以原生方式渲染在条目下方,不使用 iframe。该机制由 server/src/nest/plugins/journal-entry-rows.controller.ts 实现,GET /api/journal-entry-rows/:entryId具备完整的加性(additive)与故障安全(fail-safe)设计:

  • 要求 Journey addon 已启用,且调用者对条目所属日记具备访问权限(与读取日记使用同一套canAccessJourney校验);
  • 只允许http:/https:/mailto:链接,杜绝javascript:/data:造成的点击型 XSS;
  • 行数据在服务端统一归一化:label最长 60 字符、value最长 200 字符(均会剥离 emoji),每插件最多 12 行;
  • 每个 provider 在 5 秒超时内运行,出错或超时的插件被直接跳过,绝不拖垮整页。

调用插件需要hook:journal-entry-provider权限,hook 契约详见 Plugin-Development.md。

公开分享

你可以用只读公开链接分享整本日记。生成链接时可独立开关三个区块的可见性:

开关可见内容
Timeline条目与故事(entries and stories)
Gallery照片(photos)
Map地图

访客只能看到你开启的区块,且无需 TREK 账号。对应的共享设置接口为POST /api/journeys/:id/share-link(参数share_timeline/share_gallery/share_map,见 journey.controller.ts),读取 token 同样需要具备日记访问权限。

公开读取走独立的无鉴权通道GET /api/public/journey/:token(server/src/nest/journey/journey-public.controller.ts):每个路由都以分享 token 校验为前提(校验失败统一 404),照片按thumbnail/original两种规格流式输出;本地资源会做path.basename归一化并限定在uploads/journey目录内(防目录穿越),immich/synology提供商的资源则通过各自的服务代理流式传输。关于独立的日记分享 token 机制,可进一步阅读 Public-Share-Links.md。

权限与协作者模型

从服务端代码可以提炼出 Journey 的三级权限模型(journeyService.ts):

  • Owner(创建者):可以修改日记级设置(标题、副标题、封面、状态),管理行程关联、协作者与分享链接;
  • Editor(协作者角色为editor):可以新增、编辑、删除条目与照片;
  • Viewer(协作者角色为viewer):可以查看,不能编辑。

协作者通过POST /api/journeys/:id/contributors添加并指定roleeditor|viewer),相关请求体契约见 journey.schema.ts 的journeyContributorRequestSchema。所有条目变更都会通过 WebSocket 以journey:entry:created/journey:entry:updated/journey:entries:reordered等事件广播给日记的所有者与协作者(broadcastJourneyEvent),实现多人实时协作;x-socket-id头用于排除发起者自身的回放。

相关文档

  • Addons-Overview.md —— Addon 体系总览
  • Admin-Addons.md —— 在管理后台启用 Journey
  • Public-Share-Links.md —— 公开分享与分享 token 细节
  • Plugin-Development.md ——journalEntryProvider插件 hook 开发指南

【免费下载链接】TREKA self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.项目地址: https://gitcode.com/GitHub_Trending/nomad22/TREK

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/15 17:58:40

跨境电商物流渠道选择与评估模型搭建指南

1. 跨境物流渠道选择的现实困境做跨境电商的朋友们应该都深有体会,每次打开物流服务商的报价单,密密麻麻的渠道选项简直让人眼花缭乱。空运专线、海运快船、邮政小包、商业快递...每种渠道的时效、价格、稳定性差异巨大,更别提不同国家、不同…

作者头像 李华
网站建设 2026/9/15 17:57:54

图覆盖与软件测试充分性:从控制流图到覆盖准则全解析

很多人刚开始学软件测试时,对“覆盖率”三个字的第一反应是:覆盖率越高,测试质量越好。这个想法方向没错,但很容易让一个人走进死胡同——因为只看百分比,根本说不清楚“缺口到底在哪里”。真正把测试思路打通、让我知…

作者头像 李华
网站建设 2026/9/15 17:56:03

douyin-downloader 完整指南:批量下载抖音无水印视频与主页作品

douyin-downloader 完整指南:批量下载抖音无水印视频与主页作品 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fallb…

作者头像 李华
网站建设 2026/9/15 17:54:14

DeepEval 怎么把 Qdrant 等向量数据库接入 RAG 评估流程?

DeepEval 怎么把 Qdrant 等向量数据库接入 RAG 评估流程? 【免费下载链接】deepeval The LLM Evaluation Framework 项目地址: https://gitcode.com/GitHub_Trending/de/deepeval 如果你的 RAG 系统用 Qdrant(或 PGVector)作为检索引擎…

作者头像 李华