SolveMY
面向马来西亚的三语本地服务平台,覆盖 Android、iOS 和网页。用户发布需求,经过认证的服务商和商家报价并在线沟通。
- 角色
- CTO 兼一线工程负责人
- 类型
- 我自己的产品
- 时间
- 2026.01 至今
- 状态
- 网页与 Google Play 已上线 · 已提交 App Store 审核
SolveMY 用英文、马来文和中文,把用户与经过认证的服务商和商家(维修、清洁、运输、美容、补习等)连接起来。整套系统由我搭建并运维:基于 PostgreSQL 与 PostGIS(地理位置搜索)的分层 Go REST API,带服务端渲染 SEO 页面的 Next.js 16 网站,以及 Android 和 iOS 的 Flutter 应用。生产环境运行在自管的 Hetzner 服务器上(Docker、nginx、Let's Encrypt),存储和 CDN 使用 Cloudflare R2,网站部署在 Vercel;预发与生产完全隔离,每晚加密备份,CI 质量门禁和监控齐全。
问题
在马来西亚想找一个靠谱的本地服务商,往往得用三种语言到处打听。SolveMY 是一个服务平台:用户发布需求,经过认证的服务商和商家报价并在线沟通。它需要一个网站、Android 和 iOS 应用、一个管理后台,以及背后的运维,却没有工程团队。只有我。
难点在哪里
- 三个客户端(网页、Android、iOS)对接同一套 API,支持英文、马来文和中文。
- 位置搜索:对服务商、商家和需求做半径搜索和最近优先排序。
- 会话可以按设备吊销;聊天功能在旧版 App 上也要表现正常。
- 两个应用商店对定价和支付的规则不同,却要用同一套代码来满足。
- 一个人运维生产环境:部署、备份、监控和故障响应。
架构
一个分层的 Go 服务(领域、服务、仓储、HTTP),基于 PostgreSQL 与 PostGIS。一个带服务端渲染页面的 Next.js 网站。一个面向 Android 和 iOS 的 Flutter 应用。生产环境运行在自管服务器上,使用 Docker、nginx 和 Let's Encrypt,存储和备份用 Cloudflare R2,网站部署在 Vercel。预发与生产相互隔离。
- Go
- chi
- pgx
- PostgreSQL 16
- PostGIS
- Next.js 16
- TanStack Query
- NextAuth
- Flutter
- Riverpod
- Docker
- nginx
- Cloudflare R2
- Vercel
- Firebase (FCM / APNs)
- Twilio
- Google Maps Platform
- Claude API
- GitHub Actions
- Sentry
我做了什么
CTO 兼一线工程负责人
- 01设计分层 Go API(领域、服务、仓储、HTTP),使用 chi 与 pgx:31 个领域服务、235 个接口、1,006 个测试函数,CI 中有 golangci-lint 与 gofmt 门禁。
- 02基于 PostGIS 的半径搜索和最近优先排序,覆盖服务商、商家和需求;App 与网站上的商家地图支持图钉聚合。
- 03会话安全:短期访问令牌 + 轮换的不透明刷新令牌,检测到重复使用即吊销整个令牌族;按设备管理会话,90 天滑动有效期。
- 04基于 Claude API 的 AI 搜索:用英文、马来文或中文的自然语言,或一张照片,匹配到对应类别和子领域。
- 0548 个页面的 Flutter 应用已上架 Google Play 并提交 App Store;同一套代码同时满足两大应用商店的定价规则。
- 06在 Hetzner 上运维生产环境(Docker、nginx、Let's Encrypt);预发环境作为真正的金丝雀;每晚 AES-256 加密备份到 Cloudflare R2;Sentry、在线监控与证书到期告警。
决策与挑战
一个可部署单元,分层清晰
31 个领域服务、235 个接口都在同一个 Go 服务里。一个工程师就能把它想清楚,边界也足够清晰,以后如果有团队需要,可以再拆分。
用 PostgreSQL 加 PostGIS 做搜索
半径查询和最近优先查询直接在已经存着数据的数据库里执行,不必再多运维一套独立的搜索系统。
轮换刷新令牌,并检测重复使用
访问令牌有效期短,刷新令牌不透明并且会轮换。旧令牌一旦被再次使用,整个令牌族都会被吊销。
预发环境是真正的金丝雀,备份每晚加密
每次发布都先经过隔离的预发环境。备份用 AES-256 加密,每晚传到服务器之外。
CI 中的质量门禁
1,006 个测试函数,代码检查和格式检查不通过就不能合并。
值得一讲的故事
推送通知在所有设备上静默失效了十天。我从 FCM 错误码追到每条消息都附带的 APNs 配置块,改为仅 iOS 携带,去除重复推送,并新增可按用户证明送达的管理接口。
一张 TLS 证书在域名迁移后续期失败并过期,导致 API “宕机”。我重新签发证书,用 nginx reload 钩子恢复自动续期,并增加每日到期告警,避免再次无声发生。
并发安全的“每人每天只计一次”统计:在 CTE 中对日表执行 INSERT … ON CONFLICT DO NOTHING,重复点击不会重复计数。
兼容旧版 App 的“对所有人删除”聊天功能:软删除,被删除的消息以纯文本加标记返回。
经过验证的结果
- 网页和 Google Play 已上线,并已提交 App Store。
- 235 个 REST 接口、31 个领域服务、1,006 个 Go 测试函数和 116 个 SQL 迁移。
- 48 个 App 页面和 75 个网页路由,支持三种语言。
- 生产环境由一个工程师运维,有监控、告警和每晚的加密备份。
数字
- 235 个 REST 接口
- 1,006 个 Go 测试函数
- 116 个 SQL 迁移
- 48 个 App 页面 · 75 个网页路由
- 3 种语言
- 8 个月 834 次提交
这对客户说明了什么
在我的经历里,这是最接近与创始人合作的一次:一个工程师把产品从空仓库做到上线的应用,包括那些不起眼的部分。我说的“对技术负全责”,指的就是这个。
告诉我你在做什么。
几句话就够了:做的是什么、目前进展到哪一步、需要工程师做什么。表单就在下方,也可以直接发邮件。我会在两个工作日内回复。