Skip to main content

Thank-you Page & Rich Menu Switching After Submission

Overview

These are the side effects that fire after answers have been saved successfully — the mechanism that turns a form into a tool for converting casual visitors into members: switching the user's rich menu and/or changing line_user.user_type from guest to member.

The critical design decision: the rich menu switch is triggered server-side on successful submit, not by a button press on the thank-you page. If the client issued the call itself, anyone holding the form URL could POST to change their own rich menu.

Business Flow

Two independent mechanisms can fire during a single submission, producing two messages on the same queue. Whichever is processed last wins the user's menu — that ordering is not guaranteed, and the CMS documents the limitation for whoever configures the form.

Mechanism 1 — the form-level convert_to_member flag

  1. Publish the payload {lineOaId, type:"setRichMenuMemberByLineUserId", userType:"member", userId} to the line_change_richmenu queue.

  2. Errors from this publish are propagated, failing the request — unlike mechanism 2, which is purely best-effort.

  3. Then issue UPDATE line_user SET user_type='member' when the user is not already a member. This is best-effort; failures are only logged.

    Why the explicit UPDATE is needed: the userType field in the payload is never read by any worker, on either the NestJS or the Go side. The result was a rich menu that changed while user_type stayed guest — a QA defect fixed by updating the column here.

Mechanism 2 — a thank_you.action of type rich_menu

Configured per form on the CMS thank-you page.

  1. Read the form_builder.thank_you jsonb defensively: it must have mode == "custom", action.type == "rich_menu", and action.rich_menu_id as an integer of 1 or greater. Anything missing logs a warning and skips, because the column is raw JSON that may have been written before the CMS had a validator.
  2. A lineUserId is required. The CMS already enforces require_line_login at save time; this is the runtime safety belt.
  3. If fb.LineOaID is 0 or less, log a warning and skip.
  4. Re-check ownership at publish time. This is the only gate on this path, because the worker-side handler looks up the rich menu without filtering on line_oa_id and then binds it using the token of whichever OA the payload names. The check is SELECT 1 FROM rich_menu WHERE id=$1 AND line_oa_id=$2 AND deleted_date IS NULL AND status='active', where $2 comes from the form's own OA, never from the request.
  5. Publish {type:"setRichMenuByTriggerRule", lineOaId, userId, richMenuId} to the same queue. setRichMenuByTriggerRule is used rather than setRichMenuMemberByLineUserId because the latter handler ignores the id it is given and picks the OA's member-type menu with a query of its own.
  6. Every failure is a warning only. The response has already been recorded, the user did nothing wrong, and the menu switch is a bonus.
  7. This mechanism never touches line_user.user_type, and must not: its job is to change the menu, not membership status.

What the web thank-you page uses

The entire thank_you object is already returned in the formBuilderInfo section of the Submit response and from GET /form-builder/:hash, so the web app renders the close, oa_chat, and url buttons plus the auto-close countdown on its own. The botBasicId used by the oa_chat button comes from the LINE OA lookup step.

Key Files & Functions

This feature has no route of its own — it is step 10 of the Submit pipeline.

FileFunctions
internal/formsubmission/thankyou.gothankYouRichMenuID(raw), (*Service).publishThankYouRichMenu, (*Service).warn
internal/formsubmission/service.goThe convertToMember(fb) block inside Submit (publish plus UPDATE line_user), the convertToMember helper, ptrStringOrNil
internal/formsubmission/register.goDefines the richMenuQueue name, defaulting to line_change_richmenu

Connections to Other Services

  • RabbitMQ — the line_change_richmenu queue, consumed by line-management-worker-go via the setRichMenuMemberByLineUserId and setRichMenuByTriggerRule handlers.
  • Databaserich_menu (ownership check), line_user (the user_type column), and form_builder (the thank_you jsonb plus the convert_to_member and require_line_login flags).
  • Related features — invoked from the form submission pipeline; all configuration originates in the CMS Form Builder screen.
  • client-web — corresponds to the form-thank-you feature, and relates to form-fill.