GET /users/referrals.json
狀態
已補 action / routing / PHPUnit;staging new 實測待補。
這支只負責回傳目前登入 user 的推薦好友列表。legacy 主流程讀 user_referrals 與被推薦 user 資料,不寫 user_referrals、不寄信、不寫 queue、不做付款。寄推薦信是另一支 POST /users/refer.json,後續應另外建文件,不和這支混在一起。
Legacy 流量
2026-07-08 查 legacy 正式機 ip-10-5-2-242 的 /var/log/apache2/get-lancer_access.log*:
| Method / path | status | count |
|---|---|---|
GET /users/referrals.json | 200 | 139 |
OPTIONS /users/referrals.json | 200 | 125 |
同一批查詢也看到 POST /users/refer.json 有 2 筆 200,但語意不同,先不列入本次搬移。
操作路徑
Web 專家推薦好友頁會觸發這支 API。實際 URL:
https://staging.pro360.com.tw/dashboard/free-credits/referral前端在 ProReferral mount 時呼叫 APIManager.getReferralInfo(),用 session token GET /users/referrals.json,再把 response.referred_users 放進畫面 state。
| 檔案 | 行為 |
|---|---|
/Users/mattsu/Documents/Site/web-app/modules/routes.js:734 | /dashboard/free-credits(/:index) route。 |
/Users/mattsu/Documents/Site/web-app/modules/components/dashboard/FreeCredits.js:17-20 | EVENT_SLUGS.REFERRAL = referral。 |
/Users/mattsu/Documents/Site/web-app/modules/components/dashboard/FreeCredits.js:247-250 | index === referral 時 render ProReferral。 |
/Users/mattsu/Documents/Site/web-app/modules/utils/api-manager.js:3441-3449 | getReferralInfo() 讀 session token,GET /users/referrals.json。 |
/Users/mattsu/Documents/Site/web-app/modules/components/dashboard/ProReferral.js:24-28 | component mount 後呼叫 getReferralInfo(),使用 data.referred_users。 |
referred_users 的前端用途:
referred_users.length:顯示「累積推薦人數」與進度條。UserProfile.first_name:列表顯示推薦好友名字。User.quote_status_id:顯示「已註冊 / 已報價 / 已接案」進度。User.awarded_credits:顯示$ +{credit}。
Legacy 對照
| 項目 | legacy |
|---|---|
| controller | /Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/UsersController.php:5697-5748 |
| session helper | /Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/RestApiHelper.php:75-152 |
| session user id + full request log | /Users/mattsu/Documents/Site/get-lancer-php56/app/Controller/RestApiHelper.php:162-182 |
| referral model | /Users/mattsu/Documents/Site/get-lancer-php56/app/Model/UserReferral.php:1-83 |
Legacy 流程:
- 預設 response 是
error=1+message=Invalid request。 - 必須是 JSON request 且通過
RestApiHelper::hasValidApiSession($this)。 - blocked user 會直接回 HTTP 403 +
{"error":5,"message":"Blocked User"}。 - 從 session 取得目前
user_id。 - 查
user_referrals:user_id = current user_idcreated > CURRENT_DATE - INTERVAL 1 YEAR- fields 只有
id、referee_user_id、quote_status_id、awarded_credits
- 用 referral rows 的
referee_user_id查users:- fields 只有
User.id - contain
UserProfile.first_name - contain
UserProfile.last_name - contain
UserAvatar.amazon_s3_original_url - contain
UserAvatar.amazon_s3_thumb_url
- fields 只有
- 把 referral row 的
quote_status_id、awarded_credits塞進對應 user 的User區塊。 - 回
referred_users、error=0、message=Success。
Invalid session 或拿不到 user_id 時,legacy 回 error=2、message=Invalid session。
New 對照
| 項目 | new |
|---|---|
| endpoint | Endpoint/V1/Users.php:1082-1113 已補 referrals()。 |
| DB formatter | Endpoint/V1/Users.php:1145-1290 已補 getLegacyReferralUsers() 與 legacy response formatter。 |
| routing | Lib/Common/RouterRule/Mapping.php:108-111 已補 /users/referrals.json explicit route。 |
| existing referral model | Lib/Model/UserReferral.php:15-74 目前有 relation / add helper,但沒有這支 API 需要的 list response method。 |
| user avatar reference | Endpoint/V1/Users.php:88-97 既有 user response 有用 Attachment::getClassRow('UserAvatar', user_id),本 API 實作時仍要以 legacy contain 欄位為準。 |
| PHPUnit | tests/UsersReferralsTest.php:71-203 覆蓋空列表、nested shape、1 年 filter、inactive referred user、missing avatar、blocked user、invalid session。 |
Request
必要 headers:
X-PRO360-Rest-Api-Key: <api key>
X-PRO360-User-Session-Token: <session id>沒有 body。
最小 curl:
curl -sS 'https://api-staging.pro360.com.tw/users/referrals.json' \
-H 'X-PRO360-Rest-Api-Key: <api key>' \
-H 'X-PRO360-User-Session-Token: <session id>'Response
Valid session success:
{
"referred_users": [
{
"User": {
"id": "123",
"quote_status_id": "0",
"awarded_credits": "0"
},
"UserProfile": {
"first_name": "First",
"last_name": "Last"
},
"UserAvatar": {
"amazon_s3_original_url": "...",
"amazon_s3_thumb_url": "..."
}
}
],
"error": 0,
"message": "Success"
}Valid session 但沒有 referral:
{
"referred_users": [],
"error": 0,
"message": "Success"
}2026-07-08 legacy staging 實測已確認 valid session + 空資料回:
{"referred_users":[],"error":0,"message":"Success"}Invalid / missing session:
{
"error": 2,
"message": "Invalid session"
}Blocked user:
{
"error": 5,
"message": "Blocked User"
}HTTP status:403。
有 referral 資料時的 response 型別待 staging legacy sample 確認。legacy Cake 通常會把 DB 欄位值以字串回傳;目前 PHPUnit 先固定 User.id、quote_status_id、awarded_credits 為字串。
DB Read
先用容器 PHP 8.2 查過相關 schema:
| Table | 欄位 |
|---|---|
user_referrals | id、created、modified、user_id、referee_user_id、quote_status_id、awarded_credits、confirmed_phone |
users | 本 API legacy 只取 id,不要因 schema 有其他欄位就補進 response。 |
user_profiles | 本 API legacy 只取 first_name、last_name。 |
attachments | 本 API legacy 只取 class=UserAvatar 的 amazon_s3_original_url、amazon_s3_thumb_url。 |
Legacy 實際讀取:
SELECT id, referee_user_id, quote_status_id, awarded_credits
FROM user_referrals
WHERE user_id = :current_user_id
AND created > CURRENT_DATE - INTERVAL 1 YEAR;第二段查被推薦 user 時,要維持 legacy 語意:
- 用第一段的
referee_user_id查users.id。 - 只回
User.id。 - 補
UserProfile.first_name、UserProfile.last_name。 - 補
UserAvatar.amazon_s3_original_url、UserAvatar.amazon_s3_thumb_url。 - 再把第一段的
quote_status_id、awarded_credits放回User。
Side Effect
這支沒有以下業務 side effect:
- 不新增 / 修改 / 刪除
user_referrals - 不寄 email
- 不寫
event_queue/task_event_queue - 不寫 payment / wallet / transaction
- 不產生 activity
Blocked user guard 是 session helper 行為,不是 referrals 主流程;blocked 時不查 referral list,不更新 access time。
Session helper side effect:legacy RestApiHelper::hasValidApiSession() 在特定條件會更新 session user 的 app last access;new 沿用 findLegacyApiSessionUser() + applyLegacyApiSessionSideEffects()。
Parity 注意
不可自行新增 legacy 沒有的條件或欄位:
- 不加
users.is_active = 1 - 不加
attachments.review_status - 不加
order - 不加
limit - 不回
referral_hash - 不回
confirmed_phone - 不回
users.email/users.phone - 不把
/users/refer.json的寄信邏輯放進來
Request log:legacy RestApiHelper::getUserId() 會記 full request info;new 搬移時先依目前 Users 已搬 API 慣例保留 RouterV3 generic request log,是否補 endpoint duplicate log 待確認。這支 request 沒有 body,敏感欄位風險低,但仍不得為了 log 自行新增 response / DB side effect。
Tests Plan
已補:
- valid session + 有 referral:回 legacy nested shape,含
User/UserProfile/UserAvatar。 - valid session + 無 referral:回
referred_users: []、error=0、message=Success。 - missing / invalid session:回
error=2、message=Invalid session。 - blocked user:回 HTTP 403 +
{"error":5,"message":"Blocked User"}。 - referral 超過一年:不回。
- 被推薦 user inactive:若 legacy 實測仍回,new 不得加
is_active = 1擋掉。 - response 不包含
referral_hash、confirmed_phone、email、phone。 - 不寫 queue / activity / user_referrals。
測試指令:
docker exec -w /project-data cd63f9147e8d vendor/bin/phpunit tests/UsersReferralsTest.php結果:OK,7 tests / 40 assertions。
待確認
- staging legacy 有 referral 資料時的完整 JSON shape 與欄位型別。
- staging legacy 有 referral 但沒有
UserAvatar時的完整 association shape;目前依 Cake hasOne empty association 與 PHPUnit 固定為[]。 - new staging curl / DB log 驗證。
- request log 是否需要補 endpoint-level duplicate log;目前保留 RouterV3 generic log,維持目前 Users 搬移慣例。