# Fuzoku Tracker — CLAUDE.md

風俗求人媒体（ガールズヘブン・バニラ）の順位監視・更新最適化SaaS。
このファイルはClaude Codeが作業を開始する前に必ず読むこと。

---

## プロジェクト構造

```
ranking-system/
├── python/
│   ├── analysis/
│   │   └── analyze.py          # 解析エンジン（メイン）
│   ├── scraping/
│   │   ├── monitor.py          # 監視ループ
│   │   ├── scrape_girlsheaven.py
│   │   └── scrape_vanilla.py
│   ├── tools/
│   │   ├── get_access_girlsheaven.py  # GHアクセス統計取得
│   │   ├── get_shop_name.py           # 店名自動取得
│   │   ├── set_benry.py               # ベンリー自動設定
│   │   ├── set_vanilla.py             # バニラ自動設定
│   │   ├── backup.py
│   │   ├── get_update_count_girlsheaven.py
│   │   └── get_update_count_vanilla.py
│   └── config/
│       ├── database.py         # DB接続（local=True→VPS内DB, local=False→カゴヤDB）
│       └── encryption.py
├── php/
│   ├── client/                 # クライアント管理画面（VPS）
│   │   ├── dashboard.php
│   │   ├── timeline.php
│   │   ├── hourly.php
│   │   ├── history.php
│   │   ├── scraping_log.php
│   │   ├── settings.php        # 4タブ（media/tool/monitor/timing）
│   │   ├── notices.php
│   │   ├── faq.php
│   │   └── api/
│   │       └── get_shop_name.php
│   └── owner/                  # オーナー管理画面（カゴヤ）
│       ├── dashboard.php
│       ├── clients.php
│       ├── client_edit.php
│       ├── notices.php
│       └── faq.php
└── CLAUDE.md
```

---

## 技術スタック

| 種別 | 技術 |
|------|------|
| バックエンド（スクレイピング・解析） | Python 3.12 / Playwright / pandas |
| フロントエンド | PHP 8.x / Bootstrap 5 / Chart.js 4.x |
| DB（カゴヤ） | MySQL 5.7 (`lerisa_rankingsystem`) |
| DB（VPS） | MySQL (`rankradar_data`) |
| AI | Anthropic Claude Haiku API（理由文生成） |
| インフラ | カゴヤ共用サーバー S12 + VPS Windows Server 2022 |

---

## インフラ・デプロイ

### カゴヤサーバー
- URL: `https://lerisa.kir.jp/rankradar/ranking-system/php/owner/`
- デプロイ（**自宅PCのコマンドプロンプト**から実行）:
  ```
  ssh -p 10022 -i C:\ssh\private.key lerisa@lerisa.kir.jp "cd ~/public_html/rankradar/ranking-system && git pull origin main"
  ```

### VPS（Windows Server 2022 / IP: 133.18.137.226）
- クライアント画面URL: `http://133.18.137.226/rankradar/php/client/`
- Apacheデプロイ（**VPS Tera Term**から実行）:
  ```
  cd C:\Apache24\htdocs\rankradar
  git pull origin main
  ```
- Pythonデプロイ（**VPS Tera Term**から実行）:
  ```
  cd C:\apps\ranking_system
  git pull origin main
  ```
- Pythonパス: `C:\apps\ranking_system\venv\Scripts\python.exe`

### 指示の出し方ルール（重要）
Claude Codeへの指示と、それ以外の作業（phpMyAdmin・Tera Term等）は**必ず分けて**指示すること。

---

## データベース

### DB接続の使い分け（重要）
```python
execute_query(sql, params, local=True)   # VPS内DB（rankradar_data）
execute_query(sql, params, local=False)  # カゴヤDB（lerisa_rankingsystem）
```

### カゴヤDB 主要テーブル
| テーブル | 重要カラム |
|---------|-----------|
| `media_settings` | client_id, media_name, login_id, login_pass, monitor_url, max_update_count, is_active, **shop_name** |
| `monitor_settings` | client_id, start_time=**06:00（固定）**, end_time=**05:00（固定）**, interval_min |
| `excluded_times` | client_id, start_time, end_time（TIME型・日をまたぐ対応済み） |
| `fixed_update_times` | client_id, update_time（**TIME型・分単位**・例:15:02:00） |
| `update_timing_settings` | client_id, update_mode（**1=集中型, 2=分散型**） |
| `faqs` | id, category, question, answer, sort_order, is_published |

