---
title: "Offlinery"
canonical: https://wavect.io/zh/case-studies/offlinery/
language: zh
description: "Offlinery 是一款反向滑屏文化的约会应用。专有算法呈现真正想线下见面的附近用户。已上线 iOS 与 Android。2025 年停运。"
image: "https://wavect.io/img/general/bak/open_graph_preview.jpg"
---

[← 所有案例研究](/zh/case-studies/)

![Offlinery logo](/img/clients/bak/offlinery.png)

案例研究 · 2024-2025

Offlinery

# 在敌对的移动操作系统上做实时、亚秒级匹配，端到端构建。

Offlinery 是一款反向滑屏文化的约会应用。专有算法呈现真正想线下见面的附近用户。已上线 iOS 与 Android。2025 年停运。

AI 全栈构建 2024-2025 已关闭

速览

项目类型 全栈构建

行业 移动 / 实时地理

阶段 种子轮前

年份 2024-2025

技术栈

React Native / Expo NestJS TypeORM Typescript Firebase Sentry

状态 已关闭

实例 [github.com/wavect/offlinery](https://github.com/wavect/offlinery)

## 概览

Offlinery 是一款反向滑屏文化的约会应用。专有算法呈现真正想线下见面的附近用户。已上线 iOS 与 Android。2025 年停运。

## 挑战是什么？

在分钟级窗口内把合适的附近用户推出来，让线下接触真的发生。两层叠加的约束：做亚秒级邻近与双向意图过滤的专有算法，以及在 iOS 与 Android 一失去兴趣就杀掉后台任务的条件下保持可靠。

## 我们做了什么决定？

### 锁屏状态下的连续、精确定位。

> 锁屏手机上拿连续、精确位置极其困难。系统为省电关后台进程。隐私模型在匹配最需要触发的时刻反复打断用户。在一个正在主动压制你的 OS 面前保持准确，同时让提示足够诚实，让用户依然愿意说是。

## 我们做了什么

两个手机 OS 都在主动压制实时匹配。我们把每一层都围绕基于运动的触发、智能延后、优雅降级来设计，OS 反向施压之前就把兜底路径备好。基于 PostGIS 的自研邻近引擎。React Native + Expo，硬化后的后台地理定位、推送、错误追踪、国际化。NestJS + TypeORM 后端，JWT、限流、事务邮件、Expo 推送。完整的 App Store 与 Play Store 上线。在不愿合作的硬件上、移动手机上的亚秒级邻近。

## 成果

### 实时 - 自研匹配引擎
- **Category:** 规模

### 亚秒级 - 邻近查询
- **Category:** 规模

想让你的项目也拿到这样的成果？

范围固定。期限固定。已签 SoW。

[联系我们](/zh/contact/)

## On Reel

![应用内演示。](/img/yt/mNqTMDJxd-A.jpg?v=9a42d13ee9be) 应用内演示。

## 精选界面

![Offlinery 截图 1](/img/case-studies/offlinery/001_hu_a987c7fb34c20161.webp)

![Offlinery 截图 2](/img/case-studies/offlinery/002_hu_45ffadc04493dd16.webp)

![Offlinery 截图 3](/img/case-studies/offlinery/003_hu_c2f7956f79790f80.webp)

![Offlinery 截图 4](/img/case-studies/offlinery/004_hu_2957c12d699ae59c.webp)

![Offlinery 截图 5](/img/case-studies/offlinery/005_hu_47be90ea2357aec6.webp)

![Offlinery 截图 6](/img/case-studies/offlinery/006_hu_63b7b7dbb80cb777.webp)

![Offlinery 截图 7](/img/case-studies/offlinery/007_hu_8eb3c450ae8da0a0.webp)

![Offlinery 截图 8](/img/case-studies/offlinery/008_hu_ca9b7bbd14ca4f82.webp)

## Architecture & Flows

### 生产环境基础设施

后端、Postgres+PostGIS、分析与日志托管在 Google Cloud。等候名单部署在 Vercel。Sentry 负责崩溃报告，MailChimp 负责事务邮件。

该图展示了简化的高层架构，省略了机密的实现与安全细节。

### 匹配通知流程

```
sequenceDiagram
  autonumber
  participant U as User App
  participant API as NestJS API
  participant DB as PostGIS
  participant N as Expo Push
  U->>API: location heartbeat
  API->>DB: find nearby candidates
  DB-->>API: matches (geo + filters)
  API->>N: notify both users
  N-->>U: silent push
```

心跳 → 邻近查询 → 双向推送。后台地理定位驱动整个循环；API 保持无状态。

## 经验总结

实时线下匹配的生死，取决于被 OS 主动扼杀的信号。一半是算法，一半是平台硬仗。基于运动的触发、智能延后、优雅降级、精度被压制时的兜底。在移动中的手机上，速度与韧性是同一个问题。

## 技术栈

- [React Native / Expo](https://expo.dev/)
- [NestJS](https://nestjs.com/)
- [TypeORM](https://typeorm.io/)
- [Typescript](https://www.typescriptlang.org/)
- [Firebase](https://firebase.google.com/)
- [Sentry](https://sentry.io/)

## 标签

- AI / 匹配
- 实时地理
- 移动应用

## 链接与资源

[GitHub 仓库](https://github.com/wavect/offlinery)

相关服务

- [移动应用开发](/zh/services/mobile-apps/)
- [AI Agent 与产品](/zh/services/artificial-intelligence/)
- [定制软件开发](/zh/services/software-development/)

客户引用与商标

Wavect 发布这些案例研究，讲述的是我们自己的工作。客户名称、标识和商标归各自所有者所有，在此仅用于标识相关工作，并不表示任何合作、赞助或背书关系。每个案例描述的是 Wavect 在一项更大工作中的自身贡献，客户自己的团队以及其他服务商也共同促成了这些结果。归属于客户的数据是该客户自行提供的数字。关于我们工作的数据来自我们在页面所示核查日期的记录，可按要求提供证据。评价均转载自各卡片所标注的平台，并链接至原始来源。我们不公开任何合作的商业条款，产品截图的权利仍属于所展示的客户。如果你被列在此处，希望更正、修改或撤下相关内容，请写信给我们，我们会处理： [office@wavect.io](mailto:office@wavect.io)

### [PromptID](/zh/case-studies/promptid/)

PromptID 是一个面向雇主和高校的 AI 原生教育科技平台。它通过分析思维过程而非奖励死记硬背来考察学习者。底层驱动评估引擎的是一套专有算法。

## Structured Data

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@id": "https://wavect.io/#organization",
      "@type": [
        "Organization",
        "ProfessionalService",
        "LocalBusiness"
      ],
      "employee": [
        {
          "@id": "https://wavect.io/team/kevin-riedl/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Kevin Riedl",
          "url": "https://wavect.io/team/kevin-riedl/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        },
        {
          "@id": "https://wavect.io/team/christof-jori/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Christof Jori",
          "url": "https://wavect.io/team/christof-jori/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        }
      ],
      "founder": [
        {
          "@id": "https://wavect.io/team/kevin-riedl/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Kevin Riedl",
          "url": "https://wavect.io/team/kevin-riedl/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        },
        {
          "@id": "https://wavect.io/team/christof-jori/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Christof Jori",
          "url": "https://wavect.io/team/christof-jori/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        }
      ],
      "legalRepresentative": [
        {
          "@id": "https://wavect.io/team/kevin-riedl/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Kevin Riedl",
          "url": "https://wavect.io/team/kevin-riedl/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        },
        {
          "@id": "https://wavect.io/team/christof-jori/#person",
          "@type": "Person",
          "jobTitle": "Managing Director",
          "name": "Christof Jori",
          "url": "https://wavect.io/team/christof-jori/",
          "worksFor": {
            "@id": "https://wavect.io/#organization",
            "@type": [
              "Organization",
              "ProfessionalService",
              "LocalBusiness"
            ]
          }
        }
      ],
      "name": "Wavect GmbH",
      "subjectOf": {
        "@id": "https://wavect.io/verified-claims.json#dataset",
        "@type": "Dataset",
        "name": "Wavect verified publication claims",
        "url": "https://wavect.io/verified-claims.json"
      },
      "url": "https://wavect.io/"
    },
    {
      "@id": "https://wavect.io/team/kevin-riedl/#person",
      "@type": "Person",
      "jobTitle": "Managing Director",
      "name": "Kevin Riedl",
      "sameAs": [
        "https://www.wikidata.org/wiki/Q139796365",
        "https://www.linkedin.com/in/wsdt",
        "https://github.com/wsdt"
      ],
      "url": "https://wavect.io/team/kevin-riedl/",
      "worksFor": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      }
    },
    {
      "@id": "https://wavect.io/team/christof-jori/#person",
      "@type": "Person",
      "jobTitle": "Managing Director",
      "name": "Christof Jori",
      "sameAs": [
        "https://www.wikidata.org/wiki/Q139796367",
        "https://www.linkedin.com/in/jocr77/",
        "https://github.com/jo-chris"
      ],
      "url": "https://wavect.io/team/christof-jori/",
      "worksFor": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      }
    },
    {
      "@id": "https://wavect.io/#website",
      "@type": "WebSite",
      "inLanguage": [
        "en",
        "de",
        "es",
        "zh"
      ],
      "name": "Wavect",
      "potentialAction": {
        "@type": "SearchAction",
        "query-input": "required name=search_term_string",
        "target": {
          "@type": "EntryPoint",
          "urlTemplate": "https://wavect.io/search/?q={search_term_string}"
        }
      },
      "publisher": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      },
      "url": "https://wavect.io/"
    },
    {
      "@id": "https://wavect.io/zh/case-studies/offlinery/#webpage",
      "@type": "WebPage",
      "dateModified": "2026-05-21",
      "inLanguage": "zh",
      "isPartOf": {
        "@id": "https://wavect.io/#website",
        "@type": "WebSite"
      },
      "lastReviewed": "2026-05-21",
      "speakable": {
        "@type": "SpeakableSpecification",
        "cssSelector": [
          ".faq-question",
          ".faq-answer"
        ]
      },
      "url": "https://wavect.io/zh/case-studies/offlinery/"
    }
  ]
}
```

```json
{
  "@context": "https://schema.org",
  "@id": "https://wavect.io/zh/case-studies/offlinery/#case-study",
  "@type": "Article",
  "about": {
    "@id": "https://wavect.io/zh/case-studies/offlinery/#project",
    "@type": "CreativeWork",
    "creator": {
      "@id": "https://wavect.io/#organization",
      "@type": [
        "Organization",
        "ProfessionalService",
        "LocalBusiness"
      ]
    },
    "dateCreated": "2024-01-01",
    "description": "全栈构建 engagement",
    "funder": {
      "@type": "Organization",
      "name": "Offlinery"
    },
    "name": "Offlinery",
    "subjectOf": {
      "@id": "https://wavect.io/zh/case-studies/offlinery/#case-study",
      "@type": "Article"
    },
    "url": "https://github.com/wavect/offlinery"
  },
  "abstract": "在分钟级窗口内把合适的附近用户推出来，让线下接触真的发生。两层叠加的约束：做亚秒级邻近与双向意图过滤的专有算法，以及在 iOS 与 Android 一失去兴趣就杀掉后台任务的条件下保持可靠。",
  "author": {
    "@id": "https://wavect.io/#organization",
    "@type": [
      "Organization",
      "ProfessionalService",
      "LocalBusiness"
    ]
  },
  "datePublished": "2024-01-01",
  "description": "Offlinery 是一款反向滑屏文化的约会应用。专有算法呈现真正想线下见面的附近用户。已上线 iOS 与 Android。2025 年停运。",
  "headline": "Offlinery, Wavect Case Study",
  "image": "https://wavect.io/img/clients/bak/offlinery.png",
  "inLanguage": "zh",
  "isPartOf": {
    "@type": "CollectionPage",
    "name": "Wavect Case Studies",
    "url": "https://wavect.io/zh/case-studies/"
  },
  "keywords": "AI / 匹配, 实时地理, 移动应用, React Native / Expo, NestJS, TypeORM, Typescript, Firebase, Sentry",
  "mainEntityOfPage": "https://wavect.io/zh/case-studies/offlinery/",
  "mentions": [
    {
      "@type": "DefinedTerm",
      "name": "React Native / Expo",
      "url": "https://expo.dev/"
    },
    {
      "@type": "DefinedTerm",
      "name": "NestJS",
      "url": "https://nestjs.com/"
    },
    {
      "@type": "DefinedTerm",
      "name": "TypeORM",
      "url": "https://typeorm.io/"
    },
    {
      "@type": "DefinedTerm",
      "name": "Typescript",
      "url": "https://www.typescriptlang.org/"
    },
    {
      "@type": "DefinedTerm",
      "name": "Firebase",
      "url": "https://firebase.google.com/"
    },
    {
      "@type": "DefinedTerm",
      "name": "Sentry",
      "url": "https://sentry.io/"
    }
  ],
  "publisher": {
    "@id": "https://wavect.io/#organization",
    "@type": [
      "Organization",
      "ProfessionalService",
      "LocalBusiness"
    ]
  },
  "url": "https://wavect.io/zh/case-studies/offlinery/",
  "video": [
    {
      "@id": "https://wavect.io/zh/case-studies/offlinery/#video-mNqTMDJxd-A",
      "@type": "VideoObject",
      "description": "应用内演示。",
      "duration": "PT59S",
      "embedUrl": "https://www.youtube-nocookie.com/embed/mNqTMDJxd-A",
      "inLanguage": "zh",
      "name": "Dating Apps are Out: Meet In Real Life Instead",
      "publisher": {
        "@id": "https://wavect.io/#organization",
        "@type": [
          "Organization",
          "ProfessionalService",
          "LocalBusiness"
        ]
      },
      "thumbnailUrl": "https://i.ytimg.com/vi/mNqTMDJxd-A/maxresdefault.jpg",
      "uploadDate": "2024-10-30T00:00:00+00:00",
      "url": "https://www.youtube.com/watch?v=mNqTMDJxd-A"
    }
  ]
}
```

```json
{
  "@context": "https://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement": [
    {
      "@type": "ListItem",
      "item": "https://wavect.io/zh/",
      "name": "首页",
      "position": 1
    },
    {
      "@type": "ListItem",
      "item": "https://wavect.io/zh/case-studies/",
      "name": "案例研究",
      "position": 2
    },
    {
      "@type": "ListItem",
      "item": "https://wavect.io/zh/case-studies/offlinery/",
      "name": "Offlinery",
      "position": 3
    }
  ]
}
```
