POST /users/refer.json
狀態
已補 action / routing / PHPUnit;staging legacy / new 實測待補。
這支只負責從推薦好友頁寄出推薦邀請 email。它不查推薦列表、不新增 user_referrals、不發 referral bonus、不修改 wallet;推薦關係是在被推薦人後續註冊流程依 referral_hash 寫入。
Legacy 流量
2026-07-08 查 legacy 正式機 ip-10-5-2-242 的 /var/log/apache2/get-lancer_access.log*:
| Method / path | status | count |
|---|---|---|
POST /users/refer.json | 200 | 2 |
OPTIONS /users/refer.json | 200 | 2 |
GET /users/refer.json | 200 | 1 |
GET /users/refer.json 是同名 web route,會走 cookie / redirect 流程;JSON API 搬移先以 POST /users/refer.json 為範圍。
操作路徑
Web 專家推薦好友頁:
https://staging.pro360.com.tw/dashboard/free-credits/referral前端在此頁按「發送Email」後,popup 送出 email,呼叫 POST /users/refer.json。
| 檔案 | 行為 |
|---|---|
/Users/mattsu/Documents/Site/web-app/modules/components/dashboard/ProReferral.js:46-51 | onSendEmail(email) 呼叫 APIManager.sendReferrailEmail(email),成功後關閉 popup。 |
/Users/mattsu/Documents/Site/web-app/modules/utils/api-manager.js:3451-3461 | sendReferrailEmail(email) 讀 session token,FormData 塞 email,POST /users/refer.json。 |
Legacy 對照
| 項目 | legacy |
|---|---|
| web/json dispatcher | /Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/UsersController.php:4409-4412 |
| web cookie flow | /Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/UsersController.php:4414-4447 |
| JSON API action | /Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/UsersController.php:5668-5695 |
| email/event helper | /Users/mattsu/Documents/Site/get-lancer-php56/app/Model/User.php:852-890 |
| provider name helper | /Users/mattsu/Documents/Site/get-lancer-php56/app/Lib/QuoteUtil.php:48-60 |
| avatar helper | /Users/mattsu/Documents/Site/get-lancer-php56/app/Model/User.php:1526-1533 |
| event queue insert | /Users/mattsu/Documents/Site/get-lancer-php56/app/Model/EventQueue.php:15-28 |
| legacy email DO | /Users/mattsu/Documents/Site/get-lancer-php56/app/DataObject/Notification/EmailParameterDO.php:3-24 |
| legacy event DO | /Users/mattsu/Documents/Site/get-lancer-php56/app/DataObject/Notification/EventParameterDO.php:3-9 |
Legacy JSON 流程:
UsersController::refer()如果RequestHandler->prefers('json'),轉進refer_from_api()。- 預設 response 是
{"error":1,"message":"Invalid request"}。 - 必須是 JSON request 且
RestApiHelper::hasValidApiSession($this)通過。 - blocked user 會在 session helper 直接回 HTTP 403 +
{"error":5,"message":"Blocked User"}。 - 從 session 取得目前
user_id。 - 只有
POST會呼叫User::sendReferralEmail($user_id, $this->request->data['email'])。 - 呼叫後直接回
{"error":0,"message":"Success"}。
注意:legacy 有一段檢查 email 是否已註冊的程式,但整段被註解掉;搬移時不可加回「Email Existed」檢查。
sendReferralEmail 行為
User::sendReferralEmail($user_id, $email):
- 查
users+ containUserProfile.first_name、UserProfile.last_name。 - 只有找到 user 且
users.referral_hash非空,才寫event_queue。 - 建立一個 email task:
to = request emailtemplateKey = 204 Vendor Signup Invitation by ReferrallangId = User::getMultiLangId($user_id)replaceContent如下:
| key | legacy 來源 |
|---|---|
##ACTION_1_TEXT## | 空字串 |
##ACTION_1_URL## | 空字串 |
##ACTION_2_TEXT## | 空字串 |
##ACTION_2_URL## | 空字串 |
##PROVIDER_NAME## | QuoteUtil::getUserFullName($user) |
##REFERRAL_URL## | Configure::read('site.url') . '/pro?referral=' . users.referral_hash |
##EMAIL_SIGNATURE## | Configure::read('email.signature') |
##PROVIDER_PHOTO## | User::getUserAvatar($user_id) |
##MORE_URL## | EmailSrc::getMoreUrl() |
- 寫入
event_queue:event_key = Invite_Referralpriority = 5params.user_id = current user_id,legacy 實測 JSON 型別為 string。params.tasks[0] = email task
如果 user 不存在或 referral_hash 為空,legacy 不寫 queue;但 refer_from_api() 仍然回 {"error":0,"message":"Success"},因為 helper 沒回傳值也沒丟 exception。
New 對照
| 項目 | new |
|---|---|
| endpoint | Endpoint/V1/Users.php:1116-1158 已補 refer()。 |
| email/event formatter | Endpoint/V1/Users.php:1338-1377 已補 insertLegacyReferralInviteEvent()。 |
| provider name formatter | Endpoint/V1/Users.php:1379-1395 已補 legacy fallback。 |
| routing | Lib/Common/RouterRule/Mapping.php:112-115 已補 /users/refer.json explicit route。 |
| session helper | 可沿用 users migrated API 現有 legacy session resolver;要保留 blocked user 與 access side effect。 |
| provider name helper | Lib/Model/User.php:483-500 可重用基礎姓名組合;本 API 另補 legacy fallback:姓名空時用 username,仍空時用 User。 |
| user avatar helper | Lib/Model/User.php:505-512 已有 getUserAvatar(),SQL 與 legacy 對齊。 |
| language helper | Lib/Model/User.php:624-631 已有 getMultiLangId()。 |
| email task DO | Lib/DataObject/Task/EmailTaskDO.php:4-27 可對齊 legacy EmailParameterDO public 欄位。 |
| event task DO | Lib/DataObject/Task/EventTaskDO.php:4-15 可對齊 legacy EventParameterDO。 |
| event queue model | Lib/Model/EventQueue.php:19-31 與 legacy insertEvent() shape 一致。 |
EmailSrc::getMoreUrl() | Lib/Util/EmailSrc.php:384-388 與 legacy /pro/learn-more/ fixed URL 對齊。 |
Migration decision:
- 不直接呼叫
User::getReferralHash()來自動補users.referral_hash。legacysendReferralEmail()只檢查現有referral_hash,沒有在這支 API 補 hash;自動補會新增 legacy 沒有的 DB write。 - request log 保留
RouterV3generic log,不新增 endpoint duplicate full request log。body 只有 email,仍要維持既有 request log 過濾規則。
Request
必要 headers:
X-PRO360-Rest-Api-Key: <api key>
X-PRO360-User-Session-Token: <session id>
Content-Type: multipart/form-databody:
| 欄位 | 必填 | 說明 |
|---|---|---|
email | legacy 未明確 validate | 收件人 email;legacy 直接放進 email task to。缺欄位在 PHP 5.6 只會 notice,API 仍走 success path;new 實作不可變成 TypeError。 |
最小 curl:
curl -sS 'https://api-staging.pro360.com.tw/users/refer.json' \
-X POST \
-H 'X-PRO360-Rest-Api-Key: <api key>' \
-H 'X-PRO360-User-Session-Token: <session id>' \
-F 'email=test@example.com'Response
Valid session + POST:
{"error":0,"message":"Success"}Invalid request / invalid session / non-POST:
{"error":1,"message":"Invalid request"}Blocked user:
{"error":5,"message":"Blocked User"}Blocked user 由 session helper 回 HTTP 403。
DB / Queue Side Effect
會讀:
| table | 用途 |
|---|---|
api_sessions | 驗證 session token。 |
users | 取得目前 user、referral_hash、multi_lang_id。 |
user_profiles | 組 provider name。 |
attachments | class = UserAvatar、foreign_id = user_id,取 amazon_s3_thumb_url。legacy 沒有 review_status / order 條件。 |
event_actions | worker 後續依 Invite_Referral 找 template。 |
email_templates | worker 後續使用 204 Vendor Signup Invitation by Referral。 |
會寫:
| table | 條件 | 內容 |
|---|---|---|
event_queue | valid session + POST + user exists + users.referral_hash 非空 | event_key = Invite_Referral,payload 含一個 email task。 |
users | valid session helper | legacy session access side effect 會更新 iphone_last_access、last_access_client。 |
task_queue | valid session helper 條件成立時 | provider 超過 180 天未 access 時可能寫 returnedQuoteService,與其他 migrated users API 一致。 |
不會寫:
user_referralsuser_over_referrals- wallet / transaction
- quote activity
- login/session 建立
users.referral_hash
Config 對照
| key | legacy 來源 | new 對應 | 用途 |
|---|---|---|---|
site.url | /Users/mattsu/Documents/Site/get-lancer-php56/app/Config/settings.yml:271,範例值 https://www.pro360.com.tw | Lib/Common/Configuration.php:272-273 / deployment config | 組 ##REFERRAL_URL##。staging / production 要確認部署值。 |
email.signature | /Users/mattsu/Documents/Site/get-lancer-php56/app/Config/settings.yml:22,範例值 PRO36O客服部 敬上 | Lib/Common/Configuration.php:271 / deployment config | ##EMAIL_SIGNATURE##。 |
2026-07-09 local DB 已確認:
event_actions: id=21, event_key=Invite_Referral, template_type=email, template_key=204 Vendor Signup Invitation by Referral
email_templates: id=111, name=204 Vendor Signup Invitation by Referral, is_active=1測試計畫
已補 tests/UsersReferTest.php:
- valid session + POST + user 有
referral_hash:回 success,寫一筆event_queue,payload shape / replaceContent / templateKey / langId 對齊。 - valid session + POST + user 沒
referral_hash:回 success,但不寫event_queue,也不補users.referral_hash。 - valid session + POST + email 已是既有 user email:仍回 success,不做 Email Existed 檢查。
- valid session + POST + missing email:維持 legacy success path,不因 PHP 8.2 undefined index / null 失敗。
- invalid session:回 legacy invalid request。
- blocked user:HTTP 403 + blocked response。
- non-POST:回 invalid request。
- negative test:不寫
user_referrals/user_over_referrals/ wallet / transaction。
執行結果:
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/UsersReferTest.php結果:OK (8 tests, 46 assertions)。
待確認
- staging legacy 實測
event_queue_log_3:event_key=Invite_Referral,params.user_id為 string,tasks[0].langId為 number,worker 已從event_queue搬到 log。 - staging 部署
site.url是否要用https://staging.pro360.com.tw還是 legacy 環境實際值;這會影響 email 內的 referral link。 - legacy
RestApiHelper::getUserId()full request log 與 newRouterV3generic log 的實際 context 差異;目前建議不新增 duplicate endpoint log。