# Multi-Rider (猎影哨兵) Deployment Reference

Flask + YOLO detection service for police/public security work.

## Project Structure

- `app.py` — Flask entry, registers Blueprints
- `shared/config/config.py` — loads `app.env`, `.env`, `ops/app.env`
- `modules/` — detection, face, dispatch, training, dashboard, graph, score, profile, ai_analyst
- `requirements.txt` — includes torch, ultralytics, opencv, oracledb
- `docker-compose.yml` — web + worker services

## Key Env Vars

```
APP_PORT=5001
KINGBASE_HOST=127.0.0.1
KINGBASE_PORT=54321
KINGBASE_DB=yfywk
KINGBASE_USER=ywkuser
KINGBASE_PASSWORD=123
KINGBASE_SCHEMA=ywdata
WCNR_SCHEDULER_ENABLED=0  # disable scheduler for dev
YOLO_TELEMETRY=false       # must be false for intranet
```

## Deployment Steps (actual)

```bash
# 1. Download via API (git clone failed through proxy)
curl -L -s -H "Authorization: token $GITHUB_TOKEN" \
  "https://api.github.com/repos/mifengac/multi-rider/zipball/feature/ai-analyst-rag" \
  -o /tmp/multi-rider-rag.zip

# 2. Extract
python3 -c "
import zipfile, os, shutil
with zipfile.ZipFile('/tmp/multi-rider-rag.zip', 'r') as z:
    z.extractall('/tmp/extract')
src = os.path.join('/tmp/extract', os.listdir('/tmp/extract')[0])
shutil.move(src, '/home/longshao/multi-rider-rag')
shutil.rmtree('/tmp/extract')
"

# 3. Setup
cd /home/longshao/multi-rider-rag
python3 -m venv .venv && source .venv/bin/activate
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple Flask==3.0.0 requests==2.31.0 httpx==0.28.1 psycopg2-binary==2.9.10
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple ultralytics==8.4.27 numpy==2.0.2 opencv-python-headless Pillow==10.4.0 tqdm==4.67.1
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple torch==2.11.0 torchvision==0.26.0

# 4. Configure
# Created app.env with KingBase config pointing to existing kingbasev8 container

# 5. Create dirs
mkdir -p model/yolo/{production,foundation} model/{insightface,assets} output upload_tmp datasets face_data train_runs

# 6. Start
source .venv/bin/activate && python app.py
```

## Health Check Result

App returns degraded status when model files are missing (expected — models not in repo). Core functionality (SQLite, task queue, directories) works fine.

## KingBase Connection

Connects to existing `kingbasev8` Docker container on port 54321 (user: ywkuser, db: yfywk). Uses psycopg2-binary (PostgreSQL-compatible protocol).

## Database Migrations

### Prerequisites

KingBase container must be running:
```bash
docker start kingbasev8
sleep 15  # Wait for DB to be ready (takes 10-30s after restart)
```

### Run Built-in Migrations

```bash
cd /home/longshao/multi-rider-rag
source .venv/bin/activate
python scripts/run_migrations.py
```

This executes `scripts/sql/v*.sql` in order:
- `v1_0_init_tables.sql` — Creates `jcgkzx_monitor` schema, `wcnr_alert` and `wcnr_score_history` tables
- `v1_1_sample_data.sql` — Inserts sample alert data

### Run Full DDL

The `scripts/ddl_create_tables.sql` creates all business tables not covered by migrations:

```bash
source .venv/bin/activate
python3 -c "
import os, psycopg2
conn = psycopg2.connect(
    host=os.getenv('KINGBASE_HOST','127.0.0.1'),
    port=int(os.getenv('KINGBASE_PORT','54321')),
    database=os.getenv('KINGBASE_DB','yfywk'),
    user=os.getenv('KINGBASE_USER','ywkuser'),
    password=os.getenv('KINGBASE_PASSWORD','123')
)
cursor = conn.cursor()
cursor.execute(open('scripts/ddl_create_tables.sql','r').read())
conn.commit()
print('DDL executed successfully')
cursor.close(); conn.close()
"
```

Tables created by DDL:
- `wcnr_target_pool` — Target person pool
- `wcnr_ryrl_gj` — Face recognition trajectory
- `wcnr_ly_checkin` — Hotel check-in records
- `wcnr_rk_zp` — Population photos
- `wcnr_czrk` — Resident population info
- `wcnr_score` — Risk scores
- `wcnr_score_history` — Score history
- `wcnr_alert` — Risk alerts

### ETL Scripts (Optional)

`scripts/etl_init_target_pool.sql` and `scripts/etl_fill_business_tables.sql` populate tables from business database. These require the `ywdata` schema with actual data tables (`zq_zfba_wcnr_xyr`, `b_per_qswcnrbczj`, etc.) — skip if those don't exist.

### Verify Tables

```python
import psycopg2
conn = psycopg2.connect(host='127.0.0.1', port=54321, database='yfywk', user='ywkuser', password='123')
cursor = conn.cursor()
cursor.execute("SELECT table_name FROM information_schema.tables WHERE table_schema='jcgkzx_monitor' ORDER BY table_name")
for t in cursor.fetchall(): print(f"  - {t[0]}")
```
