Rich Menu
Overview
A rich menu is the image-based menu displayed beneath the LINE chat window. This is one of the most complex modules in the system, because it handles several concerns at once: managing the image and its tappable areas (the layout), linking and unlinking menus for users on LINE, setting the default menu, managing aliases for multi-level or tabbed menus, archiving previous versions, generating per-button tracking links (tracking tokens), and computing tap statistics.
Binding a menu to an audience requires reading a CSV of LINE users from object storage through the CSV engine.
Business Flow
Creating and publishing
POST /api/rich-menuaccepts multipart/form-data for the image upload, the layout, and the action assigned to each area.- Link actions are rewritten into token-based tracking URLs.
- A duplicate name returns error
RMN_001.
- The service calls LINE through the line message api in sequence:
callCreateRichMenuOnLineOA, thensetRichMenuImage, and where an alias is configured, the alias is created or updated. - Targeting works in two ways:
- Whole OA:
setDefaultRichMenu. - Specific audience:
processRichMenuAudiencereads the audience CSV throughDeps.CSVEngineand callslinkRichMenuIdToUsersin batches, recording results inrich_menu_member.
- Whole OA:
- The previous version is archived into
rich_menu_archivebefore being replaced.
Day-to-day use
GET /api/rich-menureturns a paginated list, andGET /api/rich-menu/find-all-objectprovides a dropdown shape with no policy attached.GET /api/rich-menu/:idreturns the detail view including actions and statistics.PUT /api/rich-menu/:idaccepts form-data for updates. It follows the same flow as creation but must also reconcile the existing menu on LINE.POST /api/rich-menu/:id/recalculate-stattriggers a recomputation of tap statistics by publishing to thecalculate_rich_menu_stat_itemqueue for a worker to process.DELETE /api/rich-menu/:idsoft-deletes and also callsdeleteRichMenuon LINE, whileDELETE /api/rich-menu/:id/hard?confirm=truedeletes permanently and is restricted to super admins.- Event-driven menu switching for individual users happens through the
line_change_richmenuqueue.
Key Files & Functions
The code lives in internal/modules/richmenu/.
| File | Role |
|---|---|
controller.go | Route registration |
service.go | Core logic, with the rich_menu, action, member, and archive repositories folded in |
create_update.go | The create/update flow, including processRichMenuAudience |
update.go | The flow for updating an already-published menu |
tracking_adapter.go | Converts link actions into tracking token URLs |
util.go, js.go, dto.go | Helpers and DTOs |
| Method | Route | Handler | Policy (metadata) |
|---|---|---|---|
| GET | /api/rich-menu | ct.findAll | readAll rich-menu |
| GET | /api/rich-menu/find-all-object | ct.findAllObject | — |
| GET | /api/rich-menu/:id | ct.findById | read rich-menu |
| POST | /api/rich-menu | ct.create | create rich-menu |
| POST | /api/rich-menu/:id/recalculate-stat | ct.recalcStat | read rich-menu |
| PUT | /api/rich-menu/:id | ct.update | update rich-menu |
| DELETE | /api/rich-menu/:id | ct.delete | delete rich-menu |
| DELETE | /api/rich-menu/:id/hard | ct.hardDelete | auth.SuperAdmin() |
Every route is wrapped in modulegate.ModuleGate(d, "rich-menu").
Connections to Other Services
- Permissions —
ModuleGate("rich-menu")is the guard that actually applies;PolicyModuleRichMenuis metadata only, and hard delete additionally requiresauth.SuperAdmin(). - Tables —
rich_menu,rich_menu_action,rich_menu_member,rich_menu_archive,tracking_token,audience,line_oa - RabbitMQ — the
line_change_richmenuandcalculate_rich_menu_stat_itemqueues - Cross-module —
linemessageapi.Service,linemessageapi.RichMenuService,tracking.TrackingTokenService, andDeps.CSVEnginefor reading audience CSVs - Note — the LINE OA record is read by querying
entities.LineOadirectly rather than throughlineoa.Service.FindByID, because this module needs thechannelAccessTokenthat the service strips out. - Error code —
RMN_001for a duplicate name