### VPS内DB 主要テーブル
| テーブル | 重要カラム |
|---------|-----------|
| `scraping_results` | client_id, media_name, shop_name, \`rank\`（予約語注意）, fetched_at |
| `analysis_suggestions` | client_id, media_name, **suggest_time**（VARCHAR・時間ごとに1行）, reason_text, suggested_at |
| `gh_access_stats` | client_id, stat_date, hour_slot（HH:MM）, total_access（30分刻み48件） |
| `error_logs` | client_id, media_name, error_type, error_message, occurred_at |

### analysis_suggestionsの構造変更（重要）
```
旧（廃止）: suggest_times（JSON配列）で1行
新（現行）: suggest_time（VARCHAR）で時間ごとに1行・reason_textカラムあり
```
set_benry.py・set_vanilla.pyも新構造に対応済み。

---

## analyze.pyの仕様（最重要）

### 2ステップアルゴリズム
```
ステップ①: gh_access_statsの実アクセスデータで30分枠ごとの更新回数を配分
  - モードA（集中型）: 平均アクセスの50%未満の枠をスキップ
  - モードB（分散型）: 全30分枠を対象

ステップ②: 各30分枠内で競合動向をもとに最適な3分スロットを選出
  - GH: 競合更新直後スコア + 競合静穏スコア
  - バニラ: 競合更新直後スコア + 競合静穏スコア + 1ページ目滞在スコア
```

### 重要な設計判断
- **PEAK_HOURS廃止**: 固定のピーク時間帯は廃止。gh_access_statsの実データのみを使用
- **絶対更新時間**: 3分スロットに丸めず設定時刻そのまま（例:15:02）を選出に追加
- **除外時間帯**: 日をまたぐ設定対応（23時→翌4時の場合 `h >= 23 or h <= 4`）
- **バニラの05時台**: set_vanilla.pyが05時台に実行できないため自動除外
- **gh_access_statsがない場合**: 直近7日平均を試み、それもなければ解析スキップ

### 競合更新の判定
```python
# 5位以上改善した場合のみ更新と判定（単純な順位上昇は除外）
rival_df["rank_improved"] = (rival_df["prev_rank"] - rival_df["rank"]) >= 5
```

### Claude API連携
- モデル: `claude-haiku-4-5-20251001`
- 各提案時間ごとに個別の理由文を生成（80文字程度）
- 形式: 「自店状況：〇〇。競合動向：〇〇。結論：〇〇。」
- 競合店3店以下→店名明記、4店以上→「主要競合店」と表現
- ネガティブな表現（「効果が限定的」「優先度が低い」等）は禁止

---

## monitor.pyの仕様

```python
INTERVAL_JITTER = 10        # ±10秒のランダム揺らぎ
WAIT_OUTSIDE_HOURS = 60     # 監視時間外のポーリング間隔（秒）
```

- 定刻スロット実行（00分・03分・06分...の3分間隔）
- 毎日05:58に自動終了
- 00:00〜04:00のランダムな時刻にget_access_girlsheaven.pyを1日1回自動実行

---

## 店名の取得先（重要）

```python
# GH・バニラ両方ともmedia_settingsから取得（clientsはフォールバック）
SELECT shop_name FROM media_settings
WHERE client_id = %s AND media_name = %s AND is_active = 1 LIMIT 1
```

---

## タスクスケジューラ（VPS）

| 時刻 | タスク | スクリプト |
|------|--------|-----------|
| 05:20 | Backup | backup.py 1 |
| 05:30 | GetCount Vanilla | get_update_count_vanilla.py 1 |
| 05:40 | GetCount GH | get_update_count_girlsheaven.py 1 |
| 05:50 | Analyze | analyze.py 1 |
| 05:55 | SetBenry | set_benry.py 1 |
| 05:57 | SetVanilla | set_vanilla.py 1 |
| 06:00 | Monitor | monitor.py 1 |
| 06:05〜翌05:00（5分ごと） | Watchdog | watchdog.bat |

### Watchdog タスクスケジューラ登録手順（VPS Tera Term）

```
schtasks /create /tn "RankRadar_Watchdog_Client1" ^
  /tr "C:\apps\ranking_system\watchdog.bat" ^
  /sc minute /mo 5 ^
  /st 06:05 /et 05:00 /k ^
  /ru SYSTEM /f
