Skip to main content

Friend Source Tracking (Friend Track)

Overview

Friend Track answers the question "where did this friend come from?" by creating tracking campaigns whose add-friend invitation links carry a ref code per channel. When someone follows the OA through one of those links, the system records which ref brought them in.

The module is built for large ref volumes, so refs can be uploaded as a CSV — one ref per staff member, per branch, or per printed QR code across thousands of them — and the generated links can be downloaded back out as a CSV.

Business Flow

Creating and managing campaigns

  1. POST /api/friend-track creates a tracking campaign with a name, an active period and ref settings.
  2. GET /api/friend-track returns a paginated campaign list, filterable via FilterFriendTrackCampaignDto.
  3. PUT /api/friend-track/:id updates a campaign; DELETE /api/friend-track/:id removes it.

Handling refs at scale

  1. POST /api/friend-track/:id/refs/upload accepts a multipart CSV of refs. The system uses batched raw SQL of the INSERT ... ON CONFLICT form to handle high volumes while preventing duplicates.
  2. GET /api/friend-track/:id/refs/status is polled to see whether the upload is still running, finished, or failed.
  3. GET /api/friend-track/:id/refs/download downloads every link and ref as a CSV, streamed directly to the response rather than being buffered in memory.

Reporting

  1. GET /api/friend-track/:id returns campaign details.
  2. GET /api/friend-track/:id/report reports how many friends each ref brought in, filterable by date range or ref via FilterFriendTrackReportDto. Note that user counts in this report are computed with getRawMany().length rather than COUNT(), matching the legacy behaviour.
  3. When someone adds the OA as a friend through a tracked link, webhook-go records the event in friend_track_event, linked to both the friend_track_campaign and the ref used.

Key Files & Functions

The code lives in internal/modules/friendtrack/, split across controller.go, service.go and dto.go.

MethodRouteHandlerPolicy
GET/api/friend-trackct.findAllreadAll friend-track
GET/api/friend-track/:idct.findByIDread friend-track
GET/api/friend-track/:id/reportct.getReportread friend-track
GET/api/friend-track/:id/refs/downloadct.downloadRefLinksread friend-track
GET/api/friend-track/:id/refs/statusct.getRefUploadStatusread friend-track
POST/api/friend-trackct.createcreate friend-track
POST/api/friend-track/:id/refs/uploadct.uploadRefsupdate friend-track
PUT/api/friend-track/:idct.updateupdate friend-track
DELETE/api/friend-track/:idct.deletedelete friend-track

Every route is wrapped in modulegate.ModuleGate(d, "friend-track").

note

PolicyModuleFriendTrack is also used as the policy metadata for the GET /api/apps and PUT /api/apps/:appId endpoints — behaviour inherited from the legacy system.

Connections to Other Services

  • Permissions — requires ModuleGate("friend-track") and carries PolicyModuleFriendTrack as metadata.
  • Tablesfriend_track_campaign, friend_track_event, line_user and line_oa.
  • Storage — ref CSV files are kept in object storage.
  • Event writer — line-management-webhook-go records the events when follow events arrive.
  • Related modules — LINE User Management, Tracking Link and All Friend Listing.