# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

**"点线得面"** - A WeChat Mini Program for collaborative meeting point selection. Users create rooms, share with friends, each member adds their location, and the system calculates the optimal meeting venue based on everyone's coordinates. Maximum 8 members per room, and rooms expire after 30 days.

**Architecture**: WeChat Mini Program frontend + WeChat Cloud Functions backend + MeetSpot Python API (external service)

## Repository Structure

```
WoMenQuNaJu/
├── miniprogram/          # WeChat Mini Program frontend
│   ├── pages/            # Page components (index, create-room, room, result)
│   ├── app.js/json/wxss  # Application entry
│   └── images/           # Static assets
├── cloudfunctions/       # WeChat Cloud Functions (Node.js)
│   ├── calculateMeetSpot/     # Core: calls MeetSpot API to calculate venues
│   ├── createRoom/            # Create a new room
│   ├── joinRoom/              # Add member to room (max 8 people)
│   ├── updateLocation/        # Update current user's location
│   ├── updateMemberLocation/  # Update test member's location (dev only)
│   ├── updateRoomInfo/        # Update room metadata
│   ├── removeMember/          # Remove member from room
│   ├── deleteRoom/            # Delete entire room
│   ├── cleanExpiredRooms/     # Scheduled: delete rooms older than 30 days
│   ├── addTestMember/         # Add test member (dev only)
│   └── getOpenId/             # Get user's WeChat OpenID
└── MeetSpot/             # Python FastAPI backend (has its own CLAUDE.md)
```

**Key Insight**: The WeChat Mini Program is a lightweight frontend/coordinator. The heavy lifting (geocoding, POI search, venue ranking) is delegated to the MeetSpot FastAPI service via the `calculateMeetSpot` cloud function.

## WeChat Mini Program Development

### Setup

```bash
# Install WeChat Developer Tools
# Download from: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html

# Open project in WeChat Developer Tools
# Project directory: d:\download\WoMenQuNaJu
# AppID: wxb7e6344198f6526f (from project.config.json)
```

**Cloud Functions**: Each function in `cloudfunctions/` needs dependencies installed separately:

```bash
# For cloud functions that use axios (like calculateMeetSpot)
cd cloudfunctions/calculateMeetSpot
npm install

# Repeat for other functions with package.json
cd ../createRoom
npm install
```

**Deploy Cloud Functions**: Use WeChat Developer Tools GUI to upload cloud functions, or use CLI:

```bash
# Upload a single cloud function
miniprogram-ci cloud uploadFunction --functionName calculateMeetSpot
```

### Testing

**Local Testing**: Use WeChat Developer Tools simulator. The simulator supports:
- Cloud function calls
- Cloud database operations
- Location services (mocked)

**Backend URL Configuration**:
- `calculateMeetSpot/index.js:78` contains `BACKEND_URL` environment variable
- Default: `https://meetspot.onrender.com` (production MeetSpot API)
- For local testing: Set cloud function env var to `http://localhost:8000` (requires ngrok or similar for Mini Program to reach local machine)

### Data Model

**Cloud Database Collection**: `rooms`

**Data Retention**: Rooms are automatically deleted 30 days after creation. A scheduled cloud function `cleanExpiredRooms` runs periodically to clean up expired data.

```javascript
{
  _id: "auto-generated",
  _openid: "creator's openid",
  createdAt: ServerDate,
  expireTime: Date,  // Auto-delete 30 days after creation
  status: "active" | "calculating" | "calculated",
  name: "聚会名称",
  meetTime: "2026-02-10 18:00",
  keywords: "咖啡馆",
  requirements: "parking, quiet",
  members: [
    {
      openid: "wx-openid",
      avatarUrl: "https://...",
      nickName: "用户昵称",
      location: {
        lng: 116.32,
        lat: 39.99,
        address: "北京大学",
        name: "北京大学"
      },
      joinedAt: Date,
      isTestMember: false  // true for dev test members
    }
  ],
  result: {
    success: true,
    venues: [...],
    center: { lat: 39.99, lng: 116.32 },
    html_url: "/workspace/js_src/recommendation_xxx.html"
  },
  lastCalculateTime: ServerDate,
  lastCalculatedMembers: "openid1_lat_lng,openid2_lat_lng,..."  // Snapshot for duplicate detection
}
```

**Member Limit**: Maximum 8 members per room. Enforced in `joinRoom` cloud function.

