体重記録仕様 (FR-004)
| 項目 | 内容 |
|---|---|
| 対象FR | FR-004(体重記録) |
| 優先度 | 高 |
| ステータス | 詳細化済み |
概要
ユーザーはペットの体重を日時とともに記録できる。1日に複数回の記録が可能で、記録にはメモを添えられる。記録された体重は一覧APIで日付範囲を指定して取得でき、クライアント側で時系列グラフを描画する。
入力フィールド
| フィールド | 型 | 必須 | バリデーション |
|---|---|---|---|
| weightG | integer | ○ | 1〜30000(単位: g) |
| recordedAt | timestamptz | ○ | 未来日時不可 |
| memo | text | - | 最大200文字 |
- 体重はグラム単位の整数で保存・入出力する(浮動小数点の精度問題を回避)
recordedAtはクライアントが計測日時を指定する(サーバー側のcreatedAtとは独立)- 同一ペット・同一日時でも複数レコードの登録を許可する
APIエンドポイント
全エンドポイントで認証必須(__Host-session Cookie)。
| メソッド | パス | 概要 |
|---|---|---|
| POST | /api/pets/:petId/weights | 体重記録 |
| GET | /api/pets/:petId/weights | 体重記録一覧(日付範囲フィルタ・ページネーション対応) |
| GET | /api/pets/:petId/weights/:weightId | 体重記録詳細 |
| PATCH | /api/pets/:petId/weights/:weightId | 体重記録更新 |
| DELETE | /api/pets/:petId/weights/:weightId | 体重記録削除 |
POST /api/pets/:petId/weights — 体重記録
リクエストボディに体重情報を指定して記録する。
リクエスト:
{ "weightG": 4500, "recordedAt": "2026-02-18T09:00:00+09:00", "memo": "朝食前"}- 成功時:
201 Created、作成されたレコードを返す - ペットが存在しない / 他ユーザーのペット / 論理削除済み:
404 Not Found
成功レスポンス:
{ "data": { "id": 1, "petId": 1, "weightG": 4500, "recordedAt": "2026-02-18T00:00:00.000Z", "memo": "朝食前", "createdAt": "2026-02-18T10:00:00.000Z", "updatedAt": "2026-02-18T10:00:00.000Z" }}GET /api/pets/:petId/weights — 体重記録一覧
指定ペットの体重記録を一覧取得する。日付範囲フィルタとページネーションに対応する。
クエリパラメータ:
| パラメータ | 型 | 必須 | デフォルト | 説明 |
|---|---|---|---|---|
| from | ISO 8601日時 | - | - | 取得開始日時(recordedAt >= from) |
| to | ISO 8601日時 | - | - | 取得終了日時(recordedAt <= to) |
| limit | integer | - | 50 | 取得件数上限(1〜100) |
| offset | integer | - | 0 | オフセット(0以上) |
| order | string | - | desc | ソート順(asc / desc、recordedAt 基準) |
- 成功時:
200 OK - 記録なし: 空配列を返す(エラーではない)
成功レスポンス:
{ "data": [ { "id": 2, "petId": 1, "weightG": 4520, "recordedAt": "2026-02-18T21:00:00.000Z", "memo": null, "createdAt": "2026-02-18T22:00:00.000Z", "updatedAt": "2026-02-18T22:00:00.000Z" }, { "id": 1, "petId": 1, "weightG": 4500, "recordedAt": "2026-02-18T00:00:00.000Z", "memo": "朝食前", "createdAt": "2026-02-18T10:00:00.000Z", "updatedAt": "2026-02-18T10:00:00.000Z" } ], "pagination": { "total": 30, "limit": 50, "offset": 0 }}GET /api/pets/:petId/weights/:weightId — 体重記録詳細
指定IDの体重記録を返す。
- 成功時:
200 OK - 存在しない / 他ユーザーのペットの記録:
404 Not Found
PATCH /api/pets/:petId/weights/:weightId — 体重記録更新
指定したフィールドのみ部分更新する。リクエストボディに含まれないフィールドは変更しない。
リクエスト(例: 体重とメモを修正):
{ "weightG": 4480, "memo": "計り直し"}- 成功時:
200 OK、更新後のレコードを返す - 存在しない / 他ユーザーのペットの記録:
404 Not Found
DELETE /api/pets/:petId/weights/:weightId — 体重記録削除
体重記録を物理削除する。
- 成功時:
204 No Content - 存在しない / 他ユーザーのペットの記録:
404 Not Found
認可ルール
- 全エンドポイントで認証必須
:petIdで指定したペットが自分の所有であること(他ユーザーのペットIDの場合は404を返し、存在の有無を漏らさない):weightIdのレコードが:petIdに属すること(不一致は404)- 論理削除済みペットへの操作は
404を返す
エッジケース
recordedAtが未来日時 →422 Unprocessable EntityweightGが範囲外(0以下 / 30001以上) →422 Unprocessable Entity- 論理削除済みペットへの体重記録作成 →
404 Not Found - 同一ペット・同一
recordedAtで複数登録 → 許可する(制約なし) - ペット論理削除時 → 体重記録は保持される(論理削除のためカスケード削除は発動しない)
- ペット物理削除時 → 関連する体重記録もカスケード削除される(DB制約)
拡張予定(現時点ではスコープ外)
- 体重の統計・集計API(期間別の平均・最大・最小など)
- BCS(Body Condition Score)の記録
- 体重の目標値設定・アラート機能
- 体重変動が大きい場合の自動通知(FR-010と連携)
検証方法
- 体重記録 → 一覧取得 → 詳細取得の一連フローが完了すること
- 必須フィールド(weightG, recordedAt)未指定時にバリデーションエラーが返ること
weightGが範囲外(0以下、30001以上)の場合に422が返ることrecordedAtが未来日時の場合に422が返ること- 他ユーザーのペットに対する操作で
404が返ること - PATCH で指定したフィールドのみが更新され、他フィールドが変更されないこと
- DELETE 後に一覧・詳細から表示されなくなること
- 日付範囲フィルタ(from / to)で正しく絞り込まれること
- ページネーション(limit / offset)が正しく動作すること
- ソート順(asc / desc)が正しく適用されること
- 同一日時に複数レコードを登録できること
- 論理削除済みペットへの操作が
404を返すこと