コンテンツにスキップ
Documents for MorningStatusApp
Esc
移動開く⌘Jプレビュー
このページの内容

メンバー データ設計書

最終更新: 2026-07-21

1. 概要

members.json に格納される Member(メンバー)エンティティのデータ設計。 Member は他の全機能(リリース紐付け・プレイリスト・ユニット・ライブ・関係性マップ・タイムライン等)から参照される最も基盤的なエンティティだが、専用のデータ設計書がこれまで存在しなかった(#1379)。


2. 背景・目的

目的 詳細
Member 本体のフィールド定義の一元化 types/member.tsMemberSchema に散在するフィールドの意味・業務ルールをまとめる
機能固有ドキュメントとの役割分担 フィールドごとの詳細な算出ロジック・UI仕様は既存の機能別設計書に記載済みのため、本書では概要とリンクに留め重複を避ける

3. 型定義

Member インターフェース(types/member.ts

export const MemberSchema = z.object({
  id: z.string().min(1),
  name: z.string().min(1),
  ruby: z.string().min(1),
  status: z.string().min(1),
  generation: z.number(),
  color: z.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),
  colorName: z.string().optional(),
  leaderGenerationNumber: z.number().int().positive().nullable().optional(),
  nickname: z.array(z.string()).optional(),
  joiningRoute: z.string().optional(),
  latestStatus: StatusEntrySchema,
  officialSns: z.array(SnsEntrySchema).optional(),
  statusHistory: z.array(StatusEntrySchema).optional(),
  snsCheck: SnsCheckSchema.optional(),
  youtubeChannel: YoutubeChannelSchema.optional(),
  tiktokChannel: TikTokChannelSchema.optional(),
  profile: z.object({
    dob: z.string(),
    joinDate: z.string(),
    gradDate: z.string().nullable(),
    family: z.string(),
    birthplace: z.string().optional(),
    instructor: z.array(z.string()).optional(),
  }),
});
export type Member = z.infer<typeof MemberSchema>;

フィールド詳細

フィールド 必須 説明
id string メンバーの識別子(kebab-case。例: iida-kaori)。他エンティティから memberId として参照される主キー
name string 氏名
ruby string ふりがな(五十音ソート等に使用)
status string 在籍状況を表す短い文字列(例: 現役メンバーの所属ユニット名、卒業メンバーの 卒業
generation number 期数
color string#RRGGBB メンバーカラー(HEX)。バッジ表示のスタイル決定に使用。詳細 → メンバーカラーバッジ設計書
colorName string メンバーカラーの表示名(例: ライトピンク)。未設定時は color の HEX 値をそのまま表示
leaderGenerationNumber number(正整数) リーダー在任時の「代」番号。関係性マップのリーダー継承エッジ算出に使用。詳細 → メンバー関係性マップ データ設計書
nickname string[] 愛称(複数バリエーションを持つメンバーがいるため配列)。詳細 → メンバー関係性マップ データ設計書 §9
joiningRoute string 加入ルート(オーディション名等)。詳細 → メンバー関係性マップ データ設計書 §10
latestStatus StatusEntry 最新の近況エントリ。詳細 → Amebaブログ機能 データ設計書
officialSns SnsEntry[] 公式 SNS リンク一覧
statusHistory StatusEntry[] 近況の変更履歴(新しい順)。保持ルールは §5 を参照
snsCheck SnsCheck 近況自動検索(sync-status)の直近チェック結果
youtubeChannel YoutubeChannel 個人 YouTube チャンネル情報。解決ロジックは §5 を参照
tiktokChannel TikTokChannel 個人 TikTok アカウント情報。解決ロジックは §5 を参照(#1478)
profile.dob string 生年月日
profile.joinDate stringYYYY-MM-DD 加入日。MemberReleaseLink の実質基準日算出に使用。詳細 → リリース データ設計書
profile.gradDate string | null 卒業日。現役メンバーは null
profile.family string 出身グループ・ユニット等(自由記述)
profile.birthplace string 出身地。ライブ機能の「ご当地ライブ」判定に使用(§6の Neon 側 Member と対)。詳細 → ライブ・フェス機能 テーブル設計書
profile.instructor string[] 師匠にあたるメンバーの id 一覧。詳細 → メンバー関係性マップ データ設計書 §「師弟関係」

StatusEntry インターフェース(Member.latestStatus / Member.statusHistory の要素)

export const StatusEntrySchema = z.object({
  content: z.string(),
  lastUpdated: z.string(),
  sources: z.array(z.string()),
});
フィールド 説明
content string 近況本文
lastUpdated stringYYYY-MM-DD 最終更新日(JST)
sources string[] 情報源 URL 一覧

SnsEntry インターフェース(Member.officialSns の要素)

export const SnsEntrySchema = z.object({
  url: z.string(),
  active: z.boolean(),
});

SnsCheck インターフェース(Member.snsCheck

export const SnsCheckSchema = z.object({
  status: z.enum(['ok', 'error', 'skipped', 'unknown']),
  hasRecentUpdate: z.boolean(),
  lastCheckedAt: z.string(),
  reason: z.string().optional(),
  lastPostAt: z.string().optional(),
});
フィールド 説明
status 'ok' | 'error' | 'skipped' | 'unknown' 直近の自動検索(Exa Search API)の実行結果
hasRecentUpdate boolean 直近の検索で近況の更新が見つかったか
lastCheckedAt string 最終チェック日時
reason string statuserror/skipped の場合の理由(任意)
lastPostAt string 検出した最新投稿の日時(任意)

YoutubeChannel インターフェース(Member.youtubeChanneltypes/youtube.ts

export const YoutubeChannelSchema = z.object({
  handle: z.string().min(1),
  playlistId: z.string().min(1),
});
フィールド 説明
handle string YouTube チャンネルハンドル(@ 始まり)
playlistId string ハンドルから解決されたアップロード動画プレイリスト ID(サーバー側で自動解決。§5参照)

TikTokChannel インターフェース(Member.tiktokChanneltypes/tiktok.ts

export const TikTokChannelSchema = z.object({
  username: z.string().min(1),
});
フィールド 説明
username string TikTok のユーザー名(@ を含まない。RapidAPI の unique_id パラメータにそのまま渡せる値)

TikTok は RapidAPI(tiktok-scraper7.p.rapidapi.com)が unique_id(username)を直接指定して投稿取得できるため、YouTube の playlistId のようなサーバー側 API 解決は不要(§5参照)。詳細 → TikTok投稿機能 データ設計書


4. データファイル

members.json(Vercel Blob)

  • Vercel Blob(MEMBERS_BLOB_URL)で管理。ローカルにフリーズドコピーは持たない(読み取りは Blob URL への fetch
  • 更新経路: PUT /api/members(メンバー詳細画面の編集フォーム)、sync-members ワークフロー(近況自動検索・Ameba同期)、各種 scripts/patch/patch-*.ts(愛称・加入ルート・メンバーカラー・リーダー代・師匠情報等の一括投入)

5. PUT /api/members の業務ルール

app/api/members/route.ts が Blob への書き込みを一元的に担う。クライアントから送信されたペイロードの一部フィールドはサーバー側で上書き・算出され、クライアント値に依存しない。

5-1. statusHistory の保持ルール

  • latestStatus.content が既存値から変化した場合のみ、新しい StatusEntrystatusHistory の先頭に追加する
  • 変化の有無に関わらず、保存前に以下の2条件でフィルタする(早い方を適用):
    • 30日以内(lastUpdated が現在日から30日以内)
    • 100件以内(slice(0, 100)
  • latestStatus.lastUpdated は JST(Asia/Tokyo)でサーバー側が生成する。クライアントが送った値は無視される(#141)

5-2. youtubeChannel の解決ロジック

条件 動作
handle が空 youtubeChannel フィールドごと削除する
handle が既存値と同じ 既存の playlistId を引き継ぐ(YouTube API クォータ節約のため再解決しない)
handle が新規・変更あり fetchPlaylistId(handle) で YouTube API から playlistId を解決する。解決失敗時は 422 エラーを返し保存しない

5-3. tiktokChannel の解決ロジック(#1478)

条件 動作
username が空 tiktokChannel フィールドごと削除する
username が指定あり トリムしてそのまま保存する(API 解決不要。§3参照)

6. Neon 側「出身地照合用メンバー」(Member

ライブ機能の「ご当地ライブ」判定でのみ使用される、Blob Member とは別の Prisma モデル。

model Member {
  memberId   String  @id @db.VarChar(50)
  birthplace String? @db.VarChar(255)

  @@map("members")
}
  • memberId は Blob Member.id と同値を Neon 側に複製したもの(DB レベルの FK 制約はない)
  • birthplace のみを保持する部分的なミラーであり、Blob Member の完全な複製ではない
  • 詳細 → ライブ・フェス機能 テーブル設計書

7. ER 図

ER 図 を参照すること。Member はコア音楽データ・ライブ/フェス・メンバー関係性マップの各セクションに登場する。


8. 関連設計書

設計書 本書との関係
Amebaブログ機能 データ設計書 latestStatus/statusHistory の RSS 取得・Topページ新着判定の詳細
メンバーカラーバッジ設計書 color/colorName を用いたバッジスタイル算出ロジック
メンバー関係性マップ データ設計書 nicknamejoiningRouteleaderGenerationNumberprofile.instructor の算出・表示ロジック
リリース データ設計書 profile.joinDate/profile.gradDate を用いた MemberReleaseLink 紐付けロジック
ライブ・フェス機能 テーブル設計書 Neon 側「出身地照合用メンバー」(Member)・ご当地ライブ判定
TikTok投稿機能 データ設計書 tiktokChannel を用いたOGメンバー個人アカウント収集ロジック

改訂履歴

更新日 変更内容
1.1 2026-08-09 OGメンバーのTikTok個人アカウント収集に伴い tiktokChannel フィールドを追加(§3・§5-3・§8)。YouTubeの youtubeChannel と異なりAPI側のID解決が不要なため username のみを保持する(#1478)
1.0 2026-07-21 初版作成(#1379)

このページは役に立ちましたか?