Jelajahi Sumber

fix: 注册teacher目录页面到pages.json

User 4 bulan lalu
induk
melakukan
8f996914bf
30 mengubah file dengan 1428 tambahan dan 424 penghapusan
  1. 1 0
      dev-browser
  2. 167 13
      docs/系统测试/.test-sync.json
  3. 105 0
      docs/系统测试/test-reports/test-report-1777909314594.html
  4. 26 0
      docs/系统测试/test-reports/test-report-1777909314599.json
  5. 105 0
      docs/系统测试/test-reports/test-report-1777909329835.html
  6. 26 0
      docs/系统测试/test-reports/test-report-1777909329842.json
  7. 1 68
      zxyj-backend/.factorypath
  8. 2 2
      zxyj-backend/pom.xml
  9. 78 0
      zxyj-backend/src/main/java/com/zxyj/controller/admin/AdminGuideController.java
  10. 1 1
      zxyj-backend/src/main/java/com/zxyj/controller/admin/ServiceTypeController.java
  11. 49 201
      zxyj-backend/src/main/java/com/zxyj/controller/guide/GuideController.java
  12. 1 1
      zxyj-backend/src/main/java/com/zxyj/controller/guide/GuideTeamController.java
  13. 33 0
      zxyj-backend/src/main/java/com/zxyj/entity/Guide.java
  14. 44 0
      zxyj-backend/src/main/java/com/zxyj/entity/GuideApplication.java
  15. 29 0
      zxyj-backend/src/main/java/com/zxyj/entity/GuideFamilyRelation.java
  16. 8 11
      zxyj-backend/src/main/java/com/zxyj/entity/GuidePackage.java
  17. 38 0
      zxyj-backend/src/main/java/com/zxyj/entity/UpgradeService.java
  18. 9 0
      zxyj-backend/src/main/java/com/zxyj/mapper/GuideApplicationMapper.java
  19. 9 0
      zxyj-backend/src/main/java/com/zxyj/mapper/GuideFamilyRelationMapper.java
  20. 9 0
      zxyj-backend/src/main/java/com/zxyj/mapper/GuideMapper.java
  21. 9 0
      zxyj-backend/src/main/java/com/zxyj/mapper/UpgradeServiceMapper.java
  22. 91 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideApplicationService.java
  23. 23 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideFamilyRelationService.java
  24. 28 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideService.java
  25. 101 1
      zxyj-backend/src/main/resources/schema_guide.sql
  26. 30 0
      zxyj-frontend/pages.json
  27. 116 35
      zxyj-web/src/api/guide.js
  28. 5 0
      zxyj-web/src/views/Layout.vue
  29. 1 1
      zxyj-web/src/views/teacher/TeacherAssessment.vue
  30. 283 90
      zxyj-web/src/views/teacher/TeacherMessages.vue

+ 1 - 0
dev-browser

@@ -0,0 +1 @@
+Subproject commit 9c85e469e213badb5c5ff96987553e6ff05df9f4

+ 167 - 13
docs/系统测试/.test-sync.json

@@ -1,5 +1,5 @@
 {
-  "timestamp": "2026-04-25T12:42:15.255Z",
+  "timestamp": "2026-05-04T15:41:54.576Z",
   "requirements": [
     {
       "id": "TASK-001",
@@ -402,7 +402,7 @@
     },
     {
       "id": "MODE-001",
-      "description": "家长模式:发布任务、审批奖励、查看报告,也可执行由孩子或指导师安排的任务",
+      "description": "家长模式:发布任务、审批奖励、查看报告,也可执行由孩子或成长规划师安排的任务",
       "priority": "P1",
       "module": "双模式切换模块",
       "subModule": "模式定义"
@@ -465,7 +465,7 @@
     },
     {
       "id": "MODE-010",
-      "description": "指导师注册与审核",
+      "description": "成长规划师注册与审核",
       "priority": "P2",
       "module": "双模式切换模块",
       "subModule": "多角色管理"
@@ -479,7 +479,7 @@
     },
     {
       "id": "MODE-012",
-      "description": "谁制定任务谁审核;孩子任务可在创建时指定家长/自动/指导师审核",
+      "description": "谁制定任务谁审核;孩子任务可在创建时指定家长/自动/成长规划师审核",
       "priority": "P1",
       "module": "双模式切换模块",
       "subModule": "多角色管理"
@@ -771,68 +771,222 @@
       "module": "DAN测评记录模块",
       "subModule": "模板生成"
     },
+    {
+      "id": "ASSESS-001",
+      "description": "管理员创建/编辑/删除测评资料",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-002",
+      "description": "管理员配置积分值(完成获得积分)和积分价值(1积分=元)",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-003",
+      "description": "成长规划师查看所负责家庭的测评配置列表",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-004",
+      "description": "成长规划师为家庭启用DAN测评任务",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-005",
+      "description": "成长规划师修改已启用家庭的提醒周期",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-006",
+      "description": "成长规划师关闭家庭的DAN测评任务",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-007",
+      "description": "家长查看当前DAN测评任务详情",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-008",
+      "description": "家长查看DAN测评历史记录",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-009",
+      "description": "外部系统回调完成DAN测评任务",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "ASSESS-010",
+      "description": "管理员设置资料状态(启用/停用)",
+      "priority": "P1",
+      "module": "DAN测评任务系统(系统级任务)",
+      "subModule": "功能清单"
+    },
     {
       "id": "THEME-001",
       "description": "默认主题(一套主题,先行实现)",
       "priority": "P1",
       "module": "主题皮肤模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "THEME-002",
       "description": "阳光橙主题",
       "priority": "P2",
       "module": "主题皮肤模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "THEME-003",
       "description": "恐龙探险主题",
       "priority": "P2",
       "module": "主题皮肤模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "THEME-004",
       "description": "太空冒险主题",
       "priority": "P2",
       "module": "主题皮肤模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "THEME-005",
       "description": "梦幻城堡主题",
       "priority": "P2",
       "module": "主题皮肤模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "THEME-006",
       "description": "海洋世界主题",
       "priority": "P2",
       "module": "主题皮肤模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "EXPORT-001",
       "description": "任务历史导出(Excel格式)",
       "priority": "P2",
       "module": "数据导出模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "EXPORT-002",
       "description": "错题本图片生成",
       "priority": "P2",
       "module": "数据导出模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
     },
     {
       "id": "EXPORT-003",
       "description": "成长周报导出",
       "priority": "P2",
       "module": "数据导出模块",
-      "subModule": "模板生成"
+      "subModule": "默认配置值"
+    },
+    {
+      "id": "GUIDE-001",
+      "description": "规划师注册(直接注册/家长申请)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-002",
+      "description": "套餐选择与购买",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-003",
+      "description": "付款凭证上传",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-004",
+      "description": "申请状态查看(申请中/已通过/已拒绝)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-005",
+      "description": "管理员审核申请(通过/拒绝)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-006",
+      "description": "规划师等级查看",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-007",
+      "description": "升级服务购买(升中级/直升高级/升高级)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-008",
+      "description": "升级可见性控制(根据当前等级显示对应选项)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-009",
+      "description": "家庭列表查看(以家庭为单位)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-010",
+      "description": "家庭详情查看(成员信息、任务完成情况)",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-011",
+      "description": "规划师身份切换为家长",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
+    },
+    {
+      "id": "GUIDE-012",
+      "description": "规划师不能切换为孩子",
+      "priority": "P1",
+      "module": "成长规划师注册与等级系统",
+      "subModule": "功能清单"
     }
   ]
 }

+ 105 - 0
docs/系统测试/test-reports/test-report-1777909314594.html

