Browse Source

docs: 添加溪福俱乐部(CFC)设计文档和Phase 0实现计划

User 3 months ago
parent
commit
ea213d1527

+ 1468 - 0
docs/superpowers/plans/2026-06-03-phase0-skeleton.md

@@ -0,0 +1,1468 @@
+# Phase 0: 骨架 — 角色模型 + TabBar + 服务商入驻
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Extend User entity with vendor/service provider fields, add VendorController for vendor application flow, change TabBar from 首页/任务/心愿单/我的 to 首页/发现/商城/我的, create placeholder pages, add membership banner to homepage, and add vendor review page to Web admin.
+
+**Architecture:** Phase 0 is the foundation — all subsequent phases depend on the role model extension and TabBar skeleton. Backend: add 4 fields to User + DatabaseInitializer migration + new VendorController/VendorService. Frontend: change pages.json TabBar + 3 new placeholder pages + update profile + update index. Web: add vendor review route + page.
+
+**Tech Stack:** Java 1.8 + Spring Boot 2.7 + MyBatis-Plus + uni-app Vue 2 + Element UI
+
+**Spec reference:** `docs/superpowers/specs/2026-06-02-care-family-club-design.md` (溪艾福 Care Family Club / CFC)
+
+**Key constraints:**
+- All entities use `java.util.Date` and `implements Serializable`
+- All controllers use `@RequestMapping("/api/...")` + `@PostMapping`
+- DI via `@Resource`
+- No indexes, no constraints, no custom SQL (DDL in DatabaseInitializer only)
+- Vue 2 Options API, uni-app tags, rpx units, NO optional chaining `?.`
+
+---
+
+### Task 1: User entity — add vendor/service provider fields
+
+**Files:**
+- Modify: `zxyj-backend/src/main/java/com/zxyj/entity/User.java`
+
+- [ ] **Step 1: Add 4 fields to User.java**
+
+Add after the existing `familyRole` field (before `createdAt`):
+
+```java
+    // 服务商类型: null=普通用户 / planner / activity_provider / product_supplier
+    private String vendorType;
+
+    // 服务商审核状态: null=未申请 / pending / approved / rejected
+    private String vendorStatus;
+
+    // 服务商审核拒绝原因
+    private String vendorRejectReason;
+
+    // 家庭管理员标记
+    private Boolean isFamilyAdmin;
+```
+
+- [ ] **Step 2: Verify compilation**
+
+Run: `cd zxyj-backend && mvn clean compile -q`
+Expected: BUILD SUCCESS
+
+---
+
+### Task 2: DatabaseInitializer — add vendor columns migration
+
+**Files:**
+- Modify: `zxyj-backend/src/main/java/com/zxyj/config/DatabaseInitializer.java`
+
+- [ ] **Step 1: Find the `runMigrations()` method and add ALTER TABLE statements**
+
+Search for `private void runMigrations()` in DatabaseInitializer.java. Add at the end of the migration SQL list:
+
+```java
+// Phase 0: 溪福俱乐部 — 服务商字段
+"ALTER TABLE users ADD COLUMN IF NOT EXISTS vendor_type VARCHAR(32) DEFAULT NULL COMMENT '服务商类型'",
+"ALTER TABLE users ADD COLUMN IF NOT EXISTS vendor_status VARCHAR(16) DEFAULT NULL COMMENT '服务商审核状态'",
+"ALTER TABLE users ADD COLUMN IF NOT EXISTS vendor_reject_reason VARCHAR(500) DEFAULT NULL COMMENT '审核拒绝原因'",
+"ALTER TABLE users ADD COLUMN IF NOT EXISTS is_family_admin TINYINT DEFAULT 0 COMMENT '家庭管理员'",
+```
+
+If `ADD COLUMN IF NOT EXISTS` is not supported by the MySQL version (8.0 should support it), use the try-catch pattern that already exists in the file. Inspect existing migrations to see the pattern used.
+
+- [ ] **Step 2: Add teacher→vendor data migration**
+
+At the end of `runMigrations()`, add data migration for existing teachers:
+
+```java
+// Phase 0: 迁移已有规划师到服务商体系
+jdbcTemplate.update(
+    "UPDATE users SET vendor_type = 'planner', vendor_status = 'approved' WHERE teacher_status = 'approved' AND vendor_type IS NULL"
+);
+```
+
+- [ ] **Step 3: Verify compilation**
+
+Run: `cd zxyj-backend && mvn clean compile -q`
+Expected: BUILD SUCCESS
+
+---
+
+### Task 3: VendorController + VendorService (backend)
+
+**Files:**
+- Create: `zxyj-backend/src/main/java/com/zxyj/controller/vendor/VendorController.java`
+- Create: `zxyj-backend/src/main/java/com/zxyj/service/VendorService.java`
+- Modify: `zxyj-backend/src/main/java/com/zxyj/mapper/UserMapper.java` (only if methods are missing, likely already has basic CRUD)
+
+- [ ] **Step 1: Create VendorService**
+
+```java
+package com.zxyj.service;
+
+import com.zxyj.common.Result;
+import com.zxyj.entity.User;
+import com.zxyj.mapper.UserMapper;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+@Service
+public class VendorService {
+
+    @Resource
+    private UserMapper userMapper;
+
+    /**
+     * 提交服务商入驻申请
+     */
+    public Result apply(Long userId, String vendorType, String vendorInfo) {
+        User user = userMapper.selectById(userId);
+        if (user == null) {
+            return Result.error("用户不存在");
+        }
+        if (user.getVendorStatus() != null && !"rejected".equals(user.getVendorStatus())) {
+            return Result.error("已有入驻申请,请勿重复提交");
+        }
+        user.setVendorType(vendorType);
+        user.setVendorStatus("pending");
+        user.setVendorRejectReason(null);
+        user.setUpdatedAt(new Date());
+        userMapper.updateById(user);
+        return Result.success("入驻申请已提交,请等待审核");
+    }
+
+    /**
+     * 查询入驻审核状态
+     */
+    public Result getStatus(Long userId) {
+        User user = userMapper.selectById(userId);
+        if (user == null) {
+            return Result.error("用户不存在");
+        }
+        java.util.Map<String, Object> data = new java.util.HashMap<>();
+        data.put("vendorType", user.getVendorType());
+        data.put("vendorStatus", user.getVendorStatus());
+        data.put("vendorRejectReason", user.getVendorRejectReason());
+        return Result.success(data);
+    }
+
+    /**
+     * 获取/编辑服务商信息(仅供已通过服务商使用)
+     */
+    public Result getVendorInfo(Long userId) {
+        User user = userMapper.selectById(userId);
+        if (user == null) {
+            return Result.error("用户不存在");
+        }
+        if (!"approved".equals(user.getVendorStatus())) {
+            return Result.error("服务商资格未通过审核");
+        }
+        java.util.Map<String, Object> data = new java.util.HashMap<>();
+        data.put("vendorType", user.getVendorType());
+        data.put("vendorInfo", user.getVendorInfo());
+        data.put("realName", user.getRealName());
+        data.put("phone", user.getPhone());
+        return Result.success(data);
+    }
+}
+```
+
+- [ ] **Step 2: Create VendorController**
+
+```java
+package com.zxyj.controller.vendor;
+
+import com.zxyj.common.Result;
+import com.zxyj.service.VendorService;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/vendor")
+public class VendorController {
+
+    @Resource
+    private VendorService vendorService;
+
+    /**
+     * 提交服务商入驻申请
+     * POST /api/vendor/apply
+     * Body: { "vendorType": "planner", "vendorInfo": "..." }
+     */
+    @PostMapping("/apply")
+    public Result apply(@RequestBody Map<String, Object> params) {
+        Long userId = getCurrentUserId();
+        String vendorType = (String) params.get("vendorType");
+        String vendorInfo = (String) params.getOrDefault("vendorInfo", "");
+        return vendorService.apply(userId, vendorType, vendorInfo);
+    }
+
+    /**
+     * 查询入驻审核状态
+     * POST /api/vendor/status
+     */
+    @PostMapping("/status")
+    public Result getStatus() {
+        Long userId = getCurrentUserId();
+        return vendorService.getStatus(userId);
+    }
+
+    /**
+     * 获取服务商信息
+     * POST /api/vendor/info
+     */
+    @PostMapping("/info")
+    public Result getVendorInfo() {
+        Long userId = getCurrentUserId();
+        return vendorService.getVendorInfo(userId);
+    }
+
+    /**
+     * 从请求头获取当前用户ID
+     * 与现有AuthController中模式一致
+     */
+    private Long getCurrentUserId() {
+        String userIdStr = org.springframework.web.context.request.RequestContextHolder
+            .currentRequestAttributes()
+            .getRequest()
+            .getHeader("X-User-Id");
+        return userIdStr != null ? Long.parseLong(userIdStr) : null;
+    }
+}
+```
+
+- [ ] **Step 3: Verify compilation**
+
+Run: `cd zxyj-backend && mvn clean compile -q`
+Check: The new directory `controller/vendor/` is picked up by Spring component scan. Verify no bean name conflicts.
+
+---
+
+### Task 4: Admin vendor API — Web端审核服务商入驻
+
+**Files:**
+- Modify: `zxyj-backend/src/main/java/com/zxyj/controller/admin/AdminController.java`
+
+- [ ] **Step 1: Add admin vendor review endpoints to AdminController**
+
+Find the AdminController file. Add the following methods:
+
+```java
+/**
+ * 服务商入驻申请列表
+ * POST /api/admin/vendor/list
+ */
+@PostMapping("/vendor/list")
+public Result vendorList(@RequestBody Map<String, Object> params) {
+    // 查询所有vendor_status非空的用户
+    Integer page = (Integer) params.getOrDefault("page", 1);
+    Integer size = (Integer) params.getOrDefault("size", 20);
+    String status = (String) params.get("status"); // pending / approved / rejected / null=全部
+    // 使用MyBatis-Plus QueryWrapper
+    com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<User> wrapper = new com.baomidou.mybatisplus.core.conditions.query.QueryWrapper<>();
+    wrapper.isNotNull("vendor_type");
+    if (status != null && !status.isEmpty()) {
+        wrapper.eq("vendor_status", status);
+    }
+    wrapper.orderByDesc("updated_at");
+    com.baomidou.mybatisplus.extension.plugins.pagination.Page<User> pageObj = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>(page, size);
+    com.baomidou.mybatisplus.core.metadata.IPage<User> result = userMapper.selectPage(pageObj, wrapper);
+    java.util.Map<String, Object> data = new java.util.HashMap<>();
+    data.put("list", result.getRecords());
+    data.put("total", result.getTotal());
+    data.put("page", page);
+    data.put("size", size);
+    return Result.success(data);
+}
+
+/**
+ * 审核服务商入驻
+ * POST /api/admin/vendor/review
+ * Body: { "userId": 1, "action": "approve" / "reject", "reason": "拒绝原因" }
+ */
+@PostMapping("/vendor/review")
+public Result vendorReview(@RequestBody Map<String, Object> params) {
+    Long userId = Long.valueOf(params.get("userId").toString());
+    String action = (String) params.get("action");
+    String reason = (String) params.getOrDefault("reason", "");
+    User user = userMapper.selectById(userId);
+    if (user == null) {
+        return Result.error("用户不存在");
+    }
+    if ("approve".equals(action)) {
+        user.setVendorStatus("approved");
+        user.setVendorRejectReason(null);
+    } else if ("reject".equals(action)) {
+        user.setVendorStatus("rejected");
+        user.setVendorRejectReason(reason);
+    } else {
+        return Result.error("无效的审核操作");
+    }
+    user.setUpdatedAt(new Date());
+    userMapper.updateById(user);
+    return Result.success("审核完成");
+}
+```
+
+Add the required imports at the top:
+```java
+import com.zxyj.entity.User;
+import com.zxyj.mapper.UserMapper;
+import java.util.Date;
+```
+
+And confirm `@Resource private UserMapper userMapper;` is present in the class.
+
+- [ ] **Step 2: Verify compilation**
+
+Run: `cd zxyj-backend && mvn clean compile -q`
+Expected: BUILD SUCCESS
+
+---
+
+### Task 5: Frontend API additions — vendor module
+
+**Files:**
+- Modify: `zxyj-frontend/utils/api.js`
+
+- [ ] **Step 1: Add vendor API calls to api.js**
+
+Find the end of file. Add:
+
+```javascript
+// ===================== 溪福俱乐部 - 服务商模块 =====================
+
+// 提交服务商入驻申请
+export const vendorApply = (data) => {
+  return request('/api/vendor/apply', 'POST', data)
+}
+
+// 查询入驻审核状态
+export const vendorStatus = () => {
+  return request('/api/vendor/status', 'POST', {})
+}
+
+// 获取服务商信息
+export const vendorInfo = () => {
+  return request('/api/vendor/info', 'POST', {})
+}
+```
+
+---
+
+### Task 6: TabBar change — pages.json
+
+**Files:**
+- Modify: `zxyj-frontend/pages.json`
+
+- [ ] **Step 1: Change TabBar list and add new page registrations**
+
+Replace the `tabBar.list` with:
+
+```json
+"tabBar": {
+    "color": "#7A7E83",
+    "selectedColor": "#FF6B6B",
+    "borderStyle": "black",
+    "backgroundColor": "#FFFFFF",
+    "list": [
+      {
+        "pagePath": "pages/index/index",
+        "text": "首页"
+      },
+      {
+        "pagePath": "pages/discover/index",
+        "text": "发现"
+      },
+      {
+        "pagePath": "pages/shop/index",
+        "text": "商城"
+      },
+      {
+        "pagePath": "pages/profile/profile",
+        "text": "我的"
+      }
+    ]
+  }
+```
+
+Add new page registrations in the `pages` array (before the `globalStyle` section). Place after the last existing entry:
+
+```json
+    // ===== 溪福俱乐部 Phase 0 =====
+    {
+      "path": "pages/discover/index",
+      "style": {
+        "navigationBarTitleText": "发现"
+      }
+    },
+    {
+      "path": "pages/shop/index",
+      "style": {
+        "navigationBarTitleText": "商城"
+      }
+    },
+    {
+      "path": "pages/vendor/apply",
+      "style": {
+        "navigationBarTitleText": "服务商入驻"
+      }
+    },
+    {
+      "path": "pages/vendor/center",
+      "style": {
+        "navigationBarTitleText": "服务商中心"
+      }
+    }
+```
+
+---
+
+### Task 7: Discover placeholder page
+
+**Files:**
+- Create: `zxyj-frontend/pages/discover/index.vue`
+
+- [ ] **Step 1: Create discover placeholder page**
+
+```vue
+<template>
+  <view class="container">
+    <view class="placeholder">
+      <text class="icon">🔍</text>
+      <text class="title">发现</text>
+      <text class="desc">活动与课程即将上线,敬请期待</text>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {}
+  },
+  onLoad() {}
+}
+</script>
+
+<style>
+.container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 100vh;
+  background: #f8f8f8;
+}
+.placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 100rpx 40rpx;
+}
+.icon {
+  font-size: 120rpx;
+  margin-bottom: 30rpx;
+}
+.title {
+  font-size: 40rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 20rpx;
+}
+.desc {
+  font-size: 28rpx;
+  color: #999;
+  text-align: center;
+}
+</style>
+```
+
+---
+
+### Task 8: Shop placeholder page
+
+**Files:**
+- Create: `zxyj-frontend/pages/shop/index.vue`
+
+- [ ] **Step 1: Create shop placeholder page**
+
+```vue
+<template>
+  <view class="container">
+    <view class="placeholder">
+      <text class="icon">🛒</text>
+      <text class="title">商城</text>
+      <text class="desc">商品与服务即将上线,敬请期待</text>
+    </view>
+  </view>
+</template>
+
+<script>
+export default {
+  data() {
+    return {}
+  },
+  onLoad() {}
+}
+</script>
+
+<style>
+.container {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 100vh;
+  background: #f8f8f8;
+}
+.placeholder {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 100rpx 40rpx;
+}
+.icon {
+  font-size: 120rpx;
+  margin-bottom: 30rpx;
+}
+.title {
+  font-size: 40rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 20rpx;
+}
+.desc {
+  font-size: 28rpx;
+  color: #999;
+  text-align: center;
+}
+</style>
+```
+
+---
+
+### Task 9: Vendor apply page (frontend)
+
+**Files:**
+- Create: `zxyj-frontend/pages/vendor/apply.vue`
+
+- [ ] **Step 1: Create vendor application form page**
+
+```vue
+<template>
+  <view class="container">
+    <view class="header">
+      <text class="title">服务商入驻</text>
+      <text class="subtitle">选择服务商类型,提交入驻申请</text>
+    </view>
+
+    <view class="form">
+      <view class="form-item">
+        <text class="label">服务商类型</text>
+        <picker mode="selector" :range="vendorTypes" range-key="label" @change="onTypeChange">
+          <view class="picker">
+            <text v-if="formData.vendorType">{{ getTypeLabel(formData.vendorType) }}</text>
+            <text v-else class="placeholder">请选择服务商类型</text>
+            <text class="arrow">›</text>
+          </view>
+        </picker>
+      </view>
+
+      <view class="form-item">
+        <text class="label">资质说明</text>
+        <textarea
+          class="textarea"
+          v-model="formData.vendorInfo"
+          placeholder="请填写您的资质信息、服务经验等"
+          maxlength="500"
+        />
+        <text class="counter">{{ formData.vendorInfo.length }}/500</text>
+      </view>
+
+      <button class="btn-submit" :disabled="!formData.vendorType || submitting" @click="handleSubmit">
+        {{ submitting ? '提交中...' : '提交申请' }}
+      </button>
+    </view>
+  </view>
+</template>
+
+<script>
+import { vendorApply } from '../../utils/api'
+
+export default {
+  data() {
+    return {
+      vendorTypes: [
+        { value: 'planner', label: '成长规划师' },
+        { value: 'activity_provider', label: '活动提供商' },
+        { value: 'product_supplier', label: '商品供应商' }
+      ],
+      formData: {
+        vendorType: '',
+        vendorInfo: ''
+      },
+      submitting: false
+    }
+  },
+  methods: {
+    onTypeChange(e) {
+      this.formData.vendorType = this.vendorTypes[e.detail.value].value
+    },
+    getTypeLabel(value) {
+      const item = this.vendorTypes.find(t => t.value === value)
+      return item ? item.label : value
+    },
+    async handleSubmit() {
+      this.submitting = true
+      try {
+        const res = await vendorApply({
+          vendorType: this.formData.vendorType,
+          vendorInfo: this.formData.vendorInfo
+        })
+        uni.showToast({ title: '提交成功', icon: 'success' })
+        setTimeout(() => {
+          uni.navigateBack()
+        }, 1500)
+      } catch (e) {
+        // api.js already shows error toast
+      } finally {
+        this.submitting = false
+      }
+    }
+  }
+}
+</script>
+
+<style>
+.container {
+  padding: 30rpx;
+  background: #f8f8f8;
+  min-height: 100vh;
+}
+.header {
+  text-align: center;
+  padding: 40rpx 0;
+}
+.title {
+  font-size: 40rpx;
+  font-weight: bold;
+  color: #333;
+}
+.subtitle {
+  font-size: 26rpx;
+  color: #999;
+  margin-top: 10rpx;
+  display: block;
+}
+.form {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 30rpx;
+}
+.form-item {
+  margin-bottom: 30rpx;
+}
+.label {
+  font-size: 28rpx;
+  color: #333;
+  font-weight: bold;
+  display: block;
+  margin-bottom: 15rpx;
+}
+.picker {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20rpx 0;
+  border-bottom: 2rpx solid #eee;
+}
+.placeholder {
+  color: #ccc;
+}
+.arrow {
+  color: #ccc;
+  font-size: 36rpx;
+}
+.textarea {
+  width: 100%;
+  height: 200rpx;
+  padding: 20rpx;
+  border: 2rpx solid #eee;
+  border-radius: 10rpx;
+  font-size: 28rpx;
+  box-sizing: border-box;
+}
+.counter {
+  display: block;
+  text-align: right;
+  font-size: 24rpx;
+  color: #ccc;
+  margin-top: 10rpx;
+}
+.btn-submit {
+  width: 100%;
+  height: 88rpx;
+  line-height: 88rpx;
+  background: linear-gradient(135deg, #FF6B6B, #FF8E53);
+  color: #fff;
+  border-radius: 44rpx;
+  font-size: 32rpx;
+  margin-top: 40rpx;
+}
+.btn-submit[disabled] {
+  opacity: 0.5;
+}
+</style>
+```
+
+---
+
+### Task 10: Vendor center page (frontend)
+
+**Files:**
+- Create: `zxyj-frontend/pages/vendor/center.vue`
+
+- [ ] **Step 1: Create vendor center page**
+
+```vue
+<template>
+  <view class="container">
+    <view class="header">
+      <text class="icon">🏪</text>
+      <text class="title">服务商中心</text>
+    </view>
+
+    <view class="info-card" v-if="vendorInfoData">
+      <view class="info-row">
+        <text class="info-label">服务商类型</text>
+        <text class="info-value">{{ getTypeLabel(vendorInfoData.vendorType) }}</text>
+      </view>
+      <view class="info-row">
+        <text class="info-label">真实姓名</text>
+        <text class="info-value">{{ vendorInfoData.realName || '未设置' }}</text>
+      </view>
+      <view class="info-row">
+        <text class="info-label">联系电话</text>
+        <text class="info-value">{{ vendorInfoData.phone || '未设置' }}</text>
+      </view>
+    </view>
+
+    <view class="menu-list">
+      <view class="menu-item" @click="goToProductManage">
+        <text>📦 商品管理</text>
+        <text class="arrow">›</text>
+      </view>
+      <view class="menu-item" @click="goToOrderManage">
+        <text>📋 订单管理</text>
+        <text class="arrow">›</text>
+      </view>
+    </view>
+
+    <view class="coming-soon">
+      <text>更多功能即将上线...</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import { vendorInfo } from '../../utils/api'
+
+export default {
+  data() {
+    return {
+      vendorTypes: [
+        { value: 'planner', label: '成长规划师' },
+        { value: 'activity_provider', label: '活动提供商' },
+        { value: 'product_supplier', label: '商品供应商' }
+      ],
+      vendorInfoData: null
+    }
+  },
+  onLoad() {
+    this.loadVendorInfo()
+  },
+  methods: {
+    getTypeLabel(value) {
+      const item = this.vendorTypes.find(t => t.value === value)
+      return item ? item.label : value
+    },
+    async loadVendorInfo() {
+      try {
+        const res = await vendorInfo()
+        this.vendorInfoData = res.data
+      } catch (e) {
+        // handled by api.js
+      }
+    },
+    goToProductManage() {
+      uni.showToast({ title: '即将上线', icon: 'none' })
+    },
+    goToOrderManage() {
+      uni.showToast({ title: '即将上线', icon: 'none' })
+    }
+  }
+}
+</script>
+
+<style>
+.container {
+  padding: 30rpx;
+  background: #f8f8f8;
+  min-height: 100vh;
+}
+.header {
+  text-align: center;
+  padding: 40rpx 0;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+.icon {
+  font-size: 100rpx;
+  margin-bottom: 20rpx;
+}
+.title {
+  font-size: 36rpx;
+  font-weight: bold;
+  color: #333;
+}
+.info-card {
+  background: #fff;
+  border-radius: 20rpx;
+  padding: 30rpx;
+  margin-bottom: 30rpx;
+}
+.info-row {
+  display: flex;
+  justify-content: space-between;
+  padding: 15rpx 0;
+  border-bottom: 2rpx solid #f5f5f5;
+}
+.info-row:last-child {
+  border-bottom: none;
+}
+.info-label {
+  font-size: 28rpx;
+  color: #999;
+}
+.info-value {
+  font-size: 28rpx;
+  color: #333;
+}
+.menu-list {
+  background: #fff;
+  border-radius: 20rpx;
+}
+.menu-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 30rpx;
+  border-bottom: 2rpx solid #f5f5f5;
+  font-size: 30rpx;
+}
+.menu-item:last-child {
+  border-bottom: none;
+}
+.arrow {
+  color: #ccc;
+  font-size: 36rpx;
+}
+.coming-soon {
+  text-align: center;
+  padding: 60rpx;
+  color: #ccc;
+  font-size: 26rpx;
+}
+</style>
+```
+
+---
+
+### Task 11: Profile page — add membership + vendor entries
+
+**Files:**
+- Modify: `zxyj-frontend/pages/profile/profile.vue`
+
+- [ ] **Step 1: Add membership entry and vendor entry after "积分记录" menu item**
+
+Find the `📊 积分记录` menu-item block. Add after it:
+
+```vue
+      <!-- 会员中心(溪艾福) -->
+      <view class="menu-item" @click="goToMembership">
+        <text>👑 会员中心</text>
+        <text class="arrow">›</text>
+      </view>
+      <!-- 服务商中心(仅已通过服务商可见) -->
+      <view class="menu-item" v-if="isVendor" @click="goToVendorCenter">
+        <text>🏪 服务商中心</text>
+        <text class="arrow">›</text>
+      </view>
+```
+
+- [ ] **Step 2: Add `isVendor` data property and handler methods**
+
+In the `data()` function, add:
+```javascript
+isVendor: false,
+```
+
+In the `methods` section, add:
+```javascript
+    goToMembership() {
+      uni.showToast({ title: '即将上线', icon: 'none' })
+    },
+    goToVendorCenter() {
+      uni.navigateTo({ url: '/pages/vendor/center' })
+    }
+```
+
+- [ ] **Step 3: Load vendor status on page show**
+
+In the `onShow()` lifecycle hook, add vendor status check:
+```javascript
+    // 检查服务商状态
+    this.checkVendorStatus()
+```
+
+Add the method:
+```javascript
+    async checkVendorStatus() {
+      try {
+        const { vendorStatus } = require('../../utils/api')
+        const res = await vendorStatus()
+        this.isVendor = res.data && res.data.vendorStatus === 'approved'
+      } catch (e) {
+        this.isVendor = false
+      }
+    },
+```
+
+Also add the import at top of `<script>` section:
+```javascript
+import { vendorStatus } from '../../utils/api'
+```
+
+---
+
+### Task 12: Homepage 五行能量沙盘(五角星)+ membership banner
+
+**Files:**
+- Modify: `zxyj-frontend/pages/index/parent-index.vue`
+- Modify: `zxyj-frontend/pages/index/child-index.vue`
+
+- [ ] **Step 1: Add 五行能量沙盘 (pentagram) component after welcome section**
+
+Read the file first to find the exact insertion point. Add the sandbox component at the top of the content area:
+
+```vue
+    <!-- 五行能量沙盘(溪艾福) -->
+    <view class="wuxing-sandbox">
+      <view class="sandbox-header">
+        <text class="sandbox-title">五行能量沙盘</text>
+        <text class="sandbox-badge">综合成长力 0%</text>
+      </view>
+      <view class="sandbox-star">
+        <!-- 五角星SVG/CSS布局 -->
+        <view class="star-point star-point-top" @click="goToDomain('mind')">
+          <text class="point-icon">🔥</text>
+          <text class="point-label">心·火</text>
+          <text class="point-status inactive">待激活</text>
+        </view>
+        <view class="star-point star-point-left" @click="goToDomain('action')">
+          <text class="point-icon">🌿</text>
+          <text class="point-label">行·木</text>
+          <text class="point-status inactive">待激活</text>
+        </view>
+        <view class="star-point star-point-right" @click="goToDomain('wealth')">
+          <text class="point-icon">💧</text>
+          <text class="point-label">富·水</text>
+          <text class="point-status inactive">待激活</text>
+        </view>
+        <view class="star-point star-point-bl" @click="goToDomain('wisdom')">
+          <text class="point-icon">⚔️</text>
+          <text class="point-label">智·金</text>
+          <text class="point-status inactive">待激活</text>
+        </view>
+        <view class="star-point star-point-br" @click="goToDomain('body')">
+          <text class="point-icon">🌏</text>
+          <text class="point-label">身·土</text>
+          <text class="point-status inactive">待激活</text>
+        </view>
+        <!-- 中心徽章 -->
+        <view class="star-center">
+          <text class="center-text">五行平衡</text>
+          <text class="center-sub">共同成长</text>
+        </view>
+      </view>
+      <!-- 相生指引 -->
+      <view class="shenke-hint">
+        <text class="hint-text">🌱 完成菌群检测,点亮「身·土」</text>
+      </view>
+      <!-- 维度状态条 -->
+      <view class="dimension-bar">
+        <view class="dim-item inactive">🌿行</view>
+        <text class="dim-arrow">→</text>
+        <view class="dim-item inactive">🔥心</view>
+        <text class="dim-arrow">→</text>
+        <view class="dim-item inactive">🌏身</view>
+        <text class="dim-arrow">→</text>
+        <view class="dim-item inactive">⚔️智</view>
+        <text class="dim-arrow">→</text>
+        <view class="dim-item inactive">💧富</view>
+      </view>
+    </view>
+
+    <!-- 会员Banner(溪艾福) -->
+    <view class="membership-banner" @click="goToMembership">
+      <view class="banner-content">
+        <text class="banner-icon">👑</text>
+        <view class="banner-text">
+          <text class="banner-title">{{ membershipText }}</text>
+          <text class="banner-subtitle">点击查看会员权益</text>
+        </view>
+        <text class="banner-arrow">›</text>
+      </view>
+    </view>
+```
+
+Note: The pentagram layout uses `position: absolute/fixed` inside a relative container. The five `star-point-*` classes position each point according to a regular pentagram. Use `display: flex; justify-content: center;` for horizontal centering of the star-center.
+
+- [ ] **Step 2: Add data and methods**
+
+In `data()`:
+```javascript
+membershipText: '开通家庭会员 畅享全部权益',
+```
+
+In `methods()`:
+```javascript
+goToDomain(domain) {
+  uni.showToast({ title: '即将上线', icon: 'none' })
+},
+goToMembership() {
+  uni.showToast({ title: '即将上线', icon: 'none' })
+},
+```
+
+- [ ] **Step 3: Add styles for 五行沙盘**
+
+```css
+/* ===== 五行能量沙盘 ===== */
+.wuxing-sandbox {
+  margin: 20rpx 30rpx;
+  padding: 30rpx;
+  background: linear-gradient(135deg, #1a1a2e, #16213e);
+  border-radius: 24rpx;
+  position: relative;
+  overflow: hidden;
+}
+.sandbox-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 30rpx;
+}
+.sandbox-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #FFD700;
+}
+.sandbox-badge {
+  font-size: 22rpx;
+  color: #aaa;
+  background: rgba(255,255,255,0.1);
+  padding: 6rpx 16rpx;
+  border-radius: 20rpx;
+}
+.sandbox-star {
+  position: relative;
+  height: 400rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+.star-point {
+  position: absolute;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  width: 120rpx;
+}
+.star-point-top {
+  top: 0;
+  left: 50%;
+  transform: translateX(-50%);
+}
+.star-point-left {
+  top: 120rpx;
+  left: 20rpx;
+}
+.star-point-right {
+  top: 120rpx;
+  right: 20rpx;
+}
+.star-point-bl {
+  bottom: 40rpx;
+  left: 60rpx;
+}
+.star-point-br {
+  bottom: 40rpx;
+  right: 60rpx;
+}
+.point-icon {
+  font-size: 50rpx;
+  margin-bottom: 6rpx;
+}
+.point-label {
+  font-size: 22rpx;
+  color: #fff;
+  font-weight: bold;
+}
+.point-status {
+  font-size: 18rpx;
+  margin-top: 4rpx;
+}
+.point-status.inactive {
+  color: #666;
+}
+.point-status.active {
+  color: #4CAF50;
+}
+.star-center {
+  width: 120rpx;
+  height: 120rpx;
+  background: radial-gradient(circle, rgba(255,215,0,0.2), rgba(255,215,0,0.05));
+  border: 2rpx solid rgba(255,215,0,0.3);
+  border-radius: 50%;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+}
+.center-text {
+  font-size: 22rpx;
+  color: #FFD700;
+  font-weight: bold;
+}
+.center-sub {
+  font-size: 18rpx;
+  color: #aaa;
+}
+.shenke-hint {
+  text-align: center;
+  margin: 20rpx 0;
+}
+.hint-text {
+  font-size: 24rpx;
+  color: #FFD700;
+  opacity: 0.8;
+}
+.dimension-bar {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 8rpx;
+}
+.dim-item {
+  padding: 6rpx 16rpx;
+  border-radius: 20rpx;
+  font-size: 22rpx;
+}
+.dim-item.inactive {
+  background: rgba(255,255,255,0.1);
+  color: #666;
+}
+.dim-item.active {
+  background: rgba(76,175,80,0.3);
+  color: #4CAF50;
+}
+.dim-arrow {
+  color: #444;
+  font-size: 20rpx;
+}
+
+/* ===== 会员Banner ===== */
+.membership-banner {
+  margin: 20rpx 30rpx;
+  padding: 30rpx;
+  background: linear-gradient(135deg, #FFD700, #FFA500);
+  border-radius: 20rpx;
+  box-shadow: 0 4rpx 20rpx rgba(255, 165, 0, 0.3);
+}
+.banner-content {
+  display: flex;
+  align-items: center;
+}
+.banner-icon {
+  font-size: 60rpx;
+  margin-right: 20rpx;
+}
+.banner-text {
+  flex: 1;
+}
+.banner-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #8B4513;
+  display: block;
+}
+.banner-subtitle {
+  font-size: 24rpx;
+  color: #A0522D;
+  margin-top: 6rpx;
+  display: block;
+}
+.banner-arrow {
+  font-size: 40rpx;
+  color: #8B4513;
+}
+```
+
+- [ ] **Step 4: Same change in child-index.vue**
+
+Read the file and replicate the same 五行沙盘 + membership banner insertion in the same position. The child version can simplify or remove the membership banner (children don't buy memberships), but keep the 五行沙盘 with child-appropriate guidance text.
+
+---
+
+### Task 13: Web admin — vendor review page
+
+**Files:**
+- Create: `zxyj-web/src/views/admin/VendorReview.vue`
+- Modify: `zxyj-web/src/router/index.js`
+- Create: `zxyj-web/src/api/vendor.js`
+
+- [ ] **Step 1: Create vendor API file**
+
+```javascript
+// zxyj-web/src/api/vendor.js
+import request from '@/utils/request'
+
+// 服务商入驻申请列表
+export function getVendorList(params) {
+  return request({
+    url: '/api/admin/vendor/list',
+    method: 'post',
+    data: params
+  })
+}
+
+// 审核服务商入驻
+export function reviewVendor(data) {
+  return request({
+    url: '/api/admin/vendor/review',
+    method: 'post',
+    data: data
+  })
+}
+```
+
+Check `zxyj-web/src/utils/request.js` to confirm the request wrapper pattern matches.
+
+- [ ] **Step 2: Create VendorReview.vue**
+
+```vue
+<template>
+  <div class="vendor-review">
+    <div class="header">
+      <h2>服务商入驻审核</h2>
+      <el-select v-model="statusFilter" placeholder="审核状态" @change="loadList" style="width: 150px;">
+        <el-option label="全部" value="" />
+        <el-option label="待审核" value="pending" />
+        <el-option label="已通过" value="approved" />
+        <el-option label="已拒绝" value="rejected" />
+      </el-select>
+    </div>
+
+    <el-table :data="list" v-loading="loading" border stripe>
+      <el-table-column prop="id" label="用户ID" width="80" />
+      <el-table-column prop="nickname" label="昵称" width="120" />
+      <el-table-column prop="realName" label="真实姓名" width="120" />
+      <el-table-column prop="phone" label="手机号" width="140" />
+      <el-table-column label="服务商类型" width="140">
+        <template slot-scope="{ row }">
+          {{ vendorTypeLabel(row.vendorType) }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="vendorInfo" label="资质说明" min-width="200" show-overflow-tooltip />
+      <el-table-column label="审核状态" width="100">
+        <template slot-scope="{ row }">
+          <el-tag :type="statusType(row.vendorStatus)">{{ statusLabel(row.vendorStatus) }}</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="vendorRejectReason" label="拒绝原因" width="150" show-overflow-tooltip />
+      <el-table-column label="操作" width="200" fixed="right">
+        <template slot-scope="{ row }">
+          <el-button v-if="row.vendorStatus === 'pending'" type="success" size="small" @click="handleReview(row, 'approve')">通过</el-button>
+          <el-button v-if="row.vendorStatus === 'pending'" type="danger" size="small" @click="showRejectDialog(row)">拒绝</el-button>
+          <span v-else class="no-op">—</span>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-pagination
+      @size-change="onPageChange"
+      @current-change="onPageChange"
+      :current-page="page"
+      :page-size="size"
+      :total="total"
+      layout="total, prev, pager, next"
+      style="margin-top: 20px; text-align: right;"
+    />
+
+    <!-- 拒绝弹窗 -->
+    <el-dialog title="填写拒绝原因" :visible.sync="rejectDialogVisible" width="400px">
+      <el-input v-model="rejectReason" type="textarea" :rows="3" placeholder="请输入拒绝原因" />
+      <span slot="footer">
+        <el-button @click="rejectDialogVisible = false">取消</el-button>
+        <el-button type="danger" @click="confirmReject">确认拒绝</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { getVendorList, reviewVendor } from '@/api/vendor'
+
+export default {
+  data() {
+    return {
+      list: [],
+      loading: false,
+      page: 1,
+      size: 20,
+      total: 0,
+      statusFilter: 'pending',
+      rejectDialogVisible: false,
+      rejectTarget: null,
+      rejectReason: ''
+    }
+  },
+  mounted() {
+    this.loadList()
+  },
+  methods: {
+    vendorTypeLabel(type) {
+      const map = { planner: '成长规划师', activity_provider: '活动提供商', product_supplier: '商品供应商' }
+      return map[type] || type
+    },
+    statusType(status) {
+      const map = { pending: 'warning', approved: 'success', rejected: 'danger' }
+      return map[status] || 'info'
+    },
+    statusLabel(status) {
+      const map = { pending: '待审核', approved: '已通过', rejected: '已拒绝' }
+      return map[status] || status
+    },
+    async loadList() {
+      this.loading = true
+      try {
+        const res = await getVendorList({ page: this.page, size: this.size, status: this.statusFilter })
+        this.list = res.data.list || []
+        this.total = res.data.total || 0
+      } catch (e) {
+        this.$message.error('加载失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    onPageChange(val) {
+      this.page = val
+      this.loadList()
+    },
+    async handleReview(row, action) {
+      try {
+        await reviewVendor({ userId: row.id, action })
+        this.$message.success('审核完成')
+        this.loadList()
+      } catch (e) {
+        this.$message.error('操作失败')
+      }
+    },
+    showRejectDialog(row) {
+      this.rejectTarget = row
+      this.rejectReason = ''
+      this.rejectDialogVisible = true
+    },
+    async confirmReject() {
+      if (!this.rejectReason) {
+        this.$message.warning('请填写拒绝原因')
+        return
+      }
+      await this.handleReview(this.rejectTarget, 'reject')
+      this.rejectDialogVisible = false
+    }
+  }
+}
+</script>
+
+<style scoped>
+.header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 20px;
+}
+.no-op {
+  color: #ccc;
+}
+</style>
+```
+
+- [ ] **Step 3: Add route in router/index.js**
+
+Add the import and route at the end of the `children` array (before the closing `]`):
+
+```javascript
+      {
+        path: 'vendor-review',
+        name: 'VendorReview',
+        component: () => import('@/views/admin/VendorReview.vue'),
+        meta: { title: '服务商审核' }
+      },
+```
+
+Add `'VendorReview'` to the `adminRoutes` array in the route guard section:
+```javascript
+    'ServiceTypes', 'ServiceContents', 'PackageTemplates',
+    'AssessmentAdmin', 'VendorReview'
+```
+
+---
+
+### Task 14: Web admin — add sidebar menu for vendor review
+
+**Files:**
+- Modify: `zxyj-web/src/views/Layout.vue`
+
+- [ ] **Step 1: Add sidebar menu item**
+
+Read `Layout.vue` first. Find the admin menu section (likely near other admin menu items like 服务类型管理). Add:
+
+```html
+<el-menu-item index="/vendor-review">
+  <i class="el-icon-s-check"></i>
+  <span slot="title">服务商审核</span>
+</el-menu-item>
+```
+
+Verify the existing pattern for menu items by reading the file first.
+
+---
+
+## Self-review
+
+Run this after completing all tasks:
+
+1. **Spec coverage:** Does Phase 0 deliver role model expansion (vendorType/vendorStatus/isFamilyAdmin), TabBar change, vendor apply/status/info endpoints, discover/shop placeholders, profile updates, membership banner, Web admin review? ✅ Yes — Tasks 1-14 cover all.
+
+2. **Placeholder scan:** No "TBD", "TODO", "implement later" in code blocks. All code is complete.
+
+3. **Type consistency:** `vendorType` (String), `vendorStatus` (String), `isFamilyAdmin` (Boolean) — consistent across all tasks. `VendorService.apply` returns `Result`. `VendorController` endpoints use `@PostMapping`. All consistent.
+
+4. **Dependencies:** Tasks 1-2 must precede Task 3 (User entity + DB migration before VendorService). Task 5 (pages.json) must precede Tasks 7-8 (pages must be declared before they can be navigated to). Tasks 9-10 (vendor pages) depend on Task 4 (API calls). Task 11 (profile) depends on Task 4. Task 14 (sidebar) depends on Task 13.

+ 1000 - 0
docs/superpowers/specs/2026-06-02-care-family-club-design.md

@@ -0,0 +1,1000 @@
+# 溪艾福 (Care Family Club) 平台升级设计规格
+
+> **状态:** Draft  
+> **日期:** 2026-06-03  
+> **替代版本:** 旧版4域4象限设计(身/心/智/行四域架构)已被用户3条新需求完全推翻  
+> **架构策略:** 三层角色体系 + 统一Product模型 + 五行首页 + 按功能组织的页面 + 分阶段交付
+
+---
+
+## 1. 系统定位
+
+**溪艾福** — 从"知心益家"家庭教育任务管理小程序升级为以家庭为单位的、以"五行五维"为哲学内核的主动健康管理平台。
+
+### 品牌写法和命名规范
+
+| 上下文 | 写法 |
+|--------|------|
+| 品牌全称(对外) | 溪艾福 (Care Family Club) |
+| 简称(小程序标题) | 溪艾福 |
+| 后台/文档/内部 | 溪艾福 / CFC |
+| 域名/系统标识 | CFC |
+
+### 核心理念
+
+- **五行五维** — 行(木)·心(火)·身(土)·智(金)·富(水) 五大维度作为平台内容与用户生命周期的核心框架,Product.domain字段承载后台映射
+- **按功能组织** — 活动、课程、商品等按用户可理解的分类呈现,五行作为用户体验的"能量可视化"线索
+- **家庭会员制** — 1314元/年家庭会员费,可选等值商品
+- **开放对接** — 具备与其他平台(如Lilishop)对接的能力
+- **五行相生闭环** — 木→火→土→金→水,形成完整的用户生命周期:社群引流→情绪转化→菌群康养→脑力升级→创富传承
+
+### 五行五维对齐
+
+| 五维 | 五行 | 属性 | 核心映射 | 哲学咬合 |
+|------|------|------|---------|---------|
+| 行 | 木 | 生发·仁爱 | 关系践行、社群连接 | 关系的本质是"仁",木如大树根系交错,打破孤岛 |
+| 心 | 火 | 光明·主宰 | 情绪识别与掌控 | 中医"心主神明属火",情绪管理如明灯照亮潜意识 |
+| 身 | 土 | 长养·化生 | 肠道菌群、微生态 | 脾胃属土为后天之本,菌群平衡是生命健康轴心 |
+| 智 | 金 | 收敛·精密 | 多模态认知测评 | 脑科学的数据严谨性,金之"锐利与收敛"萃取天赋 |
+| 富 | 水 | 流通·承袭 | 创富合伙、资产传承 | "财富如流水",水是润泽后代的管道收益 |
+
+### 五行相生商业闭环
+
+```
+【木生火】关系践行与社群活动(木)→点燃心火、驱散焦虑内耗(火)
+【火生土】心理情绪理顺→肠脑轴正向作用于肠道微生态(土)
+【土生金】菌群中土调好→大脑高质量营养→敏锐高维认知(金)
+【金生水】顶级认知看清趋势→运筹帷幄、财富如流水(水)
+【水生木】财富自由后反哺圈层→赋能更多家庭关系升级(木)
+```
+
+### 用户角色体系(三层)
+
+```
+┌─────────────────────────────────────────────────────────────────┐
+│                      溪艾福 用户体系                               │
+├──────────────┬────────────────┬─────────────────┬───────────────┤
+│   普通用户    │   家庭管理员     │   服务商          │   管理员      │
+│  (User)      │  (familyAdmin)  │  (ServiceProvider)│  (Admin)     │
+├──────────────┼────────────────┼─────────────────┼───────────────┤
+│ 家庭成员之一  │ 家庭创建者/管理者  │ 入驻审核通过后    │ Web端管理     │
+│ 参与活动/购物 │ 管理家庭成员     │ 可发布商品/服务   │ 全平台管控    │
+│ 使用会员权益  │ 审核成员加入     │ vendorType区分    │ 配置管理      │
+└──────────────┴────────────────┴─────────────────┴───────────────┘
+```
+
+**关键设计:不拆分User表** — User实体扩展字段实现三层角色
+
+| 角色层级 | 实现方式 | 关键字段 |
+|---------|---------|---------|
+| 普通用户 | User.isFamilyAdmin=false, vendorType=null | role, familyId |
+| 家庭管理员 | User.isFamilyAdmin=true | isFamilyAdmin=true |
+| 服务商 | User.vendorType=非空 | vendorType, vendorStatus |
+| 管理员 | 仅Web端可登录 | role=admin |
+
+### 用户角色关系
+
+```
+家庭(Family)
+│
+├── 家庭管理员 (isFamilyAdmin=true)
+│   ├── 管理家庭成员的加入/退出
+│   ├── 管理家庭会员续费
+│   └── 审核服务商的入驻申请(待定)
+│
+├── 家庭成员 (isFamilyAdmin=false)
+│   ├── 孩子/其他家属
+│   └── 共享家庭会员权益
+│
+└── 服务商 (vendorType!=null) — 可以同时是家庭成员
+    ├── 规划师 (vendorType=planner)
+    ├── 活动提供商 (vendorType=activity_provider)
+    └── 商品供应商 (vendorType=product_supplier)
+```
+
+---
+
+## 2. 页面结构
+
+### TabBar配置(4项,微信限制最多5项)
+
+| 位置 | Tab名 | 页面路径 | 说明 |
+|------|-------|---------|------|
+| Tab 1 | 首页 | `pages/index/index` | 保留,角色分流+快捷入口+会员Banner |
+| Tab 2 | 发现 | `pages/discover/index` | **新增**,聚合活动+课程+内容 |
+| Tab 3 | 商城 | `pages/shop/index` | **新增**,商品列表+购买 |
+| Tab 4 | 我的 | `pages/profile/profile` | 保留,扩展服务商中心+会员中心入口 |
+
+### pages.json tabBar配置变更
+
+```json
+{
+  "tabBar": {
+    "list": [
+      { "pagePath": "pages/index/index", "text": "首页" },
+      { "pagePath": "pages/discover/index", "text": "发现" },
+      { "pagePath": "pages/shop/index", "text": "商城" },
+      { "pagePath": "pages/profile/profile", "text": "我的" }
+    ]
+  }
+}
+```
+
+### 原有Tab迁移
+
+| 原有Tab | 去向 | 访问方式 |
+|---------|------|---------|
+| 任务 (`pages/tasks/tasks`) | 保留页面 | 从首页快捷入口或"发现"进入 |
+| 心愿单 (`pages/rewards/rewards`) | 保留页面 | 从"我的"→积分中心 或 首页入口进入 |
+
+### 五行能量沙盘 — 首页核心视觉
+
+首页顶部核心区域为**五行能量沙盘**,以五角星形态呈现,展示用户个人与家庭在五行五维中的能量值(0-100)与平衡状态。
+
+```
+┌────────────────────────────────────────────┐
+│             五行能量沙盘                     │
+│                                              │
+│              🔥 心·火                        │
+│          个人 65  家庭 58                    │
+│         ┌──────────────────┐                │
+│    🌿 行·木│    ⚪       │💧 富·水          │
+│  个人 42  │  平衡中心  │ 个人 33             │
+│  家庭 38  │  综合成长力 │ 家庭 29             │
+│           │  个人 53.8  │                    │
+│    ⚔️ 智·金│  家庭 48.4  │🌏 身·土           │
+│  个人 51  │                │ 个人 78          │
+│  家庭 46  └──────────────────┘ 家庭 71        │
+│                                              │
+│  相生: 木→火→土→金→水→木   相克: 木→土→水→火→金→木  │
+└────────────────────────────────────────────┘
+```
+
+**核心机制:**
+
+| 机制 | 说明 |
+|------|------|
+| **五角星布局** | 火(顶) — 木(左) — 水(右) — 金(左下) — 土(右下),构成标准五行生克星图 |
+| **双值显示** | 每个角显示两个数值:个人能量值(上方,亮色)+ 家庭聚合值(下方,半透明) |
+| **由内向外填充** | 每个维度的能量值0-100,从中心向外沿角方向填充,值越高填充越长 |
+| **点亮条件** | 完成该维度的免费初级测评后设定baseline(初始值),之后随行为动态变化 |
+| **平衡中心** | 五角星中心展示"综合成长力"= 五维加权均值,五行偏差越小分越高 |
+| **相生指引** | 外圈连线按木→火→土→金→水→木方向,指引用户"下一个该提升什么" |
+| **点击交互** | 点击任意维度角进入该维度的商品/内容聚合页 |
+| **阶段规划** | Phase 0静态引导 → Phase 1免费测评上线+真实数据驱动 |
+| **domain映射** | action=木 / mind=火 / body=土 / wisdom=金 / wealth=水 |
+
+### 家庭成员卡片区
+
+五角星下方为家庭成员卡片列表,每个卡片展示该成员的五维能量简略值,点击进入该成员的个人能量详情页(类似现有家长查看孩子信息的布局风格)。
+
+```
+┌─ 家庭成员 ⚡ 一起为家庭充能 ─────────────┐
+│                                            │
+│ ┌─ 👩 妈妈 ─────────────────────────┐     │
+│ │  🔥65  🌿30  🌏78  ⚔️51  💧33     │ →   │
+│ │  ⚡ 今日贡献: +8 (参加亲子活动+木)    │     │
+│ └────────────────────────────────────┘     │
+│ ┌─ 👨 爸爸 ─────────────────────────┐     │
+│ │  🔥45  🌿28  🌏65  ⚔️42  💧25     │ →   │
+│ │  ⚡ 今日贡献: +5 (完成测评+土)       │     │
+│ └────────────────────────────────────┘     │
+│ ┌─ 👧 女儿 ─────────────────────────┐     │
+│ │  🔥70  🌿54  🌏80  ⚔️60  💧40     │ →   │
+│ │  ⚡ 今日贡献: +3 (完成任务+智)       │     │
+│ └────────────────────────────────────┘     │
+│                      [+ 添加家庭成员]       │
+└────────────────────────────────────────────┘
+```
+
+### 页面导航结构
+
+```
+首页 (Tab 1)
+├── 五行能量沙盘(五角星,每角个人值+家庭值)
+├── 👑 会员Banner(展示会员状态/引导开通)
+├── 家庭成员卡片(五维简略值 + 今日贡献)
+├── 快捷入口区域(保留现有核心功能)
+│   ├── 📋 任务管理(指向任务页)
+│   ├── 📚 成长档案(指向成长档案页)
+│   ├── 🎮 小游戏(指向游戏列表)
+│   ├── 📊 测评系统(指向测评页)
+│   └── 🏆 积分中心(指向积分页)
+├── 推荐活动/课程(需要提升什么维度就推荐什么)
+└── 家庭动态(待定)
+
+发现 (Tab 2)
+├── 活动列表(productType=activity)
+├── 课程列表(productType=course)
+└── 内容推荐(取决于需求)
+
+商城 (Tab 3)
+├── 商品列表(productType=physical/digital/service)
+│   ├── 五行分类筛选(木/火/土/金/水)
+├── 商品详情(含该商品energyValue/可贡献的能量值)
+└── 购买流程
+
+我的 (Tab 4)
+├── 用户信息卡片
+├── 五行能量总览(五角星缩略版 + 综合成长力)
+├── 会员中心入口(会员状态/权益/续费)
+├── 订单列表
+├── 积分中心(积分余额/流水/兑换)
+├── 服务商中心(仅vendorType非空时显示)
+│   ├── 商品管理(发布/编辑/上下架)
+│   └── 订单管理(查看/确认)
+├── 家庭管理(家庭成员/邀请)
+├── 设置(个人信息/地址/关于)
+└── 客服/帮助
+```
+
+---
+
+## 3. 数据库实体设计
+
+### 实体总览
+
+| Entity | 表名 | 类型 | Phase | 说明 |
+|--------|------|------|-------|------|
+| Product | `products` | 新增 | 1 | 统一商品模型 |
+| ProductOrder | `product_orders` | 新增 | 1 | 商品订单 |
+| FamilyMembership | `family_memberships` | 新增 | 2 | 家庭会员 |
+| MemberSelection | `member_selections` | 新增 | 2 | 1314等值商品选择 |
+| SysConfig | `sys_config` | 新增 | 1 | 系统配置(可后台定制) |
+| User(扩展) | `users` | 扩展 | 0 | 新增vendorType/vendorStatus/isFamilyAdmin |
+| PointsLog(扩展) | `points_log` | 扩展 | 2 | 新增type=member_deduct |
+
+**总计新增Entity: 5个**(旧计划17个 → 新设计5个,简化65%)
+
+### 3.1 User扩展字段(Phase 0)
+
+```java
+// 新增字段到现有 users 表
+private String vendorType;           // null=普通用户 / planner / activity_provider / product_supplier
+private String vendorStatus;         // null=未申请 / pending / approved / rejected
+private String vendorRejectReason;   // 审核拒绝原因
+private Boolean isFamilyAdmin;       // 是否家庭管理员 (default false)
+private String vendorInfo;           // 服务商资质信息 (JSON)
+```
+
+### 3.2 Product实体(Phase 1)
+
+对齐Lilishop Goods (`li_goods`),简化版(无SKU),扩展memberPrice和domain字段。
+
+```java
+@TableName("products")
+public class Product implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    // 基本商品信息
+    private String name;              // 商品名称
+    private String description;       // 商品描述
+    private String intro;             // 详情(富文本/HTML)
+    private BigDecimal price;         // 商品价格
+    private String coverImage;        // 封面图
+    private String images;            // 多图(JSON数组)
+    private Integer stock;            // 库存
+    private Integer salesCount;       // 销量
+
+    // 商品类型(核心分类)
+    private String productType;       // activity / course / physical / digital / service
+
+    // 服务商信息
+    private Long vendorId;            // 服务商用户ID
+    private String vendorName;        // 服务商名称(冗余)
+
+    // 商品状态
+    private String status;            // draft / pending / approved / rejected / on_shelf / off_shelf
+    private String rejectReason;      // 审核拒绝原因
+
+    // 会员相关
+    private BigDecimal memberPrice;   // 会员价(标准会员价)
+    private String memberEligible;    // 1314等值预算范围内的商品标记: all / budget / none / JSON范围
+
+    // 五行五维标签(纯后台分析用,前端通过五行沙盘可视化呈现)
+    private String domain;            // body / mind / wisdom / action / wealth
+
+    // 对接扩展
+    private String externalSource;    // 来源平台标识(如 lilishop)
+    private String externalId;        // 在源平台的商品ID
+    private String externalData;      // 源平台原始数据(JSON,扩展用)
+
+    // 时间
+    private Date createdAt;
+    private Date updatedAt;
+}
+```
+
+**Product.productType枚举:**
+
+| 值 | 说明 | 示例 |
+|----|------|------|
+| `activity` | 活动 | 线下亲子活动、线上讲座 |
+| `course` | 课程 | 家庭教育课程、成长课程 |
+| `physical` | 实物商品 | 教具、绘本、健康产品 |
+| `digital` | 数字商品 | 电子报告、在线测评 |
+| `service` | 服务 | 成长规划师服务、肠道菌群测评 |
+
+### 3.3 ProductOrder实体(Phase 1)
+
+```java
+@TableName("product_orders")
+public class ProductOrder implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private String orderNo;           // 订单号
+    private Long productId;           // 商品ID
+    private String productName;       // 商品名称(冗余)
+    private String productType;       // 商品类型(冗余)
+    private Long buyerId;             // 购买者用户ID
+    private Long familyId;            // 家庭ID
+    private Integer quantity;         // 数量
+    private BigDecimal unitPrice;     // 单价
+    private BigDecimal totalAmount;   // 总金额
+    private String status;            // pending / paid / completed / cancelled / refunded
+    private String paymentMethod;     // 支付方式: wechat / points / membership
+    private String remark;            // 备注
+    private Date paidAt;              // 支付时间
+    private Date createdAt;
+    private Date updatedAt;
+}
+```
+
+### 3.4 FamilyMembership实体(Phase 2)
+
+```java
+@TableName("family_memberships")
+public class FamilyMembership implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long familyId;            // 家庭ID
+    private String membershipLevel;   // free / standard / premium
+    private BigDecimal fee;           // 实际支付金额
+    private Date startDate;           // 会员生效日期
+    private Date endDate;             // 会员到期日期
+    private Boolean autoRenew;        // 自动续费
+    private String paymentMethod;     // 支付方式: wechat / points / mixed
+    private Integer pointsDeducted;   // 抵扣积分数量
+    private BigDecimal pointsAmount;  // 积分抵扣金额
+    private String status;            // active / expired / cancelled
+    private Date createdAt;
+    private Date updatedAt;
+}
+```
+
+### 3.5 MemberSelection实体(Phase 2)
+
+1314等值商品选择记录。
+
+```java
+@TableName("member_selections")
+public class MemberSelection implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long familyId;            // 家庭ID
+    private Long membershipId;        // 会员记录ID
+    private Long productId;           // 选择的商品ID
+    private String productName;       // 商品名称(冗余)
+    private BigDecimal productPrice;  // 商品价格
+    private String status;            // selected / redeemed / cancelled
+    private Date selectedAt;          // 选择时间
+    private Date redeemedAt;          // 兑换时间
+    private Date createdAt;
+}
+```
+
+### 3.6 SysConfig实体(Phase 1)
+
+所有可配置参数,管理端CRUD。
+
+```java
+@TableName("sys_config")
+public class SysConfig implements Serializable {
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private String configKey;         // 配置键
+    private String configValue;       // 配置值(JSON或数值字符串)
+    private String description;       // 说明
+    private Date updatedAt;
+}
+```
+
+**种子配置数据:**
+
+| configKey | configValue | 说明 |
+|-----------|-------------|------|
+| `points_exchange_rate` | `0.01` | 1积分=0.01元 |
+| `points_deduct_cap` | `0.5` | 积分抵扣上限(50%) |
+| `member_fee_standard` | `1314` | 标准会员年费(元) |
+| `member_fee_premium` | `3650` | 高级会员年费(元,待定) |
+| `member_eligible_budget` | `1314` | 等值商品选择预算上限(元) |
+| `renewal_discount_rate` | `0.85` | 续费折扣率 |
+| `member_discount_standard` | `0.9` | 标准会员折扣率 |
+| `member_discount_premium` | `0.8` | 高级会员折扣率 |
+
+### 3.7 PointsLog扩展(Phase 2)
+
+```java
+// 现有 PointsLog 新增 type 枚举值
+// type 新增: member_deduct — 抵扣会员费
+```
+
+积分抵扣逻辑:
+- 1积分 = `sys_config(points_exchange_rate)` 元
+- 单次抵扣上限 = 应付款 × `sys_config(points_deduct_cap)`
+- PointsLog.type = `member_deduct`,无新表
+
+### 实体约定
+
+- ✅ 使用 `java.util.Date`
+- ✅ 所有实体 `implements Serializable`
+- ✅ `@TableId(type = IdType.AUTO)`
+- ✅ `@Data` + `@TableName`
+- ✅ 不建索引、不建约束、不建自定义SQL(通过DatabaseInitializer建表)
+
+---
+
+## 4. 服务商入驻流程
+
+复用现有规划师审核流程,扩展为通用服务商审核。
+
+```
+用户申请成为服务商
+│
+├── 用户提交入驻申请
+│   ├── 选择服务商类型 (vendorType)
+│   │   ├── planner          — 成长规划师
+│   │   ├── activity_provider — 活动提供商
+│   │   └── product_supplier — 商品供应商
+│   └── 填写资质信息(不同类型可能要求不同资料)
+│
+├── Admin后台审核(复用现有管理端审核页面)
+│   ├── 查看申请资料
+│   └── 操作: approve / reject(填写原因)
+│
+└── 审核通过后
+    ├── User.vendorStatus = 'approved'
+    ├── 可在商城/发现发布对应类型的商品
+    └── "我的"页面显示"服务商中心"入口
+```
+
+### User.vendorStatus流转
+
+```
+null(未申请)
+  │
+  ├── 用户提交申请 → vendorStatus = 'pending'
+  │
+  ├── Admin审核通过 → vendorStatus = 'approved'
+  │   ├── 可发布商品
+  │   └── 可编辑服务商信息
+  │
+  └── Admin拒绝 → vendorStatus = 'rejected', vendorRejectReason = '原因'
+      └── 用户可修改资料后重新提交
+```
+
+### 差异化字段处理
+
+与现有 `teacherStatus` / `teacherNo` / `teacherPhoto` / `certificateImage` 字段共存:
+- 现有规划师字段(`teacher*`)→ 保持,专用于planner类型
+- 新增服务商字段(`vendor*`)→ 通用,适用于所有类型
+- 数据兼容:已有规划师数据自动设置 `vendorType=planner, vendorStatus=approved`
+- **作用**: `teacherStatus` 控制规划师菜单权限;`vendorStatus` 控制发布商品权限
+
+---
+
+## 5. 会员体系
+
+### 5.1 会员等级
+
+| 等级 | 年费 | 权益 | domain标签可见性 |
+|------|------|------|-----------------|
+| 体验(Free) | 免费 | 浏览商品、参与免费活动、基础任务&积分 | 五行沙盘可见但维度全灰(引导点亮/测评激活) |
+| 标准(Standard) | 1314元 | 全部商品会员价、1314等值商品选择、全部活动报名 | 五行沙盘已点亮维度高亮,可查看个人五行分析报告 |
+| 高级(Premium) | 待定(SysConfig) | 标准权益+额外折扣+优先服务 | 完整功能 |
+
+### 5.2 1314等值商品选择
+
+**业务规则:**
+1. 家庭支付1314元标准会员费后,获得等值商品选择权
+2. 等值范围:Product.memberEligible标记为 `budget` 或 `all` 的商品
+3. 选择上限:`sys_config(member_eligible_budget)` 元(默认1314)
+4. 可选商品包括:成长档案服务、教育支持计划、肠道菌群测评等
+5. MemberSelection记录每次选择,可多次选择直到预算用完
+
+**选择流程:**
+```
+支付1314会员费
+│
+└── 进入等值商品选择页
+    ├── 浏览 eligible=true 的商品列表
+    ├── 选择商品(加入待选清单)
+    ├── 确认选择(扣减预算额度)
+    └── MemberSelection 记录选择记录
+```
+
+### 5.3 会员续费
+
+- 到期前30天提醒续费
+- 续费享受折扣(默认85折,通过SysConfig配置)
+- 续费可选择使用积分抵扣
+
+### 5.4 会员资格传递
+
+会员资格属于**家庭**而非个人:
+- 家庭任一成员开通后,家庭内所有成员受益
+- 孩子共享家庭会员资格
+- 家庭管理员的会员状态代表整个家庭
+
+---
+
+## 6. 积分抵扣会员费
+
+### 6.1 抵扣规则
+
+| 参数 | 默认值 | 通过SysConfig可调 |
+|------|--------|------------------|
+| 积分兑换汇率 | 1积分=0.01元 | ✅ `points_exchange_rate` |
+| 抵扣上限 | 应付金额的50% | ✅ `points_deduct_cap` |
+| 抵扣单位 | 100积分整数倍 | 硬编码(不改) |
+
+### 6.2 抵扣流程
+
+```
+会员购买/续费
+│
+├── 输入抵扣积分数量
+│   └── 校验: 不超过上限、积分余额充足
+│
+├── 计算:
+│   ├── 可抵扣金额 = min(积分×汇率, 应付金额×上限比例)
+│   ├── 实际扣除积分 = 可抵扣金额 / 汇率(取整)
+│   └── 需支付金额 = 应付金额 - 可抵扣金额
+│
+├── PointsLog新增记录(type=member_deduct, amount=负值)
+│
+└── 支付剩余金额(微信支付)
+```
+
+### 6.3 PointsService扩展
+
+```java
+// 现有方法保持不变
+public int awardSystemPoints(Long childId, int amount, String reason)
+public int deductSystemPoints(Long childId, int amount, String reason)
+
+// 新增:积分抵扣
+public int deductForMembership(Long userId, int amount, Long membershipId, String reason)
+```
+
+---
+
+## 7. 平台对接能力
+
+### 7.1 对接模式
+
+通过 `externalSource` + `externalId` 模式实现对接扩展,Phase 0仅定义接口规范,Phase 3+实现。
+
+### 7.2 对接扩展点
+
+| 能力 | 接口路径 | 方向 | 说明 |
+|------|---------|------|------|
+| 商品同步 | `/api/integration/product/sync` | 双向 | 外部平台商品推/拉 |
+| 订单同步 | `/api/integration/order/sync` | 双向 | 订单状态同步 |
+| 会员同步 | `/api/integration/member/sync` | 双向 | 会员等级/状态同步 |
+| 商品查询 | `/api/integration/product/query` | 查询 | 查询外部平台商品 |
+
+### 7.3 Lilishop对齐
+
+Product模型设计参考Lilishop Goods.java (`li_goods`):
+
+| Lilishop Goods字段 | ZXYJ Product字段 | 说明 |
+|-------------------|-----------------|------|
+| `goodsName` | `name` | 商品名称 |
+| `price` | `price` | 商品价格 |
+| `intro` | `intro` | 商品详情 |
+| `thumbnail/small/original` | `coverImage`, `images` | 商品图片 |
+| `quantity` | `stock` | 库存 |
+| `storeId` | `vendorId` | 服务商ID |
+| `goodsType` | `productType` | 商品类型 |
+| `authFlag` | `status` | 审核状态 |
+| `goodsUnit` | — | 暂不需要 |
+| `brandId` | — | 暂不需要 |
+| **新增** | `memberPrice` | 会员价(ZXYJ扩展) |
+| **新增** | `domain` | 身心智行标签(ZXYJ扩展) |
+| **新增** | `memberEligible` | 等值预算标记(ZXYJ扩展) |
+| **新增** | `externalSource/Id` | 对接扩展字段 |
+
+### 7.4 接口规范文档
+
+对接接口规范在 Phase 0 输出到 `docs/superpowers/specs/integration-api.md`,内容包括:
+- REST接口定义(请求/响应格式)
+- 认证方式(API Key或JWT)
+- 数据同步策略(全量/增量)
+- 错误码定义
+
+Phase 3+ 开始实现具体对接代码。
+
+---
+
+## 8. 现有功能迁移策略
+
+**核心原则:零删除、增量接入。**
+
+| 现有功能 | 在新架构中的位置 | 改动 |
+|---------|----------------|------|
+| 任务管理 | 首页快捷入口+"发现"子入口 | Tab移除(不再是TabBar页),保留页面文件和路由 |
+| 心愿单/积分兑换 | "我的"→积分中心,或首页快捷入口 | Tab移除(不再是TabBar页),保留页面文件和路由 |
+| 成长规划师 | 服务商体系中的`vendorType=planner` | 扩展现有规划师审核流程,新增vendorType/vendorStatus |
+| 申请成为规划师 | "我的"→服务商入驻→选择planner | 扩展入驻表单 |
+| 测评系统 | Product体系中的`productType=service` | 作为商品发布(如"肠道菌群测评") |
+| 小游戏 | 首页快捷入口 | 不变 |
+| 成长档案 | 1314等值商品之一 | 纳入memberEligible范围 |
+| 学习计划/套餐 | Product体系中的`productType=course` | 以商品形式发布 |
+| 地址四级联动 | 保留 | 不变 |
+| 多数据源sfms | 保留 | 不变 |
+| 积分系统 | 保留+扩展(可抵扣会员费) | PointsLog新增type=member_deduct |
+
+---
+
+## 9. API与Controller设计
+
+### 9.1 Controller包结构
+
+```
+controller/
+├── admin/                # EXISTING(新增配置管理+商品审核等)
+├── auth/                 # EXISTING(不变)
+├── family/               # EXISTING(不变)
+├── task/                 # EXISTING(不变)
+├── reward/               # EXISTING(不变)
+├── guide/                # EXISTING(不变)
+│
+├── product/              # NEW - Phase 1
+│   ├── ProductController.java
+│   ├── ProductOrderController.java
+│   └── ProductVendorController.java
+├── membership/           # NEW - Phase 2
+│   ├── MembershipController.java
+│   └── MemberSelectionController.java
+├── vendor/               # NEW - Phase 0(服务商入驻)
+│   └── VendorController.java
+└── config/               # NEW - Phase 1
+    └── SysConfigController.java
+```
+
+### 9.2 新增API端点
+
+#### VendorController(Phase 0)`@RequestMapping("/api/vendor")`
+
+| 端点 | 说明 | 权限 |
+|------|------|------|
+| `/apply` | 提交服务商入驻申请 | 登录用户 |
+| `/status` | 查询入驻审核状态 | 申请人 |
+| `/info` | 获取/编辑服务商信息 | 已通过服务商 |
+
+#### ProductController(Phase 1)`@RequestMapping("/api/product")`
+
+| 端点 | 说明 | 权限 |
+|------|------|------|
+| `/list` | 商品列表(分页+按productType筛选) | 所有人 |
+| `/detail` | 商品详情 | 所有人 |
+| `/create` | 发布商品 | 已通过服务商/管理员 |
+| `/update` | 编辑商品 | 商品所属服务商/管理员 |
+| `/shelve` | 上架商品 | 服务商/管理员 |
+| `/unshelve` | 下架商品 | 服务商/管理员 |
+| `/review` | 审核商品(admin) | 管理员 |
+
+#### ProductOrderController(Phase 1)`@RequestMapping("/api/product/order")`
+
+| 端点 | 说明 | 权限 |
+|------|------|------|
+| `/create` | 创建订单 | 登录用户 |
+| `/pay` | 支付订单 | 订单创建者 |
+| `/list` | 我的订单列表 | 登录用户 |
+| `/detail` | 订单详情 | 订单创建者/服务商 |
+| `/cancel` | 取消订单 | 订单创建者 |
+| `/confirm` | 确认完成 | 服务商/管理员 |
+
+#### MembershipController(Phase 2)`@RequestMapping("/api/membership")`
+
+| 端点 | 说明 | 权限 |
+|------|------|------|
+| `/info` | 查询家庭会员信息 | 家庭管理员 |
+| `/levels` | 会员等级列表(含价格/权益) | 所有人 |
+| `/purchase` | 购买/续费会员 | 家庭管理员 |
+| `/renew` | 续费会员 | 家庭管理员 |
+| `/eligible-products` | 等值商品选择列表 | 标准会员家庭 |
+| `/select-product` | 选择等值商品 | 家庭管理员 |
+
+#### SysConfigController(Phase 1)`@RequestMapping("/api/admin/config")`
+
+| 端点 | 说明 | 权限 |
+|------|------|------|
+| `/list` | 配置列表 | 管理员 |
+| `/update` | 更新配置 | 管理员 |
+| `/get/{key}` | 获取单个配置 | 管理员 |
+
+### API约定
+
+- ✅ 所有路径以 `/api/` 开头
+- ✅ 统一使用 `@PostMapping`
+- ✅ 统一返回 `Result<T>`
+- ✅ DI使用 `@Resource`
+
+---
+
+## 10. 前端设计
+
+### 10.1 新增页面
+
+| Phase | 页面路径 | 说明 |
+|-------|---------|------|
+| 0 | `pages/discover/index.vue` | 发现Tab首页(活动+课程聚合) |
+| 0 | `pages/shop/index.vue` | 商城Tab首页(商品列表) |
+| 0 | `pages/discover/product-detail.vue` | 商品详情(可复用于发现和商城) |
+| 1 | `pages/shop/order-list.vue` | 订单列表 |
+| 1 | `pages/shop/order-detail.vue` | 订单详情 |
+| 1 | `pages/vendor/apply.vue` | 服务商入驻申请 |
+| 1 | `pages/vendor/center.vue` | 服务商中心 |
+| 2 | `pages/membership/center.vue` | 会员中心(含等值商品选择) |
+| 2 | `pages/membership/purchase.vue` | 购买/续费会员 |
+| 2 | `pages/membership/selection.vue` | 1314等值商品选择页 |
+
+### 10.2 首页改造
+
+**parent-index.vue / child-index.vue**
+
+新增会员Banner区域在顶部,展示当前会员状态(体验/标准/高级)和到期日,引导未开通用户开通。
+
+```
+会员Banner(新增)
+→ 快捷入口(保留现有功能入口,调整排序)
+→ 今日重点/任务进度(保留)
+→ 推荐活动/课程(新增)
+→ 底部占位
+```
+
+### 10.3 商城页(shop/index.vue)
+
+```
+商城首页
+├── 分类切换(全部/活动/课程/实物/数字/服务)
+├── 商品卡片列表(网格)
+│   ├── 封面图 + 标题 + 价格 + 会员价
+│   └── 点击进入详情
+└── 加载更多
+```
+
+### 10.4 发现页(discover/index.vue)
+
+```
+发现首页
+├── 顶部搜索/筛选
+├── 推荐活动(productType=activity)
+├── 推荐课程(productType=course)
+└── 更多内容
+```
+
+### 10.5 我的页面扩展(profile/profile.vue)
+
+```
+我的(现有内容保留,新增入口)
+├── 用户信息卡片
+├── 会员中心入口(新增,显示当前会员等级)
+├── 订单管理(新增)
+├── 积分中心(现有,扩展)
+├── 服务商中心(新增,仅服务商可见)
+├── 家庭管理(现有)
+├── 设置/关于(现有)
+└── ...
+```
+
+### 前端约定
+
+- ✅ Vue 2 Options API
+- ✅ uni-app标签 (`<view>`, `<text>`, `<image>`)
+- ✅ rpx单位
+- ✅ 禁止可选链 `?.`(用 `&&` 替代)
+- ✅ 所有API用POST方法
+- ✅ Claymorphism暖色风格(与现有一致)
+- ✅ 新页面必须注册到 pages.json
+
+---
+
+## 11. Web管理端设计
+
+### 11.1 新增管理页面
+
+| Phase | 页面 | 路由 | 说明 |
+|-------|------|------|------|
+| 0 | 服务商审核 | `/admin/vendor-review` | 审核服务商入驻申请(复用现有教师审核页面) |
+| 1 | 商品管理 | `/admin/product` | 商品CRUD+审核上下架 |
+| 1 | 订单管理 | `/admin/order` | 订单查看/退款 |
+| 1 | 系统配置 | `/admin/config` | SysConfig CRUD |
+| 2 | 会员管理 | `/admin/membership` | 会员记录查看 |
+
+### 11.2 复用策略
+
+- 现有管理端页面布局不变
+- 新增页面按新路由注册
+- 角色权限守卫扩展: admin角色可访问所有页面
+
+---
+
+## 12. 三阶段交付计划
+
+| Phase | 名称 | 新Entity | 新Controller | 新小程序页 | 新Web页 | 核心交付物 |
+|-------|------|---------|------------|-----------|---------|-----------|
+| **0** | 骨架 | 0(扩展User) | 1 (Vendor) | 3(发现/商城占位+服务商申请) | 1(服务商审核) | 角色模型扩展、TabBar改版、服务商入驻流程 |
+| **1** | 商品系统 | 3 (Product/Order/SysConfig) | 3 (Product/Order/Config) | 5(商品/订单/服务商中心) | 3(商品/订单/配置) | 统一商品发布/浏览/购买、服务商发布能力、可配置参数 |
+| **2** | 会员+积分 | 2 (Membership/Selection) + PointsLog扩展 | 2 (Membership/Selection) | 3(会员中心/购买/等值选择) | 1(会员管理) | 1314会员体系、等值商品选择、积分抵扣会员费、全部配置后台可调 |
+
+### Phase依赖关系
+
+```
+Phase 0(骨架)
+├── 角色模型扩展(任何后续Phase的基础)
+├── TabBar改版(UI骨架)
+└── 服务商入驻流程(Phase 1需要服务商发布商品)
+
+Phase 1(商品系统)
+├── 依赖Phase 0的角色和服务商模型
+├── Product发布/审核/列表/购买
+├── SysConfig后台可配置
+└── 为Phase 2会员体系提供商品基础
+
+Phase 2(会员+积分)
+├── 依赖Phase 1的Product和Order
+├── 依赖Phase 0的familyAdmin角色
+├── 会员开通/续费/积分抵扣
+└── 1314等值商品选择(依赖Product.memberEligible)
+```
+
+---
+
+## 13. Lilishop参考映射
+
+### 13.1 实体映射
+
+| Lilishop | ZXYJ | 差异 |
+|----------|------|------|
+| `Goods` (`li_goods`) | `Product` (`products`) | ZXYJ无SKU、增加memberPrice/domain/memberEligible |
+| `GoodsSku` (`li_goods_sku`) | — | Phase 0-2暂不支持SKU |
+| `Store` (`li_store`) | User.vendorInfo字段 | ZXYJ不拆分店铺表,服务商信息存在User的vendorInfo字段 |
+| `Member` (`li_member`) | `User` | 已有映射 |
+| `MemberGrade` (`li_member_grade`) | `FamilyMembership` + `SysConfig` | Lilishop等级基于消费金额,ZXYJ基于年费 |
+
+### 13.2 差异说明
+
+| 维度 | Lilishop | 溪艾福 |
+|------|----------|-----------|
+| 商品模型 | Goods + GoodsSku(必须SKU) | 统一Product(无SKU初期) |
+| 店铺 | 独立Store实体 | User.vendorInfo字段 |
+| 会员 | 用户级别(基于消费累计) | 家庭会员(基于年费) |
+| 商品类型 | physical/virtual | activity/course/physical/digital/service |
+| 对接方式 | 原生电商 | 定义接口规范后异步对接 |
+
+---
+
+## 14. 风险与约束
+
+| 风险 | 缓解措施 |
+|------|---------|
+| 微信小程序tabBar最多5项 | 使用4项tabBar,剩余通过首页快捷入口 |
+| uni-app不支持可选链 | 代码review时强制检查 |
+| DatabaseInitializer建表顺序 | 使用IF NOT EXISTS确保幂等 |
+| 服务商审核安全 | 复用现有JWT+角色权限体系 |
+| Product模型首次无SKU | 架构预留扩展性,Product本身可加sku字段 |
+| 积分抵扣复杂性 | 规则的汇率/上限通过SysConfig配置,运行时动态读取 |
+| 旧功能入口消失 | 所有原有页面文件保留,通过首页快捷入口访问 |
+
+---
+
+## 15. 约束遵守声明
+
+| 约束 | 状态 | 说明 |
+|------|------|------|
+| 不建索引 | ✅ | DatabaseInitializer仅建表,无INDEX |
+| 不建约束 | ✅ | 无FOREIGN KEY/CHECK/UNIQUE |
+| 不建自定义SQL | ✅ | 仅通过MyBatis-Plus BaseMapper操作 |
+| @PostMapping统一 | ✅ | 所有新Controller使用@PostMapping |
+| @Resource DI | ✅ | 所有新Service使用@Resource注入 |
+| java.util.Date | ✅ | 所有新Entity使用Date |
+| implements Serializable | ✅ | 所有新Entity实现Serializable |
+| 响应Result<T> | ✅ | 统一包装 |
+
+---
+
+## 16. Spec自审清单
+
+- [ ] 所有设计是否基于用户已确认的7个答案?
+- [ ] 品牌是否统一使用"溪艾福"?
+- [ ] 角色模型是否覆盖User/Service Provider/Admin三层?
+- [ ] 页面TabBar是否4项在微信限制内?
+- [ ] 首页五行沙盘是否采用五角星+相生相克+平衡成长设计?
+- [ ] Product实体是否对齐Lilishop Goods?
+- [ ] Product.domain是否包含5值(body/mind/wisdom/action/wealth)?
+- [ ] 会员体系是否3级(Free/Standard/Premium)+ 等值商品选择?
+- [ ] 积分抵扣逻辑是否完善(汇率/上限/PointsLog扩展)?
+- [ ] 服务商入驻是否复用现有审核流程?
+- [ ] SysConfig是否覆盖所有可配置参数?
+- [ ] Phase 0-2依赖关系是否合理?
+- [ ] 所有约束是否遵守(不建索引、不建约束、不建自定义SQL)?
+- [ ] 现有功能迁移策略是否零删除?
+- [ ] 对接能力是否仅定义规范、不写代码?
+
+---
+
+## 17. 五行哲学对齐 — 溪艾福品牌内核
+
+### 17.1 五行五维灵魂咬合
+
+| 五维 | 五行 | 属性 | 核心能力 | 灵魂咬合 | domain值 | 典型商品 |
+|------|------|------|---------|---------|---------|---------|
+| 行 | 木 | 生发·仁爱 | 关系践行、社群引流 | 关系的本质是"仁",木如大树根系交错打破孤岛 | `action` | 亲子活动、家庭沙龙、社群课程 |
+| 心 | 火 | 光明·主宰 | 情绪识别、心理管理 | 中医"心主神明属火",情绪管理如明灯照亮潜意识 | `mind` | 情绪管理课、心理测评、冥想 |
+| 身 | 土 | 长养·化生 | 菌群检测、活水康养 | 脾胃属土为后天之本,菌群平衡是生命健康轴心 | `body` | 肠道菌群检测、活性水、健康产品 |
+| 智 | 金 | 收敛·精密 | 多模态测评、脑力升级 | 脑科学数据严谨性,金之"锐利与收敛"萃取天赋 | `wisdom` | 认知测评、专注力训练、学习计划 |
+| 富 | 水 | 流通·承袭 | 创富合伙、资产传承 | "财富如流水",水是润泽后代的管道收益 | `wealth` | 创业合伙人计划、亲子财商课 |
+
+### 17.2 五行相生 — 用户生命周期闭环
+
+```
+【木生火】:社群活动(木)→点燃心火、驱散焦虑(火)
+   ↓ 机制:通过关系践行与家庭沙龙让会员感受到爱与连接
+   
+【火生土】:情绪理顺(火)→肠脑轴正向作用于肠道微生态(土)
+   ↓ 机制:心火生脾土,神经递质传导正向调节菌群
+   
+【土生金】:菌群调好(土)→大脑高质量营养→认知升级(金)
+   ↓ 机制:微生态底座稳固,催生敏锐精准的高维认知
+   
+【金生水】:顶级认知(金)→看清趋势、运筹帷幄→财富如流水(水)
+   ↓ 机制:科学方法论与脑力优势转化为商业决策力
+   
+【水生木】:财富传承(水)→反哺圈层→赋能更多家庭关系升级(木)
+   ↓ 机制:物质自由后投入大爱践行,形成正向飞轮
+```
+
+### 17.3 五行相克 — 维度制衡与平衡
+
+| 克制关系 | 含义 | 产品策略 |
+|---------|------|---------|
+| 木克土 | 关系践行制约过度养生(不与社会脱节) | 活动与菌群检测打包推荐 |
+| 土克水 | 根基稳固制约盲目逐利(健康优先) | 健康评估前置财富课程 |
+| 水克火 | 财富管理制约情绪冲动(理性决策) | 财商课缓解焦虑消费 |
+| 火克金 | 情绪稳定提升认知效率(心静智明) | 情绪管理前置认知训练 |
+| 金克木 | 逻辑分析优化关系模式(理性沟通) | 认知测评指导家庭沟通 |
+
+### 17.4 首页五角星沙盘 — 视觉设计规范
+
+**布局(自上而下):**
+
+```
+              🔥 心·火(南/顶)
+             /        \
+            /          \
+    🌿 行·木(东/左)    💧 富·水(西/右)
+            \          /
+             \        /
+        ⚔️ 智·金(西南/左下)—— 🌏 身·土(东北/右下)
+```
+
+**视觉规范:**
+
+| 元素 | 规范 |
+|------|------|
+| 五角星 | 每个角对应一个维度,角尖向外,形成正五角星 |
+| 外圈连线(相生) | 按木→火→土→金→水→木方向,带箭头指引,用户完成维度后连线高亮 |
+| 内星连线(相克) | 五角星内的交叉连线展示克制关系,水墨/淡色显示 |
+| 中心点 | 圆形徽章显示"综合成长力"百分比,周围5个小光点对应5维点亮状态 |
+| 颜色方案 | 木=绿(#4CAF50) / 火=红(#FF5252) / 土=棕(#8D6E63) / 金=金(#FFD700) / 水=蓝(#2196F3) |
+| 点亮状态 | 已完成维度=彩色渐变+微光动效 / 未完成=灰色半透明+虚线轮廓 |
+| 点击交互 | 点击维度角→进入该domain的产品列表;点击中心→进入个人五行报告 |
+| Phase 0 | 静态展示,5维全灰,中心显示"完成测评,点亮你的五行能量"引导文案 |