```

- タスク名: `RankRadar_Watchdog_Client1`
- 実行ファイル: `C:\apps\ranking_system\watchdog.bat`
- 開始時刻: `06:05`（monitor.py 起動5分後）
- 繰り返し: 5分ごと・終了時刻 `05:00`（`/k` で時間超過時もkill）
- 実行ユーザー: SYSTEM

---

## PHP画面の重要な仕様

### サイドバー（全クライアント画面共通）
「各種設定」はアコーディオンメニュー。settings.phpを開いている時は自動展開。
サブメニュー: 媒体設定・更新ツール設定・モニタリング時間設定・更新タイミング設定

### history.php
- 時間昇順（suggest_time ASC）で表示
- 絶対更新時間に一致する時間帯は「絶対更新時間対応」バッジ（緑・太字）を表示、reason_textは非表示
- fixed_update_timesから `TIME_FORMAT(update_time, '%H:%i')` で比較

### scraping_log.php
- 05:00〜05:59はメンテナンス時間帯（グレー表示）
- 圏外（rank=0）はオレンジ表示

### hourly.php
- Chart.js 4.x使用（`getPixelForValue`を使うこと・`getPixelForIndex`は3.x以前のAPIで動作しない）
- 05:00〜05:59の背景グレー表示

### settings.php（モニタリング時間設定タブ）
- 開始時刻（06:00）・終了時刻（05:00）は固定・変更不可（readonly）
- 05:01〜05:59は設定不可
- モニタリング間隔は最短3分

### 新規クライアント登録時（client_edit.php）
monitor_settingsに自動挿入:
```sql
INSERT INTO monitor_settings (client_id, start_time, end_time, interval_min)
VALUES (%s, '06:00:00', '05:00:00', 3)
ON DUPLICATE KEY UPDATE start_time=start_time, end_time=end_time, interval_min=interval_min
```

---

## 手動実行コマンド（VPS Tera Term）

```bash
# analyze.py
C:\apps\ranking_system\venv\Scripts\python.exe C:\apps\ranking_system\python\analysis\analyze.py 1

# GHアクセス統計
C:\apps\ranking_system\venv\Scripts\python.exe C:\apps\ranking_system\python\tools\get_access_girlsheaven.py 1

# GH店名取得
C:\apps\ranking_system\venv\Scripts\python.exe C:\apps\ranking_system\python\tools\get_shop_name.py 1 girlsheaven

# バニラ店名取得
C:\apps\ranking_system\venv\Scripts\python.exe C:\apps\ranking_system\python\tools\get_shop_name.py 1 vanilla

# monitor.py再起動
taskkill /f /im python.exe
schtasks /run /tn "RankRadar_Monitor_Client1"
```

---

## 現在の進捗

### 完了済み
- [x] GH・バニラのスクレイピング（3分間隔・定刻スロット実行）
- [x] analyze.py 2ステップアルゴリズム実装
- [x] gh_access_statsをピークスコアに反映（PEAK_HOURS廃止）
- [x] 集中型・分散型モード（モードA/B）実装
- [x] 除外時間帯の日をまたぐ対応
- [x] 絶対更新時間の分単位対応（TIME型）
- [x] Claude APIによる理由文の自動生成
- [x] 店名自動取得（GH・バニラ）
- [x] get_access_girlsheaven.py（Highchartsデータ取得）
- [x] Q&Aページ（クライアント・オーナー）
- [x] サイドバーのアコーディオン化
- [x] history.phpの時間昇順・絶対更新時間バッジ
- [x] hourly.phpのメンテナンス時間帯背景色（Chart.js 4.x対応中）

### PENDING（未確認・未完了）
- [ ] hourly.phpのメンテナンス背景色表示確認（Chart.js 4.x対応マージ後）
- [ ] set_benry.py・set_vanilla.pyの新テーブル構造での動作確認
- [ ] オーナー画面faq.phpのカゴヤデプロイ確認

---

## 次にやること（優先順位順）

1. hourly.phpのメンテナンス背景色の表示確認
2. set_benry.py・set_vanilla.pyの動作確認
3. 新しいチャットへの引き継ぎ

---

## 重要な設計判断（経緯）

| 決定事項 | 理由 |
|---------|------|
| PEAK_HOURS廃止 | gh_access_statsの実データの方が精度が高いため |
| analysis_suggestionsを1行ずつに変更 | reason_textを時間ごとに管理するため |
| 絶対更新時間をTIME型で保存 | 分単位（15:02等）を正確に扱うため。3分スロットへの丸めは廃止 |
| バニラの05時台を自動除外 | set_vanilla.pyが05:55以降に実行されるため05時台に更新設定しても意味がない |
| 競合更新判定を「5位以上改善」に変更 | 単純な順位変動（自然変動）と実際の更新を区別するため |
| モニタリング開始・終了時刻を固定 | タスクスケジューラの実行時刻と整合性を保つため |
| 店名取得をmedia_settingsに変更 | GH・バニラで異なる店名表記に対応するため |
| Chart.js 4.xでgetPixelForValueを使用 | getPixelForIndexはChart.js 3.x以前のAPIのため |
