Documents
Home

schema.sql

-- ============================================================
-- Robot-BoT Database Schema
-- SQLite 互換 / PostgreSQL 対応
-- 共通カラム: *_id, created_at, updated_at, source, history_ref
-- ソフトデリート対応テーブル: deleted_at, deleted_by も共通
-- source 値: home-bot / home-web / cf-web / d1 / turso
-- ============================================================


-- ============================================================
-- メンバー系
-- ============================================================

CREATE TABLE team_roles (
    team_role_id    TEXT        PRIMARY KEY,
    name            TEXT        NOT NULL,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

-- 初期データ
-- INSERT INTO team_roles VALUES ('<uuid>', 'キャプテン',               '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO team_roles VALUES ('<uuid>', 'プロジェクトマネージャー', '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO team_roles VALUES ('<uuid>', '安全管理',                 '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO team_roles VALUES ('<uuid>', 'PR担当',                   '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO team_roles VALUES ('<uuid>', '操縦手',                   '<ISO8601>', '<ISO8601>', 'home-web', NULL);

CREATE TABLE members (
    member_id       TEXT        PRIMARY KEY,
    name            TEXT        NOT NULL,
    discord_id      TEXT        NOT NULL UNIQUE,    -- 代表 Discord ID
    is_active       INTEGER     NOT NULL DEFAULT 1, -- 1=在籍中 0=脱退
    joined_at       TEXT        NOT NULL,
    left_at         TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

CREATE TABLE member_team_roles (
    member_team_role_id TEXT    PRIMARY KEY,
    member_id           TEXT    NOT NULL REFERENCES members(member_id),
    team_role_id        TEXT    NOT NULL REFERENCES team_roles(team_role_id),
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

-- member_auth は廃止・member_discord_accounts に統合

CREATE TABLE member_roles (
    member_role_id  TEXT    PRIMARY KEY,
    member_id       TEXT    NOT NULL REFERENCES members(member_id),
    role            TEXT    NOT NULL,
                            -- admin / sub_admin / member
    granted_by      TEXT    REFERENCES members(member_id),
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    deleted_at      TEXT    DEFAULT NULL,
    deleted_by      TEXT    REFERENCES members(member_id),
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE member_discord_accounts (
    discord_account_id          TEXT    PRIMARY KEY,
    member_id                   TEXT    NOT NULL REFERENCES members(member_id),
    discord_id                  TEXT    NOT NULL UNIQUE,
    username                    TEXT    NOT NULL,
    email                       TEXT    NOT NULL UNIQUE,
    totp_secret                 TEXT,
    discord_access_token        TEXT,
    discord_refresh_token       TEXT,
    discord_token_expires_at    TEXT,
    created_at                  TEXT    NOT NULL,
    updated_at                  TEXT    NOT NULL,
    deleted_at                  TEXT    DEFAULT NULL,
    deleted_by                  TEXT    REFERENCES members(member_id),
    source                      TEXT    NOT NULL,
    history_ref                 TEXT
);

CREATE TABLE member_passkeys (
    passkey_id          TEXT    PRIMARY KEY,
    discord_account_id  TEXT    NOT NULL REFERENCES member_discord_accounts(discord_account_id),
    credential_id       TEXT    NOT NULL UNIQUE,
    public_key          TEXT    NOT NULL,
    device_name         TEXT,
    registered_at       TEXT    NOT NULL,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    deleted_at          TEXT    DEFAULT NULL,
    deleted_by          TEXT    REFERENCES members(member_id),
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE member_sessions (
    session_id          TEXT    PRIMARY KEY,
    discord_account_id  TEXT    NOT NULL REFERENCES member_discord_accounts(discord_account_id),
    session_token       TEXT    NOT NULL UNIQUE,
    device_info         TEXT,
    last_active_at      TEXT    NOT NULL,
    expires_at          TEXT    NOT NULL,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    deleted_at          TEXT    DEFAULT NULL,
    deleted_by          TEXT    REFERENCES members(member_id),
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE member_devices (
    device_id               TEXT    PRIMARY KEY,
    discord_account_id      TEXT    NOT NULL REFERENCES member_discord_accounts(discord_account_id),
    fingerprint_data        TEXT    NOT NULL,
    device_name             TEXT,
    last_seen_at            TEXT    NOT NULL,
    created_at              TEXT    NOT NULL,
    updated_at              TEXT    NOT NULL,
    deleted_at              TEXT    DEFAULT NULL,
    deleted_by              TEXT    REFERENCES members(member_id),
    source                  TEXT    NOT NULL,
    history_ref             TEXT
);

CREATE TABLE member_emergency_keys (
    key_id          TEXT    PRIMARY KEY,
    member_id       TEXT    NOT NULL REFERENCES members(member_id),
    issued_by       TEXT    REFERENCES members(member_id),
    key_hash        TEXT,
    status          TEXT    NOT NULL DEFAULT 'pending',
                            -- pending / issued / used / expired
    request_at      TEXT    NOT NULL,
    expires_at      TEXT,
    used_at         TEXT,
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    deleted_at      TEXT    DEFAULT NULL,
    deleted_by      TEXT    REFERENCES members(member_id),
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE member_login_notification_settings (
    setting_id                  TEXT    PRIMARY KEY,
    discord_account_id          TEXT    NOT NULL UNIQUE REFERENCES member_discord_accounts(discord_account_id),
    notify_on_session           INTEGER NOT NULL DEFAULT 0,
    notify_on_oauth             INTEGER NOT NULL DEFAULT 1,
    notify_on_new_device        INTEGER NOT NULL DEFAULT 1,
    notify_on_passkey           INTEGER NOT NULL DEFAULT 1,
    notify_on_totp              INTEGER NOT NULL DEFAULT 1,
    notify_on_emergency_key     INTEGER NOT NULL DEFAULT 1,
    created_at                  TEXT    NOT NULL,
    updated_at                  TEXT    NOT NULL,
    deleted_at                  TEXT    DEFAULT NULL,
    deleted_by                  TEXT    REFERENCES members(member_id),
    source                      TEXT    NOT NULL,
    history_ref                 TEXT
);

CREATE TABLE member_login_history (
    login_id                TEXT    PRIMARY KEY,
    discord_account_id      TEXT    NOT NULL REFERENCES member_discord_accounts(discord_account_id),
    login_method            TEXT    NOT NULL,
                                    -- session / oauth / passkey / totp / emergency_key
    os                      TEXT,
    browser                 TEXT,
    device_id               TEXT    REFERENCES member_devices(device_id),
    is_new_device           INTEGER NOT NULL DEFAULT 0,
    logged_in_at            TEXT    NOT NULL,
    created_at              TEXT    NOT NULL,
    updated_at              TEXT    NOT NULL,
    source                  TEXT    NOT NULL,
    history_ref             TEXT
);

CREATE TABLE permanent_delete_logs (
    log_id          TEXT    PRIMARY KEY,
    deleted_by      TEXT    NOT NULL REFERENCES members(member_id),
    target_table    TEXT    NOT NULL,
    target_id       TEXT    NOT NULL,
    snapshot        TEXT    NOT NULL,   -- 削除前データ JSON
    deleted_at      TEXT    NOT NULL,
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    source          TEXT    NOT NULL,
    history_ref     TEXT
);


-- ============================================================
-- 活動系
-- ============================================================

CREATE TABLE venues (
    venue_id        TEXT        PRIMARY KEY,
    name            TEXT        NOT NULL,
    location        TEXT,               -- 住所または URL
    memo            TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    deleted_at      TEXT        DEFAULT NULL,
    deleted_by      TEXT        REFERENCES members(member_id),
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

CREATE TABLE activity_sessions (
    session_id          TEXT    PRIMARY KEY,
    venue_id            TEXT    NOT NULL REFERENCES venues(venue_id),
    date                TEXT    NOT NULL,
    planned_start_at    TEXT    NOT NULL,
    planned_end_at      TEXT    NOT NULL,
    actual_start_at     TEXT,
    actual_end_at       TEXT,
    work_end_at         TEXT,
    cleanup_end_at      TEXT,
    is_cancelled        INTEGER NOT NULL DEFAULT 0,
    cancel_reason       TEXT,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE attendance_plans (
    plan_id                 TEXT    PRIMARY KEY,
    session_id              TEXT    NOT NULL REFERENCES activity_sessions(session_id),
    member_id               TEXT    NOT NULL REFERENCES members(member_id),
    planned_status          TEXT    NOT NULL DEFAULT 'unknown',
                                    -- venue / online / absent / unknown
    planned_late            INTEGER NOT NULL DEFAULT 0,
    planned_arrival_at      TEXT,
    planned_early_leave     INTEGER NOT NULL DEFAULT 0,
    planned_leave_at        TEXT,
    absence_reason          TEXT,
    created_at              TEXT    NOT NULL,
    updated_at              TEXT    NOT NULL,
    source                  TEXT    NOT NULL,
    history_ref             TEXT
);

CREATE TABLE attendance_records (
    record_id           TEXT    PRIMARY KEY,
    plan_id             TEXT    REFERENCES attendance_plans(plan_id),
    session_id          TEXT    NOT NULL REFERENCES activity_sessions(session_id),
    member_id           TEXT    NOT NULL REFERENCES members(member_id),
    actual_status       TEXT    NOT NULL DEFAULT 'unknown',
                                -- venue / online / absent / unknown
    is_late             INTEGER NOT NULL DEFAULT 0,
    arrival_at          TEXT,
    is_early_leave      INTEGER NOT NULL DEFAULT 0,
    leave_at            TEXT,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE attendance_notes (
    note_id                 TEXT    PRIMARY KEY,
    plan_id                 TEXT    NOT NULL REFERENCES attendance_plans(plan_id),
    discord_account_id      TEXT    NOT NULL REFERENCES member_discord_accounts(discord_account_id),
    content                 TEXT    NOT NULL,
    created_at              TEXT    NOT NULL,
    updated_at              TEXT    NOT NULL,
    deleted_at              TEXT    DEFAULT NULL,
    deleted_by              TEXT    REFERENCES members(member_id),
    source                  TEXT    NOT NULL,
    history_ref             TEXT
);

CREATE TABLE attendance_reminder_patterns (
    pattern_id      TEXT    PRIMARY KEY,
    name            TEXT    NOT NULL,
    created_by      TEXT    NOT NULL REFERENCES members(member_id),
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    deleted_at      TEXT    DEFAULT NULL,
    deleted_by      TEXT    REFERENCES members(member_id),
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE attendance_reminder_timings (
    timing_id       TEXT    PRIMARY KEY,
    pattern_id      TEXT    NOT NULL REFERENCES attendance_reminder_patterns(pattern_id),
    timing_type     TEXT    NOT NULL,
                            -- hours_before / days_before
    timing_value    INTEGER NOT NULL,
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    source          TEXT    NOT NULL,
    history_ref     TEXT
);


-- ============================================================
-- アンケート系
-- ============================================================

CREATE TABLE survey_templates (
    template_id     TEXT    PRIMARY KEY,
    title           TEXT    NOT NULL,
    created_by      TEXT    NOT NULL REFERENCES members(member_id),
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    deleted_at      TEXT    DEFAULT NULL,
    deleted_by      TEXT    REFERENCES members(member_id),
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE survey_template_items (
    item_id         TEXT    PRIMARY KEY,
    template_id     TEXT    NOT NULL REFERENCES survey_templates(template_id),
    order_index     INTEGER NOT NULL,
    content         TEXT    NOT NULL,
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE surveys (
    survey_id           TEXT    PRIMARY KEY,
    template_id         TEXT    REFERENCES survey_templates(template_id),
    title               TEXT    NOT NULL,
    created_by          TEXT    NOT NULL REFERENCES members(member_id),
    closes_at           TEXT,               -- NULL = 期限なし
    closed_at           TEXT,
    closed_by           TEXT    REFERENCES members(member_id),
    discord_message_id  TEXT,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    deleted_at          TEXT    DEFAULT NULL,
    deleted_by          TEXT    REFERENCES members(member_id),
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE survey_items (
    item_id         TEXT    PRIMARY KEY,
    survey_id       TEXT    NOT NULL REFERENCES surveys(survey_id),
    order_index     INTEGER NOT NULL,
    content         TEXT    NOT NULL,
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE survey_responses (
    response_id             TEXT    PRIMARY KEY,
    survey_id               TEXT    NOT NULL REFERENCES surveys(survey_id),
    item_id                 TEXT    NOT NULL REFERENCES survey_items(item_id),
    discord_account_id      TEXT    NOT NULL REFERENCES member_discord_accounts(discord_account_id),
    response                TEXT    NOT NULL,
                                    -- ok / unsure / ng
    note                    TEXT,
    created_at              TEXT    NOT NULL,
    updated_at              TEXT    NOT NULL,
    source                  TEXT    NOT NULL,
    history_ref             TEXT
);


-- ============================================================
-- 作業系
-- ============================================================

CREATE TABLE task_roles (
    task_role_id    TEXT        PRIMARY KEY,
    name            TEXT        NOT NULL,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

-- 初期データ
-- INSERT INTO task_roles VALUES ('<uuid>', '機械系',           '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO task_roles VALUES ('<uuid>', 'プログラミング系', '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO task_roles VALUES ('<uuid>', '回路系',           '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO task_roles VALUES ('<uuid>', '連絡担当メイン',   '<ISO8601>', '<ISO8601>', 'home-web', NULL);
-- INSERT INTO task_roles VALUES ('<uuid>', '連絡担当サブ',     '<ISO8601>', '<ISO8601>', 'home-web', NULL);

CREATE TABLE tasks (
    task_id             TEXT    PRIMARY KEY,
    parent_task_id      TEXT    REFERENCES tasks(task_id),
    name                TEXT    NOT NULL,
    description         TEXT,
    goal                TEXT,
    status              TEXT    NOT NULL DEFAULT 'todo',
                                -- todo / in_progress / blocked / pending /
                                -- waiting / review / done / cancelled
    priority            INTEGER NOT NULL DEFAULT 50,    -- 1〜100 大きいほど高優先度
    planned_end_at      TEXT,
    estimated_hours     INTEGER,                        -- 予定所要時間(分単位)
    actual_end_at       TEXT,
    is_cancelled        INTEGER NOT NULL DEFAULT 0,
    cancel_reason       TEXT,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    deleted_at          TEXT    DEFAULT NULL,
    deleted_by          TEXT    REFERENCES members(member_id),
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE task_assignments (
    task_assignment_id  TEXT    PRIMARY KEY,
    task_id             TEXT    NOT NULL REFERENCES tasks(task_id),
    member_id           TEXT    NOT NULL REFERENCES members(member_id),
    task_role_id        TEXT    REFERENCES task_roles(task_role_id),
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE task_dependency_groups (
    group_id        TEXT    PRIMARY KEY,
    task_id         TEXT    NOT NULL REFERENCES tasks(task_id),
    created_at      TEXT    NOT NULL,
    updated_at      TEXT    NOT NULL,
    source          TEXT    NOT NULL,
    history_ref     TEXT
);

CREATE TABLE task_dependencies (
    dependency_id       TEXT    PRIMARY KEY,
    group_id            TEXT    NOT NULL REFERENCES task_dependency_groups(group_id),
    depends_on_task_id  TEXT    NOT NULL REFERENCES tasks(task_id),
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE task_progress_history (
    history_id          TEXT    PRIMARY KEY,
    task_id             TEXT    NOT NULL REFERENCES tasks(task_id),
    changed_by          TEXT    NOT NULL REFERENCES members(member_id),
    previous_status     TEXT,
    new_status          TEXT,
    previous_priority   INTEGER,
    new_priority        INTEGER,
    memo                TEXT,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE task_memos (
    memo_id         TEXT        PRIMARY KEY,
    task_id         TEXT        NOT NULL REFERENCES tasks(task_id),
    member_id       TEXT        NOT NULL REFERENCES members(member_id),
    content         TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    deleted_at      TEXT        DEFAULT NULL,
    deleted_by      TEXT        REFERENCES members(member_id),
    source          TEXT        NOT NULL,
    history_ref     TEXT
);


-- ============================================================
-- 手順系
-- ============================================================

CREATE TABLE procedure_templates (
    template_id     TEXT        PRIMARY KEY,
    task_id         TEXT        REFERENCES tasks(task_id),  -- NULL なら汎用テンプレート
    name            TEXT        NOT NULL,
    description     TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    deleted_at      TEXT        DEFAULT NULL,
    deleted_by      TEXT        REFERENCES members(member_id),
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

CREATE TABLE procedure_result_definitions (
    result_id       TEXT        PRIMARY KEY,
    template_id     TEXT        NOT NULL REFERENCES procedure_templates(template_id),
    value           TEXT        NOT NULL,   -- ok / ng / skip / timeout など
    label           TEXT        NOT NULL,   -- 「正常」「異常」など表示名
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

CREATE TABLE procedure_template_steps (
    step_id         TEXT        PRIMARY KEY,
    template_id     TEXT        NOT NULL REFERENCES procedure_templates(template_id),
    order_index     INTEGER     NOT NULL,
    name            TEXT        NOT NULL,
    description     TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

CREATE TABLE procedure_step_transitions (
    transition_id       TEXT    PRIMARY KEY,
    from_step_id        TEXT    NOT NULL REFERENCES procedure_template_steps(step_id),
    to_step_id          TEXT    NOT NULL REFERENCES procedure_template_steps(step_id),
    priority            INTEGER NOT NULL,
    condition_type      TEXT    NOT NULL,   -- result / always
    condition_value     TEXT,               -- condition_type=result のときの result value
    label               TEXT,               -- 「安全確認OK」など表示名
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE procedure_instances (
    instance_id     TEXT        PRIMARY KEY,
    task_id         TEXT        NOT NULL REFERENCES tasks(task_id),
    template_id     TEXT        NOT NULL REFERENCES procedure_templates(template_id),
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    deleted_at      TEXT        DEFAULT NULL,
    deleted_by      TEXT        REFERENCES members(member_id),
    source          TEXT        NOT NULL,
    history_ref     TEXT
);

CREATE TABLE procedure_instance_steps (
    instance_step_id    TEXT    PRIMARY KEY,
    instance_id         TEXT    NOT NULL REFERENCES procedure_instances(instance_id),
    step_id             TEXT    NOT NULL REFERENCES procedure_template_steps(step_id),
    order_index         INTEGER NOT NULL,
    name                TEXT    NOT NULL,
    result_value        TEXT,
    is_done             INTEGER NOT NULL DEFAULT 0,
    done_by             TEXT    REFERENCES members(member_id),
    done_at             TEXT,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);


-- ============================================================
-- 活動×作業紐付け
-- ============================================================

CREATE TABLE session_task_records (
    record_id       TEXT        PRIMARY KEY,
    session_id      TEXT        NOT NULL REFERENCES activity_sessions(session_id),
    task_id         TEXT        NOT NULL REFERENCES tasks(task_id),
    progress_note   TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);


-- ============================================================
-- 通知系
-- ============================================================

CREATE TABLE notification_rules (
    rule_id             TEXT    PRIMARY KEY,
    type                TEXT    NOT NULL,
                                -- delay_pre / delay_progress / delay_over
                                -- session_remind / attendance_remind / procedure_done
    discord_channel_id  TEXT    NOT NULL,
    is_active           INTEGER NOT NULL DEFAULT 1,
    created_at          TEXT    NOT NULL,
    updated_at          TEXT    NOT NULL,
    source              TEXT    NOT NULL,
    history_ref         TEXT
);

CREATE TABLE notification_timings (
    timing_id       TEXT        PRIMARY KEY,
    rule_id         TEXT        NOT NULL REFERENCES notification_rules(rule_id),
    timing_type     TEXT        NOT NULL,   -- days_before / hours_before
    timing_value    INTEGER     NOT NULL,
                                -- days_before:  正=期限前 0=当日 負=期限後
                                -- hours_before: 正=前 負=後
    time_of_day     TEXT,                   -- HH:MM hours_before の場合 NULL
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    source          TEXT        NOT NULL,
    history_ref     TEXT
);


-- ============================================================
-- 共通 URL 管理
-- ============================================================

CREATE TABLE urls (
    url_id          TEXT        PRIMARY KEY,
    target_type     TEXT        NOT NULL,
                                -- task / task_memo / procedure_step など
    target_id       TEXT        NOT NULL,
    url             TEXT        NOT NULL,
    label           TEXT,
    created_at      TEXT        NOT NULL,
    updated_at      TEXT        NOT NULL,
    deleted_at      TEXT        DEFAULT NULL,
    deleted_by      TEXT        REFERENCES members(member_id),
    source          TEXT        NOT NULL,
    history_ref     TEXT
);


-- ============================================================
-- インデックス
-- ============================================================

-- members
CREATE INDEX idx_members_discord_id ON members(discord_id);
CREATE INDEX idx_members_is_active ON members(is_active);

-- member_discord_accounts
CREATE INDEX idx_member_discord_accounts_member_id ON member_discord_accounts(member_id);
CREATE INDEX idx_member_discord_accounts_discord_id ON member_discord_accounts(discord_id);

-- member_devices
CREATE INDEX idx_member_devices_discord_account_id ON member_devices(discord_account_id);

-- member_login_history
CREATE INDEX idx_member_login_history_discord_account_id ON member_login_history(discord_account_id);
CREATE INDEX idx_member_login_history_logged_in_at ON member_login_history(logged_in_at);

-- activity_sessions
CREATE INDEX idx_activity_sessions_date ON activity_sessions(date);
CREATE INDEX idx_activity_sessions_venue_id ON activity_sessions(venue_id);

-- attendance
CREATE INDEX idx_attendance_plans_session_id ON attendance_plans(session_id);
CREATE INDEX idx_attendance_plans_member_id ON attendance_plans(member_id);
CREATE INDEX idx_attendance_records_session_id ON attendance_records(session_id);
CREATE INDEX idx_attendance_records_member_id ON attendance_records(member_id);
CREATE INDEX idx_attendance_notes_plan_id ON attendance_notes(plan_id);

-- surveys
CREATE INDEX idx_surveys_created_by ON surveys(created_by);
CREATE INDEX idx_survey_responses_survey_id ON survey_responses(survey_id);
CREATE INDEX idx_survey_responses_discord_account_id ON survey_responses(discord_account_id);

-- tasks
CREATE INDEX idx_tasks_parent_task_id ON tasks(parent_task_id);
CREATE INDEX idx_tasks_status ON tasks(status);
CREATE INDEX idx_tasks_planned_end_at ON tasks(planned_end_at);

-- task_assignments
CREATE INDEX idx_task_assignments_task_id ON task_assignments(task_id);
CREATE INDEX idx_task_assignments_member_id ON task_assignments(member_id);

-- procedure
CREATE INDEX idx_procedure_instances_task_id ON procedure_instances(task_id);
CREATE INDEX idx_procedure_instance_steps_instance_id ON procedure_instance_steps(instance_id);

-- urls
CREATE INDEX idx_urls_target ON urls(target_type, target_id);

-- notifications
CREATE INDEX idx_notification_timings_rule_id ON notification_timings(rule_id);