Files
mp-pilates/EXPLORATION_SUMMARY.md
2026-04-05 13:25:54 +08:00

429 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 卡种管理 (Card Types Management) - Complete Exploration Summary
**Date**: 2026-04-05
**Project**: MP-Pilates (WeChat Mini-Program for Pilates Studio Booking)
**Focus**: Card types (卡种) admin feature
---
## 📦 What Was Explored
A comprehensive exploration of the **card types management system** across all three tiers of the application:
- Frontend (Vue 3 + Uni-app)
- Backend (NestJS)
- Database (Prisma/MySQL)
- Shared Types
### Total Files Analyzed: **13 files, ~1,800 lines of code**
---
## 📚 Documentation Generated
Three comprehensive documentation files have been created in the project root:
### 1. **CARD_TYPES_ANALYSIS.md** (Complete Technical Guide)
- **Sections**: 11 major sections
- **Content**:
- Database schema details
- Shared types and DTOs
- Server-side implementation (controller, service, DTOs)
- Frontend admin page structure
- Admin store (Pinia) implementation
- Complete workflow flows
- API communication details
- Price handling
- Card type categories
- Field requirements & validation table
- **Detailed bug analysis**: Edit popup closes immediately
### 2. **CARD_TYPES_FLOW_DIAGRAM.txt** (Visual Architecture)
- **Content**:
- Database tier diagram (CardType model, enums, soft delete)
- API tier diagram (endpoints, validators, DTOs)
- Shared types tier
- Frontend tier (page structure, store, components)
- Complete operation flows (Add, Edit, Toggle, Delete)
- **Bug analysis with solutions** (3 solution options)
### 3. **CARD_TYPES_QUICK_REFERENCE.md** (Quick Lookup)
- **Sections**: 13 quick-reference sections
- **Content**:
- File quick links with line numbers
- Key data model
- API endpoints
- DTOs & validation rules
- UI components
- Form fields
- Operations guide
- React refs & state
- Admin store methods
- Bug explanation and solutions
- Price handling notes
- Testing checklist
- Card type categories
---
## 🎯 Key Findings
### Data Structure
```
CardType
├── id (UUID)
├── name (卡种名称)
├── type (TIMES | DURATION | TRIAL)
├── totalTimes (次卡的次数)
├── durationDays (有效天数)
├── price (现价,单位:分)
├── originalPrice (原价,可选)
├── description (描述)
├── isActive (上架状态)
├── sortOrder (显示顺序)
└── timestamps
```
### Three Card Type Categories
1. **次卡 (TIMES)**: Class count-based (e.g., 10 classes)
2. **月卡 (DURATION)**: Time period-based (e.g., 30 days)
3. **体验卡 (TRIAL)**: Trial cards
### Core Operations
-**Create**: Add new card types
-**Read**: View all cards (admin) or active cards (public)
-**Update**: Edit card details or toggle status
-**Delete**: Soft delete (sets isActive=false)
### API Endpoints
```
GET /membership/card-types (public)
GET /admin/card-types (admin only)
POST /admin/card-types (admin only)
PUT /admin/card-types/:id (admin only)
DELETE /admin/card-types/:id (admin only)
```
---
## 🐛 Critical Bug Identified
### **Edit Modal Closes Immediately on Tap**
**Symptom**: When user taps the [编辑] button, the edit form modal appears and then instantly closes.
**Root Cause**: Event propagation issue
- User taps [编辑] button
- `openEdit()` sets `showModal = true`
- Modal renders in the same event tick
- Tap event propagates to `modal-mask` element
- `@tap.self="closeModal"` fires immediately
- Modal closes
**Current Code (Buggy)**:
```vue
<view class="ct-action-btn edit-btn" @tap="openEdit(ct)">
<text>编辑</text>
</view>
<view v-if="showModal" class="modal-mask" @tap.self="closeModal">
<!-- form -->
</view>
```
**Recommended Fix (Option 1 - Simplest)**:
```vue
<view class="ct-action-btn edit-btn" @tap.stop="openEdit(ct)">
<!-- Add .stop modifier to stop event propagation -->
</view>
```
**Alternative Fixes**: See CARD_TYPES_QUICK_REFERENCE.md for 2 additional solutions using nextTick() or state guards.
---
## 📂 File Inventory
### Frontend Files
| File | Purpose | Lines |
|------|---------|-------|
| `packages/app/src/pages/admin/card-types.vue` | Admin page (ADD, EDIT, DELETE, TOGGLE) | 607 |
| `packages/app/src/stores/admin.ts` | Pinia store (state + API calls) | 198 |
| `packages/app/src/utils/request.ts` | HTTP request utilities | 80 |
| `packages/app/src/utils/format.ts` | Price & date formatting | 46 |
### Backend Files
| File | Purpose | Lines |
|------|---------|-------|
| `packages/server/src/membership/membership.controller.ts` | API endpoints | 68 |
| `packages/server/src/membership/membership.service.ts` | Business logic | 173 |
| `packages/server/src/membership/dto/create-card-type.dto.ts` | Create validation | 45 |
| `packages/server/src/membership/dto/update-card-type.dto.ts` | Update validation | 49 |
### Database Files
| File | Purpose | Lines |
|------|---------|-------|
| `packages/server/prisma/schema.prisma` | DB schema definition | 205 |
### Shared/Types Files
| File | Purpose | Lines |
|------|---------|-------|
| `packages/shared/src/types/card-type.ts` | CardType, CreateCardTypeDto, UpdateCardTypeDto | 39 |
| `packages/shared/src/enums.ts` | CardTypeCategory enum | 47 |
| `packages/shared/src/types/api.ts` | API response types | 20 |
| `packages/shared/src/types/membership.ts` | Membership types | 19 |
**Total**: 13 files, ~1,800 lines analyzed
---
## 🔄 Complete Workflow
### Adding a New Card Type
```
User → [ 新增卡种] → openAdd()
Modal appears with empty form
User fills: name, type, price, durationDays
[确认] → submitForm()
Validate inputs → Build payload → adminStore.createCardType()
POST /admin/card-types → Backend creates card
Refetch list → Modal closes → Page updates
```
### Editing a Card Type
```
User → [编辑] on card → openEdit(card)
Modal appears with card data
User edifies fields
[确认] → submitForm()
Validate inputs → Build payload → adminStore.updateCardType(id, payload)
PUT /admin/card-types/:id → Backend updates card
Refetch list → Modal closes → Page updates
```
### Toggling Status (上架/下架)
```
User → [上架/下架] button → toggleActive(card)
adminStore.updateCardType(id, { isActive: !current })
PUT /admin/card-types/:id → Backend toggles isActive
Refetch list → Card UI updates (opacity, status tag, button text)
```
### Deleting a Card Type
```
User → [删除] button → confirmDelete(card)
Confirmation dialog appears
User confirms
adminStore.deleteCardType(id)
DELETE /admin/card-types/:id → Backend soft-deletes (isActive=false)
Refetch list → Page updates
```
---
## 💾 Database Details
### CardType Model
- **Storage**: MySQL table `card_types`
- **Primary Key**: UUID
- **Important Field**: `isActive` (boolean, default: true)
- **Delete Strategy**: Soft delete (set isActive=false, not actually removed)
- **Relationships**:
- One-to-many with Membership
- One-to-many with Order
### Indexes
- `isActive` (for filtering active cards)
- `sortOrder` (for ordering)
---
## 🎨 UI/UX Details
### Page Layout
```
┌─ Toolbar ─────────────┐
│ Count + Add button │
├──────────────────────┤
│ Loading skeleton │ (while loading)
├──────────────────────┤
│ Card List │
├──────────────────────┤
│ Modal (Add/Edit) │ (if showModal=true)
└──────────────────────┘
```
### Card Display
- **Header**: Colored band (type-specific gradient)
- **Status tag**: "销售中" or "已下架"
- **Content**: Name, price, description, meta info
- **Actions**: 3 buttons (编辑, 上架/下架, 删除)
- **Inactive styling**: opacity: 0.6 when isActive=false
### Modal Form
```
Title: 新增卡种 / 编辑卡种
Fields:
- 卡种名称 (text input)
- 类型 (picker)
- 现价 (digit)
- 原价 (digit, optional)
- 次数 (number, optional)
- 有效天数 (number, default: 90)
- 排序值 (number, default: 0)
- 描述 (textarea, optional)
Buttons: [取消] [确认/保存中...]
```
---
## 🔐 Security & Auth
### Authentication
- All admin endpoints require JWT Bearer token
- Token stored in localStorage and included in all requests
### Authorization
- Admin endpoints require `UserRole.ADMIN`
- Enforced via RolesGuard on backend
### Public Endpoints
- GET /membership/card-types (no auth needed)
- Returns only `isActive=true` cards
---
## 📊 Validation Rules
### On Create
| Field | Required | Validation |
|-------|----------|-----------|
| name | ✓ | Non-empty string |
| type | ✓ | One of: TIMES, DURATION, TRIAL |
| durationDays | ✓ | Int, Min: 1 |
| price | ✓ | Number, Min: 0 |
| totalTimes | - | Int, Min: 1 (optional) |
| originalPrice | - | Number, Min: 0 (optional) |
| description | - | String, Max: 200 (optional) |
| sortOrder | - | Int, Min: 0 (optional, default: 0) |
### On Update
- All fields optional (partial update)
- Can include `isActive` for toggling status
---
## 💡 Price Handling
**Critical**: Prices are stored as **integers (cents)**, not floats
- In DB: `98000` (cents)
- In API: `{ price: 98000 }`
- Display: `¥980.00` (using formatPrice utility)
**Conversion**:
```typescript
// Display
formatPrice(cents: number): string {
return (cents / 100).toFixed(2)
}
// Store (frontend → backend)
// User inputs: "980"
// Send as: 98000 (no need to convert, prices are already in cents in the UI)
```
---
## 🧪 Testing Recommendations
### Unit Tests Needed
- [ ] CardType service methods (create, update, delete)
- [ ] Card type validation (DTO validation)
- [ ] Price formatting utilities
### Integration Tests Needed
- [ ] Admin endpoints require ADMIN role
- [ ] Public endpoint returns only active cards
- [ ] Soft delete sets isActive=false
### E2E Tests Needed (Frontend)
- [ ] Create card flow
- [ ] Edit card flow (including bug fix)
- [ ] Toggle status flow
- [ ] Delete card flow
- [ ] Modal closes properly on submit
- [ ] Modal closes on outside tap
- [ ] Modal closes on cancel button
---
## 🚀 Next Steps (If Implementing Bug Fix)
1. **Locate file**: `packages/app/src/pages/admin/card-types.vue`
2. **Find**: Line 67 with `<view class="ct-action-btn edit-btn" @tap="openEdit(ct)">`
3. **Change**: `@tap="openEdit(ct)"``@tap.stop="openEdit(ct)"`
4. **Also check**: Lines 6 and 77 (other buttons that might have same issue)
5. **Test**: Try editing a card - modal should stay open
---
## 📖 How to Use This Documentation
1. **Quick lookup**: Start with `CARD_TYPES_QUICK_REFERENCE.md`
2. **Understanding architecture**: Read `CARD_TYPES_FLOW_DIAGRAM.txt`
3. **Deep dive**: Consult `CARD_TYPES_ANALYSIS.md` for detailed information
4. **Bug fix**: Find solution in Quick Reference "THE BUG" section
---
## 📝 Summary Statistics
| Metric | Value |
|--------|-------|
| Files Analyzed | 13 |
| Total Lines of Code | ~1,800 |
| Endpoints | 5 |
| Card Type Categories | 3 |
| Core Operations | 4 (CRUD) |
| Bugs Identified | 1 |
| Bug Severity | High (UX-breaking) |
| Documentation Pages | 3 |
| Recommended Solution | @tap.stop modifier |
---
## ✅ Exploration Complete
All files related to the card types management feature have been thoroughly reviewed, analyzed, and documented.
**Key Achievement**: Identified and documented the root cause of the edit popup bug, along with three solution approaches.
**Ready to**:
- Implement bug fix
- Build additional features
- Optimize performance
- Add tests
- Deploy updates
---
**Generated**: 2026-04-05
**Location**: `/Users/richard/Documents/code/pilates/mp-pilates/`