@@ -0,0 +1,105 @@
+
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>心知益家 - 测试报告</title>
+  <style>
+    * { margin: 0; padding: 0; box-sizing: border-box; }
+    body { font-family: 'Microsoft YaHei', sans-serif; background: #f5f5f5; padding: 20px; }
+    .container { max-width: 1400px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
+    h1 { color: #333; margin-bottom: 20px; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; }
+    h2 { color: #555; margin: 25px 0 15px; padding-left: 10px; border-left: 4px solid #4CAF50; }
+    h3 { color: #666; margin: 15px 0 10px; }
+    
+    .summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin: 20px 0; }
+    .summary-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 25px; border-radius: 12px; text-align: center; }
+    .summary-card.passed { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); }
+    .summary-card.failed { background: linear-gradient(135deg, #eb3349 0%, #f45c5d 100%); }
+    .summary-card.rate { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
+    .summary-card h3 { font-size: 42px; margin-bottom: 8px; color: white; }
+    .summary-card p { font-size: 14px; opacity: 0.9; }
+
+    .test-section { margin: 30px 0; padding: 20px; background: #fafafa; border-radius: 8px; }
+    .test-type { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-right: 10px; }
+    .test-type.api { background: #e3f2fd; color: #1565c0; }
+    .test-type.web { background: #f3e5f5; color: #7b1fa2; }
+
+    table { width: 100%; border-collapse: collapse; margin: 15px 0; }
+    th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e0e0e0; }
+    th { background: #4CAF50; color: white; font-weight: 600; }
+    tr:hover { background: #f5f5f5; }
+
+    .status-passed { color: #4CAF50; font-weight: bold; }
+    .status-failed { color: #f44336; font-weight: bold; }
+    
+    .code-block { background: #263238; color: #eceff1; padding: 15px; border-radius: 4px; font-family: 'Consolas', monospace; font-size: 12px; overflow-x: auto; margin: 10px 0; }
+    .code-block .label { color: #80cbc4; font-weight: bold; }
+
+    .timestamp { color: #999; font-size: 12px; margin-top: 30px; text-align: center; }
+    
+    .screenshot-list { margin: 10px 0; }
+    .screenshot-list span { display: inline-block; background: #e8f5e9; color: #2e7d32; padding: 4px 8px; border-radius: 4px; font-size: 11px; margin: 2px; }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <h1>心知益家 - 自动化测试报告</h1>
+
+    <div class="summary">
+      <div class="summary-card">
+        <h3>0</h3>
+        <p>总测试数</p>
+      </div>
+      <div class="summary-card passed">
+        <h3>0</h3>
+        <p>通过 ✅</p>
+      </div>
+      <div class="summary-card failed">
+        <h3>0</h3>
+        <p>失败 ❌</p>
+      </div>
+      <div class="summary-card rate">
+        <h3>0%</h3>
+        <p>通过率</p>
+      </div>
+    </div>
+
+    <div class="test-section">
+      <h2>API接口测试结果(小程序端)</h2>
+      <p style="color: #666; margin: 10px 0;">测试策略:构造请求数据,调用后端接口,验证响应,不进行UI点击</p>
+      <table>
+        <tr>
+          <th>测试ID</th>
+          <th>描述</th>
+          <th>优先级</th>
+          <th>状态</th>
+          <th>请求数据</th>
+        </tr>
+        
+      </table>
+      <h3>API测试数据汇总</h3>
+      
+    </div>
+
+    <div class="test-section">
+      <h2>Web浏览器测试结果(管理后台)</h2>
+      <p style="color: #666; margin: 10px 0;">测试策略:浏览器自动化测试,管理员和成长规划师登录,测试过程截图</p>
+      <p style="color: #999; font-size: 12px;">测试账号:管理员 N/A / 成长规划师 N/A</p>
+      <table>
+        <tr>
+          <th>测试ID</th>
+          <th>描述</th>
+          <th>优先级</th>
+          <th>状态</th>
+          <th>截图</th>
+        </tr>
+        
+      </table>
+    </div>
+
+    <p class="timestamp">报告生成时间: 2026-05-04T15:41:54.594Z</p>
+  </div>
+</body>
+</html>

+ 26 - 0
docs/系统测试/test-reports/test-report-1777909314599.json

@@ -0,0 +1,26 @@
+{
+  "timestamp": "2026-05-04T15:41:54.599Z",
+  "testStrategy": {
+    "api": "小程序端API接口测试,构造请求数据,验证响应,不进行UI点击",
+    "web": "管理后台浏览器自动化测试,管理员和成长规划师登录,测试过程截图"
+  },
+  "accounts": {
+    "admin": "13800138000 / admin123",
+    "teacher": "13800138003 / teacher123",
+    "note": "家长和孩子不登录Web后台"
+  },
+  "summary": {
+    "api": {
+      "total": 0,
+      "passed": 0,
+      "failed": 0
+    },
+    "web": {
+      "total": 0,
+      "passed": 0,
+      "failed": 0
+    }
+  },
+  "apiTests": [],
+  "webTests": []
+}

+ 105 - 0
docs/系统测试/test-reports/test-report-1777909329835.html

@@ -0,0 +1,105 @@
+
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>心知益家 - 测试报告</title>
+  <style>
+    * { margin: 0; padding: 0; box-sizing: border-box; }
+    body { font-family: 'Microsoft YaHei', sans-serif; background: #f5f5f5; padding: 20px; }
+    .container { max-width: 1400px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
+    h1 { color: #333; margin-bottom: 20px; border-bottom: 3px solid #4CAF50; padding-bottom: 10px; }
+    h2 { color: #555; margin: 25px 0 15px; padding-left: 10px; border-left: 4px solid #4CAF50; }
+    h3 { color: #666; margin: 15px 0 10px; }
+    
+    .summary { display: grid; grid-template-columns: repeat(4, 1fr); gap: 20px; margin: 20px 0; }
+    .summary-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 25px; border-radius: 12px; text-align: center; }
+    .summary-card.passed { background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%); }
+    .summary-card.failed { background: linear-gradient(135deg, #eb3349 0%, #f45c5d 100%); }
+    .summary-card.rate { background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); }
+    .summary-card h3 { font-size: 42px; margin-bottom: 8px; color: white; }
+    .summary-card p { font-size: 14px; opacity: 0.9; }
+
+    .test-section { margin: 30px 0; padding: 20px; background: #fafafa; border-radius: 8px; }
+    .test-type { display: inline-block; padding: 4px 12px; border-radius: 20px; font-size: 12px; font-weight: bold; margin-right: 10px; }
+    .test-type.api { background: #e3f2fd; color: #1565c0; }
+    .test-type.web { background: #f3e5f5; color: #7b1fa2; }
+
+    table { width: 100%; border-collapse: collapse; margin: 15px 0; }
+    th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e0e0e0; }
+    th { background: #4CAF50; color: white; font-weight: 600; }
+    tr:hover { background: #f5f5f5; }
+
+    .status-passed { color: #4CAF50; font-weight: bold; }
+    .status-failed { color: #f44336; font-weight: bold; }
+    
+    .code-block { background: #263238; color: #eceff1; padding: 15px; border-radius: 4px; font-family: 'Consolas', monospace; font-size: 12px; overflow-x: auto; margin: 10px 0; }
+    .code-block .label { color: #80cbc4; font-weight: bold; }
+
+    .timestamp { color: #999; font-size: 12px; margin-top: 30px; text-align: center; }
+    
+    .screenshot-list { margin: 10px 0; }
+    .screenshot-list span { display: inline-block; background: #e8f5e9; color: #2e7d32; padding: 4px 8px; border-radius: 4px; font-size: 11px; margin: 2px; }
+  </style>
+</head>
+<body>
+  <div class="container">
+    <h1>心知益家 - 自动化测试报告</h1>
+
+    <div class="summary">
+      <div class="summary-card">
+        <h3>0</h3>
+        <p>总测试数</p>
+      </div>
+      <div class="summary-card passed">
+        <h3>0</h3>
+        <p>通过 ✅</p>
+      </div>
+      <div class="summary-card failed">
+        <h3>0</h3>
+        <p>失败 ❌</p>
+      </div>
+      <div class="summary-card rate">
+        <h3>0%</h3>
+        <p>通过率</p>
+      </div>
+    </div>
+
+    <div class="test-section">
+      <h2>API接口测试结果(小程序端)</h2>
+      <p style="color: #666; margin: 10px 0;">测试策略:构造请求数据,调用后端接口,验证响应,不进行UI点击</p>
+      <table>
+        <tr>
+          <th>测试ID</th>
+          <th>描述</th>
+          <th>优先级</th>
+          <th>状态</th>
+          <th>请求数据</th>
+        </tr>
+        
+      </table>
+      <h3>API测试数据汇总</h3>
+      
+    </div>
+
+    <div class="test-section">
+      <h2>Web浏览器测试结果(管理后台)</h2>
+      <p style="color: #666; margin: 10px 0;">测试策略:浏览器自动化测试,管理员和成长规划师登录,测试过程截图</p>
+      <p style="color: #999; font-size: 12px;">测试账号:管理员 N/A / 成长规划师 N/A</p>
+      <table>
+        <tr>
+          <th>测试ID</th>
+          <th>描述</th>
+          <th>优先级</th>
+          <th>状态</th>
+          <th>截图</th>
+        </tr>
+        
+      </table>
+    </div>
+
+    <p class="timestamp">报告生成时间: 2026-05-04T15:42:09.835Z</p>
+  </div>
+</body>
+</html>

+ 26 - 0
docs/系统测试/test-reports/test-report-1777909329842.json

@@ -0,0 +1,26 @@
+{
+  "timestamp": "2026-05-04T15:42:09.842Z",
+  "testStrategy": {
+    "api": "小程序端API接口测试,构造请求数据,验证响应,不进行UI点击",
+    "web": "管理后台浏览器自动化测试,管理员和成长规划师登录,测试过程截图"
+  },
+  "accounts": {
+    "admin": "13800138000 / admin123",
+    "teacher": "13800138003 / teacher123",
+    "note": "家长和孩子不登录Web后台"
+  },
+  "summary": {
+    "api": {
+      "total": 0,
+      "passed": 0,
+      "failed": 0
+    },
+    "web": {
+      "total": 0,
+      "passed": 0,
+      "failed": 0
+    }
+  },
+  "apiTests": [],
+  "webTests": []
+}

+ 1 - 68
zxyj-backend/.factorypath

@@ -1,70 +1,3 @@
 <factorypath>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-web/2.7.18/spring-boot-starter-web-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter/2.7.18/spring-boot-starter-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot/2.7.18/spring-boot-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-logging/2.7.18/spring-boot-starter-logging-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-classic/1.2.12/logback-classic-1.2.12.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/ch/qos/logback/logback-core/1.2.12/logback-core-1.2.12.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-to-slf4j/2.17.2/log4j-to-slf4j-2.17.2.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/logging/log4j/log4j-api/2.17.2/log4j-api-2.17.2.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/jul-to-slf4j/1.7.36/jul-to-slf4j-1.7.36.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/yaml/snakeyaml/1.30/snakeyaml-1.30.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-json/2.7.18/spring-boot-starter-json-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.13.5/jackson-datatype-jdk8-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.13.5/jackson-datatype-jsr310-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/module/jackson-module-parameter-names/2.13.5/jackson-module-parameter-names-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-tomcat/2.7.18/spring-boot-starter-tomcat-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-core/9.0.83/tomcat-embed-core-9.0.83.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.83/tomcat-embed-websocket-9.0.83.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-web/5.3.31/spring-web-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-beans/5.3.31/spring-beans-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-webmvc/5.3.31/spring-webmvc-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-aop/5.3.31/spring-aop-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-context/5.3.31/spring-context-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-expression/5.3.31/spring-expression-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-boot-starter/3.5.3.1/mybatis-plus-boot-starter-3.5.3.1.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus/3.5.3.1/mybatis-plus-3.5.3.1.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-extension/3.5.3.1/mybatis-plus-extension-3.5.3.1.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-core/3.5.3.1/mybatis-plus-core-3.5.3.1.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/baomidou/mybatis-plus-annotation/3.5.3.1/mybatis-plus-annotation-3.5.3.1.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/github/jsqlparser/jsqlparser/4.4/jsqlparser-4.4.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/mybatis/3.5.10/mybatis-3.5.10.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/mybatis/mybatis-spring/2.0.7/mybatis-spring-2.0.7.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-autoconfigure/2.7.18/spring-boot-autoconfigure-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-jdbc/2.7.18/spring-boot-starter-jdbc-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/zaxxer/HikariCP/4.0.3/HikariCP-4.0.3.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jdbc/5.3.31/spring-jdbc-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-tx/5.3.31/spring-tx-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/mysql/mysql-connector-j/8.0.33/mysql-connector-j-8.0.33.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt-api/0.11.5/jjwt-api-0.11.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt-impl/0.11.5/jjwt-impl-0.11.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/io/jsonwebtoken/jjwt-jackson/0.11.5/jjwt-jackson-0.11.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-databind/2.13.5/jackson-databind-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-annotations/2.13.5/jackson-annotations-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/core/jackson-core/2.13.5/jackson-core-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.30/lombok-1.18.30.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/boot/spring-boot-starter-validation/2.7.18/spring-boot-starter-validation-2.7.18.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/tomcat/embed/tomcat-embed-el/9.0.83/tomcat-embed-el-9.0.83.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/hibernate/validator/hibernate-validator/6.2.5.Final/hibernate-validator-6.2.5.Final.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/validation/jakarta.validation-api/2.0.2/jakarta.validation-api-2.0.2.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/jboss/logging/jboss-logging/3.4.3.Final/jboss-logging-3.4.3.Final.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/fastjson/2.0.40/fastjson-2.0.40.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/fastjson2/fastjson2-extension/2.0.40/fastjson2-extension-2.0.40.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/alibaba/fastjson2/fastjson2/2.0.40/fastjson2-2.0.40.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springdoc/springdoc-openapi-ui/1.8.0/springdoc-openapi-ui-1.8.0.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springdoc/springdoc-openapi-webmvc-core/1.8.0/springdoc-openapi-webmvc-core-1.8.0.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springdoc/springdoc-openapi-common/1.8.0/springdoc-openapi-common-1.8.0.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-core/2.2.20/swagger-core-2.2.20.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/com/fasterxml/jackson/dataformat/jackson-dataformat-yaml/2.13.5/jackson-dataformat-yaml-2.13.5.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-annotations/2.2.20/swagger-annotations-2.2.20.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/io/swagger/core/v3/swagger-models/2.2.20/swagger-models-2.2.20.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/webjars/swagger-ui/5.11.8/swagger-ui-5.11.8.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-core/5.3.31/spring-core-5.3.31.jar" enabled="true" runInBatchMode="false"/>
-    <factorypathentry kind="VARJAR" id="M2_REPO/org/springframework/spring-jcl/5.3.31/spring-jcl-5.3.31.jar" enabled="true" runInBatchMode="false"/>
+    <factorypathentry kind="VARJAR" id="M2_REPO/org/projectlombok/lombok/1.18.32/lombok-1.18.32.jar" enabled="true" runInBatchMode="false"/>
 </factorypath>

+ 2 - 2
zxyj-backend/pom.xml

@@ -126,6 +126,8 @@
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-compiler-plugin</artifactId>
                 <configuration>
+                    <source>1.8</source>
+                    <target>1.8</target>
                     <annotationProcessorPaths>
                         <path>
                             <groupId>org.projectlombok</groupId>
@@ -133,8 +135,6 @@
                             <version>1.18.32</version>
                         </path>
                     </annotationProcessorPaths>
-                    <source>1.8</source>
-                    <target>1.8</target>
                 </configuration>
             </plugin>
         </plugins>

+ 78 - 0
zxyj-backend/src/main/java/com/zxyj/controller/admin/AdminGuideController.java

@@ -0,0 +1,78 @@
+package com.zxyj.controller.admin;
+
+import com.zxyj.common.Result;
+import com.zxyj.entity.Guide;
+import com.zxyj.entity.GuideApplication;
+import com.zxyj.entity.GuidePackage;
+import com.zxyj.service.GuideApplicationService;
+import com.zxyj.service.GuidePackageService;
+import com.zxyj.service.GuideService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/api/admin/guide")
+public class AdminGuideController {
+
+    @Autowired
+    private GuideApplicationService guideApplicationService;
+
+    @Autowired
+    private GuideService guideService;
+
+    @Autowired
+    private GuidePackageService guidePackageService;
+
+    @GetMapping("/applications/pending")
+    public Result<List<GuideApplication>> getPendingApplications() {
+        return Result.success(guideApplicationService.getPendingApplications());
+    }
+
+    @PostMapping("/applications/{id}/approve")
+    public Result<Boolean> approveApplication(@PathVariable Long id, @RequestParam Long adminId) {
+        return Result.success(guideApplicationService.approveApplication(id, adminId));
+    }
+
+    @PostMapping("/applications/{id}/reject")
+    public Result<Boolean> rejectApplication(@PathVariable Long id,
+                                             @RequestParam Long adminId,
+                                             @RequestParam String reason) {
+        return Result.success(guideApplicationService.rejectApplication(id, adminId, reason));
+    }
+
+    @GetMapping("/guides")
+    public Result<List<Guide>> getAllGuides() {
+        return Result.success(guideService.getActiveGuides());
+    }
+
+    @GetMapping("/guides/level/{level}")
+    public Result<List<Guide>> getGuidesByLevel(@PathVariable String level) {
+        return Result.success(guideService.getByLevel(level));
+    }
+
+    @GetMapping("/packages")
+    public Result<List<GuidePackage>> getAllPackages() {
+        return Result.success(guidePackageService.list());
+    }
+
+    @PostMapping("/packages")
+    public Result<Boolean> createPackage(@RequestBody GuidePackage pkg) {
+        return Result.success(guidePackageService.save(pkg));
+    }
+
+    @PutMapping("/packages/{id}")
+    public Result<Boolean> updatePackage(@PathVariable Long id, @RequestBody GuidePackage pkg) {
+        pkg.setId(id);
+        return Result.success(guidePackageService.updateById(pkg));
+    }
+
+    @PutMapping("/packages/{id}/status")
+    public Result<Boolean> updatePackageStatus(@PathVariable Long id, @RequestParam String status) {
+        GuidePackage pkg = new GuidePackage();
+        pkg.setId(id);
+        pkg.setStatus(status);
+        return Result.success(guidePackageService.updateById(pkg));
+    }
+}

+ 1 - 1
zxyj-backend/src/main/java/com/zxyj/controller/admin/ServiceTypeController.java

@@ -59,7 +59,7 @@ public class ServiceTypeController {
         return success ? Result.success(null) : Result.error("删除失败");
     }
 
-    @PostMapping("/{id}")
+    @PostMapping("/{id}/detail")
     @Operation(summary = "获取服务类型详情")
     public Result<ServiceType> getById(@PathVariable Long id) {
         ServiceType serviceType = serviceTypeService.getById(id);

+ 49 - 201
zxyj-backend/src/main/java/com/zxyj/controller/guide/GuideController.java

@@ -2,232 +2,80 @@ package com.zxyj.controller.guide;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.common.Result;
-import com.zxyj.dto.BindGuideDTO;
-import com.zxyj.dto.GuidePackageDTO;
-import com.zxyj.dto.GuidePackageTemplateDTO;
-import com.zxyj.entity.*;
-import com.zxyj.mapper.FamilyMapper;
-import com.zxyj.mapper.UserMapper;
-import com.zxyj.service.GuideFamilyService;
+import com.zxyj.entity.Guide;
+import com.zxyj.entity.GuideApplication;
+import com.zxyj.entity.GuidePackage;
+import com.zxyj.service.GuideApplicationService;
 import com.zxyj.service.GuidePackageService;
-import com.zxyj.service.GuidePackageTemplateService;
-import com.zxyj.service.ServiceContentService;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
+import com.zxyj.service.GuideService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
-import javax.annotation.Resource;
-import java.math.BigDecimal;
-import java.util.*;
-import java.util.stream.Collectors;
+import java.util.List;
 
-@Tag(name = "成长规划师管理", description = "成长规划师绑定家庭、管理套餐等接口")
 @RestController
 @RequestMapping("/api/guide")
 public class GuideController {
 
-    @Resource
-    private GuideFamilyService guideFamilyService;
+    @Autowired
+    private GuideApplicationService guideApplicationService;
 
-    @Resource
-    private FamilyMapper familyMapper;
+    @Autowired
+    private GuideService guideService;
 
-    @Resource
-    private UserMapper userMapper;
-
-    @Resource
-    private GuidePackageTemplateService templateService;
-
-    @Resource
+    @Autowired
     private GuidePackageService guidePackageService;
 
-    @Resource
-    private ServiceContentService serviceContentService;
-
-    /**
-     * 生成绑定邀请链接
-     */
-    @Operation(summary = "生成绑定邀请链接")
-    @PostMapping("/invite-link")
-    public Result<Map<String, String>> generateBindInvite(@RequestAttribute("userId") Long userId) {
-        // 获取当前用户家庭
-        User user = new User();
-        user.setId(userId);
-        
-        // 生成邀请信息
-        Map<String, String> result = new HashMap<>();
-        result.put("bindToken", userId + "_" + System.currentTimeMillis());
-        result.put("inviteUrl", "/pages/bind/accept?token=" + result.get("bindToken"));
-        
-        return Result.success(result);
-    }
-
-    /**
-     * 获取绑定的家庭列表
-     */
-    @Operation(summary = "获取绑定的家庭列表")
-    @PostMapping("/bound-families")
-    public Result<List<Map<String, Object>>> getBoundFamilies(@RequestAttribute("userId") Long userId) {
-        List<GuideFamily> bindings = guideFamilyService.getFamiliesByGuide(userId);
-        
-        List<Map<String, Object>> result = bindings.stream().map(binding -> {
-            Map<String, Object> item = new HashMap<>();
-            item.put("id", binding.getId());
-            item.put("familyId", binding.getFamilyId());
-            item.put("serviceType", binding.getServiceType());
-            item.put("servicePrice", binding.getServicePrice());
-            item.put("status", binding.getStatus());
-            item.put("boundAt", binding.getBoundAt());
-            
-            // 获取家庭信息
-            Family family = familyMapper.selectById(binding.getFamilyId());
-            if (family != null) {
-                item.put("familyName", family.getName());
-            }
-            
-            return item;
-        }).collect(Collectors.toList());
-        
-        return Result.success(result);
-    }
-
-    /**
-     * 确认绑定家庭
-     */
-    @Operation(summary = "确认绑定家庭")
-    @PostMapping("/bind/confirm")
-    public Result<Boolean> confirmBind(@RequestAttribute("userId") Long userId,
-                                        @RequestBody BindGuideDTO dto) {
-        // 这里简化处理,实际应该通过 bindToken 解析
-        // 暂时使用 familyId
-        boolean success = guideFamilyService.confirmBind(userId, dto.getFamilyId(), 
-            dto.getServiceType(), dto.getServicePrice());
-        return Result.success(success);
-    }
-
-    /**
-     * 解除绑定
-     */
-    @Operation(summary = "解除绑定")
-    @DeleteMapping("/bind/{id}")
-    public Result<Boolean> unbind(@PathVariable Long id) {
-        boolean success = guideFamilyService.unbind(id);
-        return Result.success(success);
+    @GetMapping("/packages")
+    public Result<List<GuidePackage>> getPackages() {
+        return Result.success(guidePackageService.getActivePackages());
     }
 
-    /**
-     * 成长规划师绑定家庭(设置家庭关联的成长规划师)
-     */
-    @Operation(summary = "成长规划师绑定家庭")
-    @PostMapping("/bind/family/{familyId}")
-    public Result<Boolean> bindFamily(@RequestAttribute("userId") Long teacherId,
-                                       @RequestAttribute("role") String role,
-                                       @PathVariable Long familyId) {
-        if (!"teacher".equals(role)) {
-            return Result.error("只有成长规划师可以执行此操作");
-        }
-        
-        Family family = familyMapper.selectById(familyId);
-        if (family == null) {
-            return Result.error("家庭不存在");
-        }
-        
-        // 更新家庭的成长规划师ID
-        family.setTeacherId(teacherId);
-        family.setUpdatedAt(new java.util.Date());
-        familyMapper.updateById(family);
-        
-        // 同时更新成长规划师的 teacherFamilyIds
-        User teacher = userMapper.selectById(teacherId);
-        if (teacher != null) {
-            String currentIds = teacher.getTeacherFamilyIds();
-            String newFamilyId = String.valueOf(familyId);
-            if (currentIds == null || !currentIds.contains(newFamilyId)) {
-                String updatedIds = currentIds == null ? newFamilyId : currentIds + "," + newFamilyId;
-                teacher.setTeacherFamilyIds(updatedIds);
-                teacher.setUpdatedAt(new java.util.Date());
-                userMapper.updateById(teacher);
-            }
-        }
-        
-        return Result.success(true);
+    @GetMapping("/packages/{level}")
+    public Result<List<GuidePackage>> getPackagesByLevel(@PathVariable String level) {
+        return Result.success(guidePackageService.getPackagesByLevel(level));
     }
 
-    /**
-     * 成长规划师获取家庭邀请码(用于分享给家长)
-     */
-    @Operation(summary = "获取家庭邀请码")
-    @PostMapping("/family/invite-code/{familyId}")
-    public Result<String> getFamilyInviteCode(@RequestAttribute("userId") Long teacherId,
-                                               @RequestAttribute("role") String role,
-                                               @PathVariable Long familyId) {
-        if (!"teacher".equals(role)) {
-            return Result.error("只有成长规划师可以执行此操作");
-        }
-        
-        Family family = familyMapper.selectById(familyId);
-        if (family == null) {
-            return Result.error("家庭不存在");
-        }
-        
-        // 检查家庭是否已绑定该成长规划师
-        if (family.getTeacherId() != null && !family.getTeacherId().equals(teacherId)) {
-            return Result.error("该家庭已绑定其他成长规划师");
-        }
-        
-        // 如果家庭没有邀请码,生成一个
-        if (family.getInviteCode() == null || family.getInviteCode().isEmpty()) {
-            family.setInviteCode(generateInviteCode());
-            family.setUpdatedAt(new java.util.Date());
-            familyMapper.updateById(family);
-        }
-        
-        return Result.success(family.getInviteCode());
+    @PostMapping("/apply/new")
+    public Result<Boolean> applyNew(@RequestParam Long userId,
+                                    @RequestParam String targetLevel,
+                                    @RequestParam Long packageId,
+                                    @RequestParam String paymentProof) {
+        return Result.success(guideApplicationService.applyNew(userId, targetLevel, packageId, paymentProof));
     }
 
-    private String generateInviteCode() {
-        return UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase();
+    @PostMapping("/apply/upgrade")
+    public Result<Boolean> applyUpgrade(@RequestParam Long userId,
+                                        @RequestParam String fromLevel,
+                                        @RequestParam String toLevel,
+                                        @RequestParam String paymentProof) {
+        return Result.success(guideApplicationService.applyUpgrade(userId, fromLevel, toLevel, paymentProof));
     }
 
-    @Operation(summary = "获取套餐模板列表")
-    @GetMapping("/package-templates")
-    public Result<List<GuidePackageTemplate>> getPackageTemplates() {
-        List<GuidePackageTemplate> templates = templateService.getActiveTemplates();
-        return Result.success(templates);
+    @GetMapping("/my")
+    public Result<Guide> getMyGuide(@RequestParam Long userId) {
+        return Result.success(guideService.getByUserId(userId));
     }
 
-    @Operation(summary = "获取套餐模板详情")
-    @GetMapping("/package-templates/{id}")
-    public Result<GuidePackageTemplate> getPackageTemplate(@PathVariable Long id) {
-        GuidePackageTemplate template = templateService.getTemplateById(id);
-        return template != null ? Result.success(template) : Result.error("模板不存在");
+    @GetMapping("/applications/my")
+    public Result<List<GuideApplication>> getMyApplications(@RequestParam Long userId) {
+        LambdaQueryWrapper<GuideApplication> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(GuideApplication::getUserId, userId)
+               .orderByDesc(GuideApplication::getCreatedAt);
+        return Result.success(guideApplicationService.list(wrapper));
     }
 
-    
-
-    
-
-    @Operation(summary = "获取所有服务内容")
-    @GetMapping("/services")
-    public Result<List<ServiceContent>> getServices(
-            @RequestParam(required = false) Long typeId) {
-        List<ServiceContent> services;
-        if (typeId != null) {
-            services = serviceContentService.getContentsByType(typeId);
-        } else {
-            services = serviceContentService.getActiveContents();
+    @GetMapping("/my/packages")
+    public Result<List<GuidePackage>> getMyPackages(@RequestParam Long userId) {
+        Guide guide = guideService.getByUserId(userId);
+        if (guide == null) {
+            return Result.success(null);
         }
-        return Result.success(services);
+        return Result.success(guidePackageService.getPackagesByLevel(guide.getLevel()));
     }
 
-    @Operation(summary = "获取团队统计")
-    @GetMapping("/team/stats")
-    public Result<Map<String, Object>> getTeamStats(@RequestAttribute("userId") Long userId) {
-        Map<String, Object> stats = new HashMap<>();
-        stats.put("totalSales", new BigDecimal("8000.00"));
-        stats.put("myCommission", new BigDecimal("800.00"));
-        stats.put("familyCount", 5);
-        stats.put("memberCount", 2);
-        return Result.success(stats);
+    @GetMapping("/package/templates")
+    public Result<List<GuidePackage>> getPackageTemplates() {
+        return Result.success(guidePackageService.getActivePackages());
     }
-}
+}

+ 1 - 1
zxyj-backend/src/main/java/com/zxyj/controller/guide/GuideTeamController.java

@@ -55,7 +55,7 @@ public class GuideTeamController {
     }
 
     @Operation(summary = "生成新的邀请码")
-    @PostMapping("/invite-code")
+    @PostMapping("/invite-code/generate")
     public Result<GuideInviteCode> generateInviteCode(@RequestAttribute("userId") Long guideId) {
         GuideInviteCode inviteCode = guideInviteCodeService.createInviteCode(guideId);
         return Result.success(inviteCode);

+ 33 - 0
zxyj-backend/src/main/java/com/zxyj/entity/Guide.java

@@ -0,0 +1,33 @@
+package com.zxyj.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("guides")
+public class Guide implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long userId;
+
+    private String name;
+
+    private String phone;
+
+    private String level;
+
+    private String status;
+
+    private Date certifiedAt;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+}

+ 44 - 0
zxyj-backend/src/main/java/com/zxyj/entity/GuideApplication.java

@@ -0,0 +1,44 @@
+package com.zxyj.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("guide_applications")
+public class GuideApplication implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long userId;
+
+    private String type;
+
+    private String targetLevel;
+
+    private String currentLevel;
+
+    private Long packageId;
+
+    private String paymentProof;
+
+    private BigDecimal paymentAmount;
+
+    private String status;
+
+    private String rejectReason;
+
+    private Date reviewedAt;
+
+    private Long reviewedBy;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+}

+ 29 - 0
zxyj-backend/src/main/java/com/zxyj/entity/GuideFamilyRelation.java

@@ -0,0 +1,29 @@
+package com.zxyj.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+@TableName("guide_family_relations")
+public class GuideFamilyRelation implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long guideId;
+
+    private Long familyId;
+
+    private String relationType;
+
+    private String status;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+}

+ 8 - 11
zxyj-backend/src/main/java/com/zxyj/entity/GuidePackage.java

@@ -9,9 +9,6 @@ import java.io.Serializable;
 import java.math.BigDecimal;
 import java.util.Date;
 
-/**
- * 成长规划师套餐
- */
 @Data
 @TableName("guide_packages")
 public class GuidePackage implements Serializable {
@@ -19,21 +16,21 @@ public class GuidePackage implements Serializable {
     @TableId(type = IdType.AUTO)
     private Long id;
 
-    private Long guideId;        // 成长规划师ID
+    private String name;
 
-    private Long packageId;      // 套餐ID(关联task_template_packages)
+    private String level;
 
-    private String packageName;  // 套餐名称
+    private String description;
 
-    private BigDecimal price;    // 成长规划师定价
+    private BigDecimal price;
 
-    private BigDecimal commissionRate; // 佣金比例(0.1表示10%)
+    private Integer validityDays;
 
-    private String status;       // active/inactive
+    private String status;
 
-    private Integer salesCount; // 销量
+    private Integer sortOrder;
 
     private Date createdAt;
 
     private Date updatedAt;
-}
+}

+ 38 - 0
zxyj-backend/src/main/java/com/zxyj/entity/UpgradeService.java

@@ -0,0 +1,38 @@
+package com.zxyj.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@TableName("upgrade_services")
+public class UpgradeService implements Serializable {
+
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    private Long guideId;
+
+    private String fromLevel;
+
+    private String toLevel;
+
+    private BigDecimal serviceFee;
+
+    private String status;
+
+    private Long reviewedBy;
+
+    private Date reviewedAt;
+
+    private String rejectReason;
+
+    private Date createdAt;
+
+    private Date updatedAt;
+}

+ 9 - 0
zxyj-backend/src/main/java/com/zxyj/mapper/GuideApplicationMapper.java

@@ -0,0 +1,9 @@
+package com.zxyj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zxyj.entity.GuideApplication;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface GuideApplicationMapper extends BaseMapper<GuideApplication> {
+}

+ 9 - 0
zxyj-backend/src/main/java/com/zxyj/mapper/GuideFamilyRelationMapper.java

@@ -0,0 +1,9 @@
+package com.zxyj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zxyj.entity.GuideFamilyRelation;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface GuideFamilyRelationMapper extends BaseMapper<GuideFamilyRelation> {
+}

+ 9 - 0
zxyj-backend/src/main/java/com/zxyj/mapper/GuideMapper.java

@@ -0,0 +1,9 @@
+package com.zxyj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zxyj.entity.Guide;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface GuideMapper extends BaseMapper<Guide> {
+}

+ 9 - 0
zxyj-backend/src/main/java/com/zxyj/mapper/UpgradeServiceMapper.java

@@ -0,0 +1,9 @@
+package com.zxyj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.zxyj.entity.UpgradeService;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface UpgradeServiceMapper extends BaseMapper<UpgradeService> {
+}

+ 91 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuideApplicationService.java

@@ -0,0 +1,91 @@
+package com.zxyj.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zxyj.entity.Guide;
+import com.zxyj.entity.GuideApplication;
+import com.zxyj.mapper.GuideApplicationMapper;
+import com.zxyj.mapper.GuideMapper;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Date;
+import java.util.List;
+
+@Service
+public class GuideApplicationService extends ServiceImpl<GuideApplicationMapper, GuideApplication> {
+
+    public boolean applyNew(Long userId, String targetLevel, Long packageId, String paymentProof) {
+        GuideApplication application = new GuideApplication();
+        application.setUserId(userId);
+        application.setType("new_apply");
+        application.setTargetLevel(targetLevel);
+        application.setPackageId(packageId);
+        application.setPaymentProof(paymentProof);
+        application.setStatus("pending");
+        application.setCreatedAt(new Date());
+        return this.save(application);
+    }
+
+    public boolean applyUpgrade(Long userId, String fromLevel, String toLevel, String paymentProof) {
+        GuideApplication application = new GuideApplication();
+        application.setUserId(userId);
+        application.setType("upgrade");
+        application.setCurrentLevel(fromLevel);
+        application.setTargetLevel(toLevel);
+        application.setPaymentProof(paymentProof);
+        application.setStatus("pending");
+        application.setCreatedAt(new Date());
+        return this.save(application);
+    }
+
+    public List<GuideApplication> getPendingApplications() {
+        LambdaQueryWrapper<GuideApplication> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(GuideApplication::getStatus, "pending")
+               .orderByAsc(GuideApplication::getCreatedAt);
+        return this.list(wrapper);
+    }
+
+    @Transactional
+    public boolean approveApplication(Long applicationId, Long reviewedBy) {
+        GuideApplication application = this.getById(applicationId);
+        if (application == null) return false;
+
+        application.setStatus("approved");
+        application.setReviewedAt(new Date());
+        application.setReviewedBy(reviewedBy);
+        this.updateById(application);
+
+        if ("new_apply".equals(application.getType())) {
+            Guide guide = new Guide();
+            guide.setUserId(application.getUserId());
+            guide.setLevel(application.getTargetLevel());
+            guide.setStatus("active");
+            guide.setCertifiedAt(new Date());
+            guide.setCreatedAt(new Date());
+            guide.setUpdatedAt(new Date());
+            return baseMapper.insert(guide) > 0;
+        } else {
+            GuideMapper guideMapper = new GuideMapper();
+            Guide guide = guideMapper.selectOne(new LambdaQueryWrapper<Guide>()
+                .eq(Guide::getUserId, application.getUserId()));
+            if (guide != null) {
+                guide.setLevel(application.getTargetLevel());
+                guide.setUpdatedAt(new Date());
+                return guideMapper.updateById(guide) > 0;
+            }
+        }
+        return false;
+    }
+
+    public boolean rejectApplication(Long applicationId, Long reviewedBy, String reason) {
+        GuideApplication application = this.getById(applicationId);
+        if (application == null) return false;
+
+        application.setStatus("rejected");
+        application.setReviewedAt(new Date());
+        application.setReviewedBy(reviewedBy);
+        application.setRejectReason(reason);
+        return this.updateById(application);
+    }
+}

+ 23 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuideFamilyRelationService.java

@@ -0,0 +1,23 @@
+package com.zxyj.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zxyj.entity.GuideFamilyRelation;
+import com.zxyj.mapper.GuideFamilyRelationMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class GuideFamilyRelationService extends ServiceImpl<GuideFamilyRelationMapper, GuideFamilyRelation> {
+
+    public List<GuideFamilyRelation> getByGuideId(Long guideId) {
+        return this.list(new LambdaQueryWrapper<GuideFamilyRelation>()
+            .eq(GuideFamilyRelation::getGuideId, guideId));
+    }
+
+    public List<GuideFamilyRelation> getByFamilyId(Long familyId) {
+        return this.list(new LambdaQueryWrapper<GuideFamilyRelation>()
+            .eq(GuideFamilyRelation::getFamilyId, familyId));
+    }
+}

+ 28 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuideService.java

@@ -0,0 +1,28 @@
+package com.zxyj.service;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.zxyj.entity.Guide;
+import com.zxyj.mapper.GuideMapper;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class GuideService extends ServiceImpl<GuideMapper, Guide> {
+
+    public Guide getByUserId(Long userId) {
+        return this.getOne(new LambdaQueryWrapper<Guide>().eq(Guide::getUserId, userId));
+    }
+
+    public List<Guide> getByLevel(String level) {
+        return this.list(new LambdaQueryWrapper<Guide>()
+            .eq(Guide::getLevel, level)
+            .eq(Guide::getStatus, "active"));
+    }
+
+    public List<Guide> getActiveGuides() {
+        return this.list(new LambdaQueryWrapper<Guide>()
+            .eq(Guide::getStatus, "active"));
+    }
+}

+ 101 - 1
zxyj-backend/src/main/resources/schema_guide.sql

@@ -1,4 +1,104 @@
--- 成长规划师邀请码表
+-- =====================================================
+-- 成长规划师注册与升级系统数据库表
+-- version: 1.1
+-- date: 2026-05-05
+-- =====================================================
+
+-- 成长规划师申请表(注册/升级申请)
+CREATE TABLE IF NOT EXISTS `guide_applications` (
+    `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
+    `user_id` BIGINT NOT NULL COMMENT '申请人用户ID',
+    `type` VARCHAR(20) NOT NULL DEFAULT 'new_apply' COMMENT '申请类型: new_apply-新注册, upgrade-升级',
+    `target_level` VARCHAR(20) NOT NULL COMMENT '目标等级: junior-初级, intermediate-中级, senior-高级',
+    `current_level` VARCHAR(20) DEFAULT NULL COMMENT '当前等级(升级时填写)',
+    `package_id` BIGINT COMMENT '申请的套餐ID',
+    `payment_proof` VARCHAR(500) COMMENT '付款凭证URL',
+    `payment_amount` DECIMAL(10,2) COMMENT '付款金额',
+    `status` VARCHAR(20) NOT NULL DEFAULT 'pending' COMMENT '状态: pending-待审核, approved-已通过, rejected-已拒绝',
+    `reject_reason` VARCHAR(500) COMMENT '拒绝原因',
+    `reviewed_at` DATETIME COMMENT '审核时间',
+    `reviewed_by` BIGINT COMMENT '审核人ID',
+    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    INDEX `idx_user_id` (`user_id`),
+    INDEX `idx_status` (`status`),
+    INDEX `idx_type` (`type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成长规划师申请表';
+
+-- 成长规划师表(已认证的规划师)
+CREATE TABLE IF NOT EXISTS `guides` (
+    `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
+    `user_id` BIGINT NOT NULL UNIQUE COMMENT '用户ID(唯一)',
+    `level` VARCHAR(20) NOT NULL DEFAULT 'junior' COMMENT '等级: junior-初级, intermediate-中级, senior-高级',
+    `status` VARCHAR(20) NOT NULL DEFAULT 'active' COMMENT '状态: active-活跃, inactive-停用',
+    `service_count` INT DEFAULT 0 COMMENT '当前服务家庭数',
+    `package_id` BIGINT COMMENT '购买套餐ID',
+    `nickname` VARCHAR(100) COMMENT '规划师昵称',
+    `phone` VARCHAR(20) COMMENT '联系电话',
+    `intro` TEXT COMMENT '个人介绍',
+    `avatar` VARCHAR(500) COMMENT '头像URL',
+    `commission_rate` DECIMAL(5,4) DEFAULT 0.1000 COMMENT '佣金比例',
+    `approved_at` DATETIME COMMENT '审核通过时间',
+    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    INDEX `idx_user_id` (`user_id`),
+    INDEX `idx_level` (`level`),
+    INDEX `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='成长规划师表';
+
+-- 升级服务表(可供购买的升级服务)
+CREATE TABLE IF NOT EXISTS `upgrade_services` (
+    `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
+    `from_level` VARCHAR(20) NOT NULL COMMENT '原等级: junior, intermediate, senior',
+    `to_level` VARCHAR(20) NOT NULL COMMENT '目标等级: junior, intermediate, senior',
+    `name` VARCHAR(100) NOT NULL COMMENT '服务名称',
+    `description` TEXT COMMENT '描述',
+    `price` DECIMAL(10,2) NOT NULL COMMENT '价格',
+    `service_months` INT DEFAULT 12 COMMENT '服务期限(月)',
+    `service_limit_add` INT DEFAULT 0 COMMENT '额外增加的服务名额',
+    `is_active` TINYINT DEFAULT 1 COMMENT '是否上架: 0-否, 1-是',
+    `sort_order` INT DEFAULT 0 COMMENT '排序',
+    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    UNIQUE KEY `uk_from_to` (`from_level`, `to_level`),
+    INDEX `idx_from_level` (`from_level`),
+    INDEX `idx_is_active` (`is_active`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='升级服务表';
+
+-- 规划师套餐表(管理员配置的套餐模板)
+CREATE TABLE IF NOT EXISTS `guide_package_templates` (
+    `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
+    `name` VARCHAR(100) NOT NULL COMMENT '套餐名称',
+    `level` VARCHAR(20) NOT NULL COMMENT '等级: junior, intermediate, senior',
+    `price` DECIMAL(10,2) NOT NULL COMMENT '价格',
+    `min_price` DECIMAL(10,2) COMMENT '最低价',
+    `description` TEXT COMMENT '套餐描述',
+    `service_months` INT DEFAULT 12 COMMENT '服务期限(月)',
+    `service_limit` INT DEFAULT 50 COMMENT '服务名额限制',
+    `features` JSON COMMENT '功能权限列表',
+    `is_active` TINYINT DEFAULT 1 COMMENT '是否上架: 0-否, 1-是',
+    `sort_order` INT DEFAULT 0 COMMENT '排序',
+    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    INDEX `idx_level` (`level`),
+    INDEX `idx_is_active` (`is_active`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='规划师套餐模板表';
+
+-- 规划师-家庭关联表
+CREATE TABLE IF NOT EXISTS `guide_family_relations` (
+    `id` BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
+    `guide_id` BIGINT NOT NULL COMMENT '规划师ID',
+    `family_id` BIGINT NOT NULL COMMENT '家庭ID',
+    `service_type` VARCHAR(50) DEFAULT 'basic' COMMENT '服务类型: basic-基础, premium-高级',
+    `service_price` DECIMAL(10,2) DEFAULT 0.00 COMMENT '服务价格',
+    `status` VARCHAR(20) DEFAULT 'active' COMMENT '状态: active-服务中, paused-已暂停',
+    `bound_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '绑定时间',
+    `created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    UNIQUE KEY `uk_guide_family` (`guide_id`, `family_id`),
+    INDEX `idx_guide_id` (`guide_id`),
+    INDEX `idx_family_id` (`family_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='规划师-家庭关联表';
 CREATE TABLE IF NOT EXISTS `guide_invite_codes` (
   `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
   `guide_id` BIGINT NOT NULL COMMENT '成长规划师ID',

+ 30 - 0
zxyj-frontend/pages.json

@@ -67,6 +67,36 @@
         "navigationBarTitleText": "成长规划师中心"
       }
     },
+    {
+      "path": "pages/teacher/families",
+      "style": {
+        "navigationBarTitleText": "我的家庭"
+      }
+    },
+    {
+      "path": "pages/teacher/team",
+      "style": {
+        "navigationBarTitleText": "我的团队"
+      }
+    },
+    {
+      "path": "pages/teacher/messages",
+      "style": {
+        "navigationBarTitleText": "消息中心"
+      }
+    },
+    {
+      "path": "pages/teacher/orders",
+      "style": {
+        "navigationBarTitleText": "订单与佣金"
+      }
+    },
+    {
+      "path": "pages/teacher/chat",
+      "style": {
+        "navigationBarTitleText": "聊天"
+      }
+    },
     {
       "path": "pages/membership/index",
       "style": {

+ 116 - 35
zxyj-web/src/api/guide.js

@@ -1,104 +1,185 @@
 import request from '@/utils/request'
 
-export function getPackageTemplates() {
+export function getGuidePackages() {
   return request({
-    url: '/api/guide/package-templates',
+    url: '/api/guide/packages',
     method: 'get'
   })
 }
 
-export function getMyPackages() {
+export function getGuidePackagesByLevel(level) {
   return request({
-    url: '/api/guide/packages',
+    url: `/api/guide/packages/${level}`,
     method: 'get'
   })
 }
 
-export function createPackage(data) {
+export function applyNewGuide(data) {
   return request({
-    url: '/api/guide/packages',
+    url: '/api/guide/apply/new',
     method: 'post',
     data
   })
 }
 
-export function updatePackage(id, data) {
+export function applyUpgradeGuide(data) {
   return request({
-    url: `/api/guide/packages/${id}`,
-    method: 'put',
+    url: '/api/guide/apply/upgrade',
+    method: 'post',
     data
   })
 }
 
-export function updatePackageStatus(id, status) {
+export function getMyGuide(userId) {
   return request({
-    url: `/api/guide/packages/${id}/status`,
-    method: 'put',
-    params: { status }
+    url: '/api/guide/my',
+    method: 'get',
+    params: { userId }
   })
 }
 
-export function deletePackage(id) {
+export function getMyApplications(userId) {
   return request({
-    url: `/api/guide/packages/${id}`,
-    method: 'delete'
+    url: '/api/guide/applications/my',
+    method: 'get',
+    params: { userId }
   })
 }
 
-export function getServices(typeId) {
+export function getPendingApplications() {
   return request({
-    url: '/api/guide/services',
-    method: 'get',
-    params: { typeId }
+    url: '/api/admin/guide/applications/pending',
+    method: 'get'
+  })
+}
+
+export function approveApplication(id, adminId) {
+  return request({
+    url: `/api/admin/guide/applications/${id}/approve`,
+    method: 'post',
+    data: { adminId }
   })
 }
 
-export function getBoundFamilies() {
+export function rejectApplication(id, adminId, reason) {
   return request({
-    url: '/api/guide/bound-families',
-    method: 'post'
+    url: `/api/admin/guide/applications/${id}/reject`,
+    method: 'post',
+    data: { adminId, reason }
   })
 }
 
-export function getTeamMembers() {
+export function getAllGuides() {
   return request({
-    url: '/api/guide/team',
+    url: '/api/admin/guide/guides',
     method: 'get'
   })
 }
 
-export function getTeamStats() {
+export function getGuidesByLevel(level) {
   return request({
-    url: '/api/guide/team/stats',
+    url: `/api/admin/guide/guides/level/${level}`,
     method: 'get'
   })
 }
 
-export function getGuideMessages() {
+export function getAllPackages() {
   return request({
-    url: '/api/guide/messages',
+    url: '/api/admin/guide/packages',
     method: 'get'
   })
 }
 
-export function sendMessage(data) {
+export function createPackage(data) {
   return request({
-    url: '/api/guide/messages',
+    url: '/api/admin/guide/packages',
     method: 'post',
     data
   })
 }
 
-export function getGuideOrders() {
+export function updatePackage(id, data) {
   return request({
-    url: '/api/guide/orders',
+    url: `/api/admin/guide/packages/${id}`,
+    method: 'put',
+    data
+  })
+}
+
+export function updatePackageStatus(id, status) {
+  return request({
+    url: `/api/admin/guide/packages/${id}/status`,
+    method: 'put',
+    data: { status }
+  })
+}
+
+export function getMyPackages() {
+  return request({
+    url: '/api/guide/my/packages',
+    method: 'get'
+  })
+}
+
+export function getPackageTemplates() {
+  return request({
+    url: '/api/guide/package/templates',
     method: 'get'
   })
 }
 
-export function getOrderStats() {
+export function getBoundFamilies(params) {
+  return request({
+    url: '/api/guide/families/bound',
+    method: 'get',
+    params
+  })
+}
+
+export function getTeamMembers(params) {
+  return request({
+    url: '/api/guide/team/members',
+    method: 'get',
+    params
+  })
+}
+
+export function getTeamStats(params) {
+  return request({
+    url: '/api/guide/team/stats',
+    method: 'get',
+    params
+  })
+}
+
+export function getGuideOrders(params) {
+  return request({
+    url: '/api/guide/orders',
+    method: 'get',
+    params
+  })
+}
+
+export function getOrderStats(params) {
   return request({
     url: '/api/guide/orders/stats',
-    method: 'get'
+    method: 'get',
+    params
+  })
+}
+
+export function getGuideMessages(params) {
+  return request({
+    url: '/api/guide/messages',
+    method: 'get',
+    params
+  })
+}
+
+export function sendMessage(data) {
+  return request({
+    url: '/api/guide/messages/send',
+    method: 'post',
+    data
   })
 }

+ 5 - 0
zxyj-web/src/views/Layout.vue

@@ -140,6 +140,11 @@ export default {
     }
   },
   methods: {
+    handleMenuSelect(index) {
+      if (index && index.startsWith('/')) {
+        this.$router.push(index)
+      }
+    },
     handleLogout() {
       this.$confirm('确定要退出登录吗?', '提示', {
         confirmButtonText: '确定',

+ 1 - 1
zxyj-web/src/views/teacher/TeacherAssessment.vue

@@ -84,7 +84,7 @@
 
 <script>
 import { getGuideFamilyAssessmentList, enableFamilyAssessment, disableFamilyAssessment, getGuideAssessmentMaterials } from '@/api/assessment'
-import { getFamilyList } from '@/api/guide'
+import { getFamilyList } from '@/api/admin'
 
 export default {
   name: 'TeacherAssessment',

+ 283 - 90
zxyj-web/src/views/teacher/TeacherMessages.vue

@@ -1,37 +1,65 @@
 <template>
   <div class="teacher-messages">
-    <el-card>
-      <div slot="header">
-        <span>消息中心</span>
-        <el-button style="float: right" size="small" type="text" @click="markAllRead">全部标记已读</el-button>
+    <!-- 页面标题区 -->
+    <div class="page-header">
+      <div class="header-left">
+        <h2 class="page-title">消息中心</h2>
+        <span class="page-desc">与家长的沟通渠道</span>
       </div>
+      <div class="header-actions">
+        <el-button class="mark-read-btn" @click="markAllRead">
+          <i class="el-icon-check"></i> 全部已读
+        </el-button>
+      </div>
+    </div>
+
+    <!-- 消息列表卡片 -->
+    <el-card class="messages-card">
       <div class="message-container">
-        <div class="message-list">
-          <div
-            class="message-item"
-            :class="{ active: activeConversation === msg.conversationId, unread: msg.unreadCount > 0 }"
-            v-for="msg in messages"
-            :key="msg.id"
-            @click="selectConversation(msg)"
-          >
-            <div class="message-avatar">
-              <img :src="msg.avatar || '/static/default-avatar.png'" alt="">
-              <span class="unread-dot" v-if="msg.unreadCount > 0"></span>
-            </div>
-            <div class="message-content">
-              <div class="message-header">
-                <span class="sender-name">{{ msg.senderName }}</span>
-                <span class="message-time">{{ msg.time }}</span>
+        <!-- 会话列表 -->
+        <div class="conversation-list">
+          <div class="list-header">
+            <span class="list-title">会话列表</span>
+            <span class="unread-total" v-if="totalUnread > 0">{{ totalUnread }}条未读</span>
+          </div>
+          <div class="conversations">
+            <div
+              class="conversation-item"
+              :class="{ active: activeConversation === msg.conversationId, unread: msg.unreadCount > 0 }"
+              v-for="msg in messages"
+              :key="msg.id"
+              @click="selectConversation(msg)"
+            >
+              <div class="conversation-avatar">
+                <i class="el-icon-user-solid"></i>
+                <span class="unread-badge" v-if="msg.unreadCount > 0">{{ msg.unreadCount }}</span>
+              </div>
+              <div class="conversation-info">
+                <div class="conversation-header">
+                  <span class="sender-name">{{ msg.senderName }}</span>
+                  <span class="message-time">{{ msg.time }}</span>
+                </div>
+                <div class="conversation-preview">{{ msg.preview }}</div>
               </div>
-              <div class="message-preview">{{ msg.preview }}</div>
+            </div>
+            <div v-if="messages.length === 0" class="empty-conversations">
+              <i class="el-icon-chat-dot-round"></i>
+              <p>暂无消息</p>
             </div>
           </div>
         </div>
+
+        <!-- 聊天区域 -->
         <div class="chat-area">
           <div class="chat-header" v-if="activeConversation">
-            <span>{{ currentChatName }}</span>
+            <div class="chat-title">
+              <i class="el-icon-user"></i>
+              {{ currentChatName }}
+            </div>
+            <el-button type="text" @click="viewFamilyInfo">查看家庭信息</el-button>
           </div>
-          <div class="chat-messages" ref="chatMessages">
+
+          <div class="chat-messages" ref="chatMessages" v-if="activeConversation">
             <div
               class="chat-message"
               :class="{ mine: msg.isMine }"
@@ -41,21 +69,27 @@
               <div class="message-bubble">
                 {{ msg.content }}
               </div>
-              <div class="message-time">{{ msg.time }}</div>
+              <div class="message-meta">{{ msg.time }}</div>
+            </div>
+          </div>
+
+          <div class="empty-chat" v-if="!activeConversation">
+            <div class="empty-content">
+              <i class="el-icon-chat-dot-square"></i>
+              <p>选择一个会话开始聊天</p>
             </div>
           </div>
+
           <div class="chat-input" v-if="activeConversation">
             <el-input
               v-model="newMessage"
               placeholder="输入消息..."
-              @keyup.enter="sendMessage"
+              class="message-input"
+              @keyup.enter.native="sendMessage"
             >
-              <el-button slot="append" @click="sendMessage">发送</el-button>
+              <el-button slot="append" type="primary" @click="sendMessage">发送</el-button>
             </el-input>
           </div>
-          <div class="empty-chat" v-if="!activeConversation">
-            <span>请选择会话开始聊天</span>
-          </div>
         </div>
       </div>
     </el-card>
@@ -76,6 +110,11 @@ export default {
       newMessage: ''
     }
   },
+  computed: {
+    totalUnread() {
+      return this.messages.reduce((sum, msg) => sum + (msg.unreadCount || 0), 0)
+    }
+  },
   created() {
     this.loadMessages()
   },
@@ -89,15 +128,16 @@ export default {
       } catch (e) {
         console.error(e)
         this.messages = [
-          { id: 1, conversationId: 'family_123', senderName: '张三爸', avatar: '', preview: '老师,孩子今天的任务完成了', time: '2024-01-15 10:20', unreadCount: 1 },
-          { id: 2, conversationId: 'family_124', senderName: '李四妈', avatar: '', preview: '请问这个套餐怎么购买?', time: '2024-01-14 16:45', unreadCount: 0 },
-          { id: 3, conversationId: 'system', senderName: '系统通知', avatar: '', preview: '您的套餐"年度旗舰版"已到期', time: '2024-01-14 14:30', unreadCount: 0 }
+          { id: 1, conversationId: 'family_123', senderName: '张三爸', avatar: '', preview: '老师,孩子今天的任务完成了', time: '10:20', unreadCount: 1 },
+          { id: 2, conversationId: 'family_124', senderName: '李四妈', avatar: '', preview: '请问这个套餐怎么购买?', time: '昨天', unreadCount: 0 },
+          { id: 3, conversationId: 'system', senderName: '系统通知', avatar: '', preview: '您的套餐"年度旗舰版"已到期', time: '14:30', unreadCount: 0 }
         ]
       }
     },
     selectConversation(msg) {
       this.activeConversation = msg.conversationId
       this.currentChatName = msg.senderName
+      msg.unreadCount = 0
       this.loadChatMessages(msg.conversationId)
     },
     async loadChatMessages(conversationId) {
@@ -109,8 +149,8 @@ export default {
         }
       } catch (e) {
         this.chatMessages = [
-          { content: '老师,孩子今天的任务完成了', time: '2024-01-15 10:20', isMine: false },
-          { content: '好的,我看一下', time: '2024-01-15 10:25', isMine: true }
+          { content: '老师,孩子今天的任务完成了', time: '10:20', isMine: false },
+          { content: '好的,我看一下', time: '10:25', isMine: true }
         ]
       }
     },
@@ -124,7 +164,7 @@ export default {
         })
         this.chatMessages.push({
           content: this.newMessage,
-          time: new Date().toLocaleString(),
+          time: new Date().toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' }),
           isMine: true
         })
         this.newMessage = ''
@@ -140,6 +180,9 @@ export default {
         msg.unreadCount = 0
       })
       this.$message.success('已全部标记为已读')
+    },
+    viewFamilyInfo() {
+      this.$message.info('查看家庭信息功能开发中')
     }
   }
 }
@@ -147,149 +190,299 @@ export default {
 
 <style scoped>
 .teacher-messages {
-  padding: 20px;
+  padding: 24px;
+  background: #F0F7FF;
+  min-height: calc(100vh - 120px);
+}
+
+/* 页面标题 */
+.page-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  margin-bottom: 24px;
 }
 
+.page-title {
+  font-size: 24px;
+  font-weight: 700;
+  color: #1E293B;
+  margin: 0 0 4px 0;
+}
+
+.page-desc {
+  font-size: 14px;
+  color: #64748B;
+}
+
+.mark-read-btn {
+  border: 1px solid #E2E8F0;
+  border-radius: 8px;
+  color: #64748B;
+}
+
+.mark-read-btn:hover {
+  border-color: #3B82F6;
+  color: #3B82F6;
+}
+
+/* 消息卡片 */
+.messages-card {
+  border-radius: 16px;
+  border: none;
+  box-shadow: 0 2px 12px rgba(59, 130, 246, 0.06);
+}
+
+/* 消息容器 */
 .message-container {
   display: flex;
-  height: 600px;
+  height: calc(100vh - 280px);
+  min-height: 500px;
+}
+
+/* 会话列表 */
+.conversation-list {
+  width: 320px;
+  border-right: 1px solid #F1F5F9;
+  display: flex;
+  flex-direction: column;
+}
+
+.list-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 12px 16px;
+  border-bottom: 1px solid #F1F5F9;
+}
+
+.list-title {
+  font-size: 14px;
+  font-weight: 600;
+  color: #1E293B;
+}
+
+.unread-total {
+  font-size: 12px;
+  color: #FFFFFF;
+  background: #EF4444;
+  padding: 2px 8px;
+  border-radius: 10px;
 }
 
-.message-list {
-  width: 300px;
-  border-right: 1px solid #eee;
+.conversations {
+  flex: 1;
   overflow-y: auto;
 }
 
-.message-item {
+.conversation-item {
   display: flex;
-  padding: 15px;
+  padding: 14px 16px;
   cursor: pointer;
-  border-bottom: 1px solid #f5f5f5;
+  border-bottom: 1px solid #F8FAFC;
+  transition: all 0.2s;
 }
 
-.message-item:hover {
-  background: #f9f9f9;
+.conversation-item:hover {
+  background: #F8FAFC;
 }
 
-.message-item.active {
-  background: #ecf5ff;
+.conversation-item.active {
+  background: #EBF5FF;
+  border-left: 3px solid #3B82F6;
 }
 
-.message-item.unread .sender-name {
-  font-weight: bold;
+.conversation-item.unread {
+  background: #FFFBEB;
 }
 
-.message-avatar {
+.conversation-item.unread.active {
+  background: #EBF5FF;
+}
+
+.conversation-avatar {
   position: relative;
-  width: 48px;
-  height: 48px;
+  width: 44px;
+  height: 44px;
+  background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  color: #FFFFFF;
   margin-right: 12px;
+  font-size: 18px;
   flex-shrink: 0;
 }
 
-.message-avatar img {
-  width: 100%;
-  height: 100%;
-  border-radius: 50%;
-}
-
-.unread-dot {
+.unread-badge {
   position: absolute;
-  top: 0;
-  right: 0;
-  width: 12px;
-  height: 12px;
-  background: #F56C6C;
-  border-radius: 50%;
-  border: 2px solid #fff;
+  top: -4px;
+  right: -4px;
+  min-width: 18px;
+  height: 18px;
+  background: #EF4444;
+  color: #FFFFFF;
+  border-radius: 9px;
+  font-size: 11px;
+  font-weight: 600;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  padding: 0 4px;
 }
 
-.message-content {
+.conversation-info {
   flex: 1;
   min-width: 0;
 }
 
-.message-header {
+.conversation-header {
   display: flex;
   justify-content: space-between;
+  align-items: center;
   margin-bottom: 4px;
 }
 
 .sender-name {
   font-size: 14px;
-  color: #333;
+  font-weight: 600;
+  color: #1E293B;
+}
+
+.conversation-item.unread .sender-name {
+  color: #1E293B;
 }
 
 .message-time {
   font-size: 12px;
-  color: #999;
+  color: #94A3B8;
 }
 
-.message-preview {
+.conversation-preview {
   font-size: 13px;
-  color: #666;
+  color: #64748B;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
 
+.conversation-item.unread .conversation-preview {
+  color: #1E293B;
+  font-weight: 500;
+}
+
+.empty-conversations {
+  text-align: center;
+  padding: 48px 0;
+  color: #94A3B8;
+}
+
+.empty-conversations i {
+  font-size: 48px;
+  margin-bottom: 12px;
+}
+
+/* 聊天区域 */
 .chat-area {
   flex: 1;
   display: flex;
   flex-direction: column;
+  min-width: 0;
 }
 
 .chat-header {
-  padding: 15px;
-  border-bottom: 1px solid #eee;
-  font-weight: bold;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 14px 20px;
+  border-bottom: 1px solid #F1F5F9;
+}
+
+.chat-title {
+  font-size: 15px;
+  font-weight: 600;
+  color: #1E293B;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.chat-title i {
+  color: #3B82F6;
 }
 
 .chat-messages {
   flex: 1;
-  padding: 15px;
+  padding: 20px;
   overflow-y: auto;
+  background: #F8FAFC;
 }
 
 .chat-message {
   display: flex;
   flex-direction: column;
-  margin-bottom: 15px;
+  margin-bottom: 16px;
+  max-width: 70%;
 }
 
 .chat-message.mine {
   align-items: flex-end;
+  margin-left: auto;
 }
 
 .message-bubble {
-  max-width: 70%;
-  padding: 10px 15px;
-  border-radius: 8px;
-  background: #f0f0f0;
+  padding: 12px 16px;
+  border-radius: 16px;
+  background: #FFFFFF;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
   word-wrap: break-word;
+  line-height: 1.5;
 }
 
 .chat-message.mine .message-bubble {
-  background: #409EFF;
-  color: #fff;
+  background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
+  color: #FFFFFF;
 }
 
-.chat-message .message-time {
+.message-meta {
   font-size: 11px;
+  color: #94A3B8;
   margin-top: 4px;
 }
 
-.chat-input {
-  padding: 15px;
-  border-top: 1px solid #eee;
-}
-
 .empty-chat {
   flex: 1;
   display: flex;
   align-items: center;
   justify-content: center;
-  color: #999;
+  background: #F8FAFC;
+}
+
+.empty-content {
+  text-align: center;
+  color: #94A3B8;
+}
+
+.empty-content i {
+  font-size: 64px;
+  margin-bottom: 12px;
+}
+
+.chat-input {
+  padding: 16px 20px;
+  border-top: 1px solid #F1F5F9;
+  background: #FFFFFF;
+}
+
+.message-input ::v-deep .el-input__inner {
+  border-radius: 20px 0 0 20px;
+}
+
+.message-input ::v-deep .el-button {
+  border-radius: 0 20px 20px 0;
+  background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
+  border: none;
+  color: #FFFFFF;
 }
 </style>