## Architecture Patterns

### Status Locking Pattern

**Problem**: Multiple users clicking "Calculate" simultaneously could trigger duplicate API calls.

**Solution**: `calculateMeetSpot` uses database-level status locking:

```javascript
// 1. Check if status is "calculating" (early return if locked)
if (data.status === 'calculating') return { msg: '正在计算中' }

// 2. Acquire lock
await db.collection('rooms').doc(roomId).update({ status: 'calculating' })

// 3. Call backend API
const result = await axios.post(BACKEND_URL, ...)

// 4. Release lock and save result
await db.collection('rooms').doc(roomId).update({
  status: 'calculated',
  result: result.data
})
```

**Exception Handling**: If API call fails, the `catch` block releases the lock by setting `status: 'active'`.

### Duplicate Calculation Prevention

**Problem**: Users might click "Calculate" multiple times without changing member locations.

**Solution**: `calculateMeetSpot/index.js:43-61` compares current member positions against `lastCalculatedMembers` snapshot:

```javascript
const currentSnapshot = validMembers
  .map(m => `${m.openid}_${m.location.lat}_${m.location.lng}`)
  .sort()
  .join(',')

if (currentSnapshot === data.lastCalculatedMembers) {
  return { isDuplicate: true, existingResult: data.result }
}
```

### Real-time Synchronization

**Pattern**: Database Watch API for live updates

**Implementation**: `miniprogram/pages/room/room.js:125-179`

```javascript
db.collection('rooms').doc(roomId).watch({
  onChange: snapshot => {
    // Sync room state, member list, calculation status
    // Auto-navigate to result page when status changes from 'calculating' to 'calculated'
  }
})
```

**Critical Detail**: Uses `previousStatus` tracking to only auto-navigate when calculation **just finished**, preventing navigation loops when users return to the room page.

### Test Member System

**Purpose**: Allow room creators to add mock members for testing without needing multiple WeChat accounts.

**Identification**: Test members have `isTestMember: true` and openid format `test_<timestamp>_<random>`

**Special Handling**:
- `addTestMember`: Creates member with random location (Beijing area)
- `updateMemberLocation`: Allows creator to update test member locations
- `removeMember`: Can delete test members
- UI shows "测试" badge and allows creator to edit their locations

## Cloud Functions

### Core Function: calculateMeetSpot

**Responsibility**: Bridge between Mini Program and MeetSpot API

**Flow**:
1. Validate: At least 2 members with locations
2. Check: Duplicate calculation detection
3. Lock: Set status to `calculating`
4. Transform: Extract `{ lng, lat, address, name }` from members
5. Call: POST to `BACKEND_URL/api/miniprogram/calculate`
6. Store: Save result to database, update status to `calculated`

**Environment Variables**: Set in WeChat Cloud Console
- `BACKEND_URL`: MeetSpot API endpoint (default: `https://meetspot.onrender.com`)

**Timeout**: 30 seconds (axios timeout). MeetSpot API typically responds in 3-15 seconds.

### Database Mutating Functions

