Skip to main content

Broadcast Campaign Management

Overview

A campaign sends a rich message out to LINE friends according to a chosen target group, with optional scheduling. This module covers the whole lifecycle: creating the campaign, selecting recipients (either the entire OA or a specific audience), and reviewing the tracking report that shows who opened the message and which links they clicked.

The actual sending does not happen in this API. The API only persists the campaign and publishes a job to RabbitMQ for a worker to deliver, which keeps the request fast and makes large sends practical.

Business Flow

Creating and sending

  1. GET /api/campaign/recipients-dropdown supplies cms-web with the recipient options, such as the available audiences.

  2. POST /api/campaign accepts multipart/form-data and stores the campaign: its name, the rich message to send, the recipient type (broadcast to the whole OA, or multicast to an audience), and the send time.

  3. The service checks whether the message contains merge tags such as {{name}} using hasMergeTags, because messages with merge tags must be personalised and sent per recipient.

  4. The job is published to RabbitMQ:

    • No audience specified goes to the line_broadcast_rich_message queue.
    • An audience specified goes to the line_multicast_rich_message queue.
    • Scheduled or aggregate processing goes to the process_campaign queue.

    The payload is built byte-identically to the TypeScript version, preserving key order, because the original worker still consumes it.

  5. The worker picks up the job and calls the LINE Messaging API.

Tracking results

  1. GET /api/campaign/campaign-tracking/:id summarises sends, opens, and clicks, computed with raw SQL aggregation.
  2. GET /api/campaign/campaign-tracking/:id/users drills into the list of people who performed a given action, accepting the actionType, originalUrl, richMessageIndex, page, and limit query parameters.
  3. Links inside a campaign are rewritten into tracking URLs by the RedirectService on the worker side, which differs from rich menus and their token-based approach.

General management

  1. GET /api/campaign returns a paginated list; the repository spreads the audience's target values into each result.
  2. GET /api/campaign/:id and PUT /api/campaign/:id view and update a campaign, while DELETE /api/campaign/:id soft-deletes it.
  3. DELETE /api/campaign/:id/hard?confirm=true deletes permanently and is restricted to super admins.

Key Files & Functions

The code lives in internal/modules/campaign/, consisting of controller.go, service.go, tracking.go, and dto.go.

MethodRouteHandlerPolicy (metadata)
GET/api/campaignct.findAllreadAll campaign
GET/api/campaign/recipients-dropdownct.recipientsDropdownreadAll campaign
GET/api/campaign/:idct.findByIDread campaign
GET/api/campaign/campaign-tracking/:idct.trackingByIDread campaign
GET/api/campaign/campaign-tracking/:id/usersct.trackingUsersByIDread campaign
POST/api/campaignct.createcreate campaign
PUT/api/campaign/:idct.updateupdate campaign
DELETE/api/campaign/:idct.deletedelete campaign
DELETE/api/campaign/:id/hardct.hardDeleteauth.SuperAdmin()

Every route is wrapped in modulegate.ModuleGate(d, "campaign").

One function worth knowing is hasMergeTags, which uses the JavaScript-equivalent regex /\{\{[^}]+\}\}/ — two closing braces must genuinely be adjacent for it to match.

Connections to Other Services

  • PermissionsModuleGate("campaign") is the guard that actually applies; PolicyModuleCampaign is metadata only, and hard delete additionally requires auth.SuperAdmin().
  • Tablescampaign, rich_message, audience, line_user, tracking_line_users, line_oa
  • RabbitMQ — the process_campaign, line_broadcast_rich_message, line_multicast_rich_message, and campaign_click_trigger queues
  • Redis — caches both the list and the detail views
  • Cross-modulelinemessageapi for validating and formatting messages, plus richmessage and audience
  • Related modules — rich messages, audience management, and trigger rules, which treat campaign clicks as a trigger source