| Function | Mutation | Permission Check |
|----------|----------|------------------|
| `createRoom` | Add document to `rooms` | Anyone (creator = caller's openid) |
| `joinRoom` | Push to `members` array | Anyone (duplicate check by openid) |
| `updateLocation` | Update member's location in array | Own openid only |
| `updateMemberLocation` | Update test member location | Creator only |
| `updateRoomInfo` | Update room metadata | Creator only (checked via `_openid`) |
| `removeMember` | Remove from `members` array | Self-exit OR creator removing others |
| `deleteRoom` | Delete entire document | Creator only |

**Security Note**: Cloud Functions automatically add `_openid` field (caller's WeChat ID) to database writes. Permission checks compare `wxContext.OPENID` against stored `_openid`.

## MeetSpot Backend Integration

The `MeetSpot/` subdirectory is a separate Python/FastAPI project. See [MeetSpot/CLAUDE.md](MeetSpot/CLAUDE.md) for details.

**Key Integration Point**: `calculateMeetSpot` cloud function calls `POST /api/miniprogram/calculate` endpoint.

**Expected Request**:
```json
{
  "locations": [
    { "lng": 116.32, "lat": 39.99, "address": "北京大学", "name": "北京大学" }
  ],
  "keywords": "咖啡馆",
  "requirements": "parking, quiet"
}
```

**Expected Response**:
```json
{
  "success": true,
  "venues": [ /* Array of ranked venues */ ],
  "center": { "lat": 39.99, "lng": 116.32 },
  "html_url": "/workspace/js_src/recommendation_xxx.html"
}
```

**To run MeetSpot locally**:
```bash
cd MeetSpot
python web_server.py
# Backend will be at http://localhost:8000
```

## Common Development Tasks

### Adding a New Cloud Function

1. Create directory: `cloudfunctions/myFunction/`
2. Add `index.js`:
```javascript
const cloud = require('wx-server-sdk')
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })

exports.main = async (event, context) => {
  const wxContext = cloud.getWXContext()
  // Your logic here
  return { success: true }
}
```
3. Add `package.json` if using npm dependencies
4. Install dependencies: `npm install` (in function directory)
5. Upload via WeChat Developer Tools

### Modifying Room Schema

**Important**: WeChat Cloud Database is schemaless, but for consistency:

1. Update `createRoom/index.js` for new fields in initial data
2. Add migration logic in `calculateMeetSpot` or other functions if transforming existing data
3. Update TypeScript interfaces if using typed Mini Program (this project uses plain JS)

### Changing Keywords/Requirements Options

**Keywords Picker**: `miniprogram/pages/room/room.js:10`
```javascript
keywordOptions: ['咖啡馆', '餐厅', '公园', ...]
```

**Requirements**: Free-text input, no predefined options. MeetSpot API parses natural language.

### Debugging Cloud Functions

**Method 1**: WeChat Developer Tools Console
- Right-click function → "Upload and Debug"
- Use `console.log()` - logs appear in Cloud Development Console

**Method 2**: Local Cloud Function Emulation
- Install `miniprogram-ci`
- Use `npx miniprogram-ci cloud emulator`

## Performance Considerations

**Cold Start**: Cloud functions have ~500ms-2s cold start. First calculation in a room may feel slow.

**Database Watch**: Each page maintains one watch connection. WeChat limits 5 concurrent watches per user. Close watches in `onUnload()`.

**Calculation Time**: Typical flow takes 5-15 seconds:
- Cloud function startup: 0.5-2s
- MeetSpot API call: 3-12s (includes geocoding, POI search, ranking)
- Database write: 0.1-0.5s

**Optimization**: Duplicate calculation prevention saves ~90% of redundant API calls.

## Deployment

### Mini Program

1. Update version in WeChat Developer Tools
2. Click "Upload" → Enter version number and description
3. Submit for review in WeChat Public Platform
4. After approval, release to production

### Cloud Functions

**Individual Upload**: Right-click function → "Upload and Deploy: Cloud Installation Dependencies"

**Batch Upload**: Cloud Development Console → Cloud Functions → Batch upload

**CI/CD**: Use `miniprogram-ci` for automated deployment:
```bash
miniprogram-ci upload --project /path/to/project \
  --appid wxb7e6344198f6526f \
  --version 1.0.0 \
  --desc "Update description"
```

## Debugging Common Issues

| Issue | Cause | Solution |
|-------|-------|----------|
| "未找到云函数" | Function not uploaded | Upload via Developer Tools |
| "正在计算中" stuck | Lock not released after error | Manually set `status: 'active'` in database |
| Empty result page | BACKEND_URL unreachable | Check cloud function logs, verify MeetSpot API is running |
| Location picker fails | Missing permission | Check `app.json` → `permission` → `scope.userLocation` |
| Watch connection lost | Network interruption | Page will auto-reconnect on next data change |
| "至少需要2人" error | Only 1 member has location | Add more member locations or use test members |

**Logs**: Cloud Development Console → Cloud Functions → Function logs (search by `requestId`)

## Code Style

- **JavaScript**: 4-space indent, ES6+ syntax, no semicolons (Mini Program convention)
- **Cloud Functions**: Use `async/await`, avoid callbacks
- **Naming**: `camelCase` for variables/functions, `PascalCase` for pages/components
- **Comments**: Chinese for user-facing strings, English for technical comments acceptable

## Related Documentation

- WeChat Mini Program Docs: https://developers.weixin.qq.com/miniprogram/dev/framework/
- Cloud Functions Guide: https://developers.weixin.qq.com/miniprogram/dev/wxcloud/basis/capabilities.html
- MeetSpot Backend: See `MeetSpot/CLAUDE.md` for Python/FastAPI development guide
