Explorar el Código

refactor: 为所有44个Service类添加Interface规范

- 创建44个Service接口文件于service/api/目录
- 所有Service实现类添加implements XxxServiceInterface
- MediaRecordService和StorageService(旧接口) extends对应新接口
- 修正接口方法签名与实现类一致:
  - TaskReminderServiceInterface: 基于Task实体(无TaskReminder实体)
  - PaymentServiceInterface: 基于PackageOrder实体(无Payment实体)
  - BindInviteServiceInterface: 基于GuideFamily(无BindInvite实体)
  - LoginAttemptServiceInterface: 内存实现(无LoginAttempt实体)
  - DataMigrationServiceInterface: 仅runMigration方法
  - PointsServiceInterface: 基于PointsLog实体
  - MembershipServiceInterface: 基于FamilyMembership实体
  - PackagePurchaseServiceInterface: 基于PackageOrder实体
  - PackagePaymentServiceInterface: 基于PackageOrder实体
  - AssessmentServiceInterface: 补充完整方法签名
- 新增TaskPlanServiceInterface(原缺失)
- mvn clean compile BUILD SUCCESS
User hace 4 meses
padre
commit
2135824804
Se han modificado 75 ficheros con 1341 adiciones y 12 borrados
  1. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/AssessmentAppointmentService.java
  2. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/AssessmentOrderService.java
  3. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/AssessmentPermissionService.java
  4. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/AssessmentService.java
  5. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/BindInviteService.java
  6. 727 0
      zxyj-backend/src/main/java/com/zxyj/service/DataMigrationService.java
  7. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/DatabaseSyncService.java
  8. 24 0
      zxyj-backend/src/main/java/com/zxyj/service/GrowthPlanService.java
  9. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GrowthRecordService.java
  10. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideApplicationService.java
  11. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideFamilyRelationService.java
  12. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideFamilyService.java
  13. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideHierarchyService.java
  14. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideInviteCodeService.java
  15. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/GuidePackageService.java
  16. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuidePackageTemplateService.java
  17. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/GuideService.java
  18. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/LoginAttemptService.java
  19. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/MediaRecordService.java
  20. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/MembershipService.java
  21. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/MiniGameService.java
  22. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/PackagePaymentService.java
  23. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/PackagePurchaseService.java
  24. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/PaymentService.java
  25. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/PointsService.java
  26. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/RewardWishlistService.java
  27. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/ServiceContentService.java
  28. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/ServiceTypeService.java
  29. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/StorageService.java
  30. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/StreetService.java
  31. 3 1
      zxyj-backend/src/main/java/com/zxyj/service/TaskPlanService.java
  32. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/TaskReminderService.java
  33. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/TaskTemplatePackageService.java
  34. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/TeacherMessageService.java
  35. 1 0
      zxyj-backend/src/main/java/com/zxyj/service/TeacherService.java
  36. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/UserOperationLogService.java
  37. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/WishService.java
  38. 2 0
      zxyj-backend/src/main/java/com/zxyj/service/ZkxzSyncService.java
  39. 14 0
      zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentAppointmentServiceInterface.java
  40. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentOrderServiceInterface.java
  41. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentPermissionServiceInterface.java
  42. 29 0
      zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentServiceInterface.java
  43. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/BindInviteServiceInterface.java
  44. 7 0
      zxyj-backend/src/main/java/com/zxyj/service/api/DataMigrationServiceInterface.java
  45. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/DatabaseSyncServiceInterface.java
  46. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GrowthPlanServiceInterface.java
  47. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GrowthRecordServiceInterface.java
  48. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuideApplicationServiceInterface.java
  49. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuideFamilyRelationServiceInterface.java
  50. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuideFamilyServiceInterface.java
  51. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuideHierarchyServiceInterface.java
  52. 11 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuideInviteCodeServiceInterface.java
  53. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuidePackageServiceInterface.java
  54. 14 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuidePackageTemplateServiceInterface.java
  55. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/GuideServiceInterface.java
  56. 9 0
      zxyj-backend/src/main/java/com/zxyj/service/api/LoginAttemptServiceInterface.java
  57. 34 0
      zxyj-backend/src/main/java/com/zxyj/service/api/MediaRecordServiceInterface.java
  58. 17 0
      zxyj-backend/src/main/java/com/zxyj/service/api/MembershipServiceInterface.java
  59. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/MiniGameServiceInterface.java
  60. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/PackagePaymentServiceInterface.java
  61. 14 0
      zxyj-backend/src/main/java/com/zxyj/service/api/PackagePurchaseServiceInterface.java
  62. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/PaymentServiceInterface.java
  63. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/PointsServiceInterface.java
  64. 11 0
      zxyj-backend/src/main/java/com/zxyj/service/api/RewardWishlistServiceInterface.java
  65. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/ServiceContentServiceInterface.java
  66. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/ServiceTypeServiceInterface.java
  67. 21 0
      zxyj-backend/src/main/java/com/zxyj/service/api/StorageServiceInterface.java
  68. 17 0
      zxyj-backend/src/main/java/com/zxyj/service/api/TaskPlanServiceInterface.java
  69. 18 0
      zxyj-backend/src/main/java/com/zxyj/service/api/TaskReminderServiceInterface.java
  70. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/TaskTemplatePackageServiceInterface.java
  71. 12 0
      zxyj-backend/src/main/java/com/zxyj/service/api/TeacherMessageServiceInterface.java
  72. 20 0
      zxyj-backend/src/main/java/com/zxyj/service/api/TeacherServiceInterface.java
  73. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/UserOperationLogServiceInterface.java
  74. 13 0
      zxyj-backend/src/main/java/com/zxyj/service/api/WishServiceInterface.java
  75. 10 0
      zxyj-backend/src/main/java/com/zxyj/service/api/ZkxzSyncServiceInterface.java

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/AssessmentAppointmentService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.AssessmentAppointmentServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.AssessmentAppointment;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/AssessmentOrderService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.AssessmentOrderServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.AssessmentOrder;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/AssessmentPermissionService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.AssessmentPermissionServiceInterface;
 import javax.annotation.Resource;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/AssessmentService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.AssessmentServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.AssessmentMaterial;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/BindInviteService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.BindInviteServiceInterface;
 import javax.annotation.Resource;
 
 import com.zxyj.entity.GuideFamily;

+ 727 - 0
zxyj-backend/src/main/java/com/zxyj/service/DataMigrationService.java

@@ -0,0 +1,727 @@
+package com.zxyj.service;
+
+import com.zxyj.service.api.DataMigrationServiceInterface;
+
+import com.zxyj.entity.*;
+import com.zxyj.mapper.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+@Slf4j
+@Service
+public class DataMigrationService {
+
+    @Resource(name = "sfmsJdbcTemplate")
+    private JdbcTemplate sfms;
+
+    @Resource
+    private FamilyMapper familyMapper;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private ChildMapper childMapper;
+    @Resource
+    private TeacherMapper teacherMapper;
+    @Resource
+    private GuideFamilyMapper guideFamilyMapper;
+    @Resource
+    private PackageOrderMapper packageOrderMapper;
+    @Resource
+    private DanAssessmentResultMapper danAssessmentResultMapper;
+    @Resource
+    private GrowthRecordMapper growthRecordMapper;
+    @Resource
+    private GrowthPlanService growthPlanService;
+
+    // ============ 统计结果 ============
+    private int migratedFamilies;
+    private int migratedParents;
+    private int migratedChildren;
+    private int migratedTeachers;
+    private int migratedOrders;
+    private int migratedResults;
+    private int migratedRecords;
+    private int migratedPlans;
+    private final List<String> errors = new ArrayList<>();
+
+    // ============ ID 映射缓存 ============
+    private final Map<String, Long> sfmsPhoneToFamilyId = new HashMap<>();
+    private final Map<Long, Long> sfmsClientIdToChildUserId = new HashMap<>();
+    private final Map<Long, Long> sfmsClientIdToFamilyId = new HashMap<>();
+    private final Map<String, Long> sfmsOpenIdToTeacherUserId = new HashMap<>();
+
+    public Map<String, Object> runMigration() {
+        long start = System.currentTimeMillis();
+        resetCounters();
+        log.info("========== 开始 SFMS 数据迁移 ==========");
+
+        try {
+            // 按依赖顺序执行
+            migrateFamilies();
+            migrateParentUsers();
+            migrateChildUsersAndChildren();
+            migrateTeachers();
+            migrateGuideFamilies();
+            migratePackageOrders();
+            migrateAssessmentResults();
+            migrateGrowthRecords();
+            migrateGrowthPlans();
+
+            log.info("========== SFMS 数据迁移完成,耗时: {}ms ==========", System.currentTimeMillis() - start);
+        } catch (Exception e) {
+            log.error("迁移过程中出错", e);
+            errors.add("全局错误: " + e.getMessage());
+        }
+
+        return buildResult();
+    }
+
+    private void resetCounters() {
+        migratedFamilies = 0;
+        migratedParents = 0;
+        migratedChildren = 0;
+        migratedTeachers = 0;
+        migratedOrders = 0;
+        migratedResults = 0;
+        migratedRecords = 0;
+        migratedPlans = 0;
+        errors.clear();
+        sfmsPhoneToFamilyId.clear();
+        sfmsClientIdToChildUserId.clear();
+        sfmsClientIdToFamilyId.clear();
+        sfmsOpenIdToTeacherUserId.clear();
+    }
+
+    // ================================================================
+    //  1. 家庭迁移:从 crmclientinfo 中提取去重家长手机号,创建家庭
+    // ================================================================
+    private void migrateFamilies() {
+        log.info("--- 1/9: 迁移家庭 ---");
+        try {
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT DISTINCT ci.cell_phone, cip.value AS urgent_name " +
+                "FROM crmclientinfo ci " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'urgentName') cip ON cip.client_id = ci.crm_client_info_id " +
+                "WHERE ci.cell_phone IS NOT NULL AND ci.cell_phone != ''"
+            );
+            for (Map<String, Object> row : rows) {
+                String phone = (String) row.get("cell_phone");
+                if (phone == null || sfmsPhoneToFamilyId.containsKey(phone)) continue;
+
+                String urgentName = (String) row.get("urgent_name");
+                String familyName = urgentName != null && !urgentName.isEmpty() ? urgentName + "的家庭" : phone + "的家庭";
+
+                Family family = new Family();
+                family.setName(familyName);
+                family.setInviteCode(generateInviteCode());
+                try {
+                    familyMapper.insert(family);
+                    sfmsPhoneToFamilyId.put(phone, family.getId());
+                    migratedFamilies++;
+                } catch (Exception e) {
+                    log.warn("创建家庭失败 phone={}: {}", phone, e.getMessage());
+                }
+            }
+            log.info("  创建 {} 个家庭", migratedFamilies);
+        } catch (Exception e) {
+            log.error("迁移家庭出错", e);
+            errors.add("迁移家庭出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  2. 家长用户迁移:从 crmclientinfo 创建家长 accounts
+    // ================================================================
+    private void migrateParentUsers() {
+        log.info("--- 2/9: 迁移家长用户 ---");
+        try {
+            // 收集去重家长(电话+家长名称分组)
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT ci.cell_phone, ci.openid, cip.value AS urgent_name " +
+                "FROM crmclientinfo ci " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'urgentName') cip ON cip.client_id = ci.crm_client_info_id " +
+                "WHERE ci.cell_phone IS NOT NULL AND ci.cell_phone != '' AND ci.openid IS NOT NULL AND ci.openid != ''"
+            );
+            Set<String> dedup = new HashSet<>();
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                String phone = (String) row.get("cell_phone");
+                String openid = (String) row.get("openid");
+                String parentName = (String) row.get("urgent_name");
+                String key = phone + "|" + openid;
+                if (dedup.contains(key)) continue;
+                dedup.add(key);
+
+                Long familyId = sfmsPhoneToFamilyId.get(phone);
+                if (familyId == null) continue;
+
+                User user = new User();
+                user.setOpenid(openid + "_parent");
+                user.setFamilyId(familyId);
+                user.setRole("parent");
+                user.setRoles("[\"parent\"]");
+                user.setNickname(parentName != null ? parentName : phone);
+                user.setPhone(phone);
+                user.setRealName(parentName);
+                try {
+                    userMapper.insert(user);
+                    count++;
+                } catch (Exception e) {
+                    log.warn("创建家长用户失败 phone={}: {}", phone, e.getMessage());
+                }
+            }
+            migratedParents = count;
+            log.info("  创建 {} 个家长用户", migratedParents);
+        } catch (Exception e) {
+            log.error("迁移家长用户出错", e);
+            errors.add("迁移家长用户出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  3. 孩子用户 + children 表迁移
+    // ================================================================
+    @Transactional
+    public void migrateChildUsersAndChildren() {
+        log.info("--- 3/9: 迁移孩子用户 ---");
+        try {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT ci.crm_client_info_id, ci.name, ci.cell_phone, ci.openid, ci.sex, " +
+                "ci.birth_day, ci.social_no, ci.province_show_label, ci.city_show_label, " +
+                "ci.district_show_label, ci.company_name_show_label " +
+                "FROM crmclientinfo ci " +
+                "WHERE ci.name IS NOT NULL AND ci.name != ''"
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    long sfmsClientId = ((Number) row.get("crm_client_info_id")).longValue();
+                    String phone = (String) row.get("cell_phone");
+                    String openid = (String) row.get("openid");
+
+                    // 确定 family
+                    Long familyId = null;
+                    if (phone != null && !phone.isEmpty()) {
+                        familyId = sfmsPhoneToFamilyId.get(phone);
+                    }
+                    if (familyId == null) {
+                        // 如果没有匹配到家庭,创建一个独立家庭
+                        Family family = new Family();
+                        family.setName(((String) row.get("name")) + "的家庭");
+                        family.setInviteCode(generateInviteCode());
+                        familyMapper.insert(family);
+                        familyId = family.getId();
+                        migratedFamilies++;
+                    }
+
+                    // 孩子 openid
+                    String childOpenid = (openid != null && !openid.isEmpty()) ? openid + "_child_" + sfmsClientId : "migrated_child_" + sfmsClientId;
+
+                    // 创建孩子用户
+                    User childUser = new User();
+                    childUser.setOpenid(childOpenid);
+                    childUser.setFamilyId(familyId);
+                    childUser.setRole("child");
+                    childUser.setRoles("[\"child\"]");
+                    childUser.setNickname((String) row.get("name"));
+                    childUser.setPhone(phone);
+                    childUser.setIdCard((String) row.get("social_no"));
+                    if (row.get("birth_day") != null) {
+                        try {
+                            childUser.setBirthday(sdf.parse((String) row.get("birth_day")));
+                        } catch (Exception ignored) {}
+                    }
+                    childUser.setGender("1".equals(String.valueOf(row.get("sex"))) ? "female" : "male");
+                    userMapper.insert(childUser);
+
+                    // 创建 children 记录
+                    Child child = new Child();
+                    child.setUserId(childUser.getId());
+                    child.setFamilyId(familyId);
+                    child.setNickname((String) row.get("name"));
+                    child.setAge(calcAge((String) row.get("birth_day")));
+                    child.setGender("1".equals(String.valueOf(row.get("sex"))) ? "female" : "male");
+                    child.setPenaltyEnabled(1);
+                    child.setTheme("default");
+                    child.setTotalPoints(0);
+                    child.setStreakDays(0);
+                    child.setFocusMaxDaily(3);
+                    child.setFocusRemaining(3);
+                    childMapper.insert(child);
+
+                    sfmsClientIdToChildUserId.put(sfmsClientId, childUser.getId());
+                    sfmsClientIdToFamilyId.put(sfmsClientId, familyId);
+                    count++;
+                } catch (Exception e) {
+                    log.warn("迁移孩子失败 clientId={}: {}", row.get("crm_client_info_id"), e.getMessage());
+                }
+            }
+            migratedChildren = count;
+            log.info("  迁移 {} 个孩子", migratedChildren);
+        } catch (Exception e) {
+            log.error("迁移孩子出错", e);
+            errors.add("迁移孩子出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  4. 规划师迁移
+    // ================================================================
+    private void migrateTeachers() {
+        log.info("--- 4/9: 迁移规划师 ---");
+        try {
+            // sfms 中规划师在 sysuser 表中
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT su.user_id, su._open_id, su.user_name, su.user_pic, " +
+                "sd.dept_bm, sd.dept_type, sd.dept_type_show_label " +
+                "FROM sysuser su " +
+                "JOIN sysdept sd ON sd.dept_id = su.user_dept_id " +
+                "WHERE sd.dept_type IN ('sales', 'assistant', 'angel') AND su._open_id IS NOT NULL"
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    String openid = (String) row.get("_open_id");
+                    if (openid == null || openid.isEmpty()) continue;
+                    String openidKey = openid + "_teacher";
+                    if (sfmsOpenIdToTeacherUserId.containsKey(openidKey)) continue;
+
+                    User user = new User();
+                    user.setOpenid(openidKey);
+                    user.setFamilyId(0L);
+                    user.setRole("teacher");
+                    user.setRoles("[\"teacher\"]");
+                    user.setNickname((String) row.get("user_name"));
+                    user.setTeacherNo((String) row.get("dept_bm"));
+                    user.setTeacherStatus("approved");
+                    userMapper.insert(user);
+
+                    Teacher teacher = new Teacher();
+                    teacher.setUserId(user.getId());
+                    teacher.setTeacherNo((String) row.get("dept_bm"));
+                    teacher.setStatus("active");
+                    teacher.setVerificationStatus("approved");
+                    teacher.setVerifiedAt(new Date());
+                    teacherMapper.insert(teacher);
+
+                    sfmsOpenIdToTeacherUserId.put(openidKey, user.getId());
+                    count++;
+                } catch (Exception e) {
+                    log.warn("迁移规划师失败: {}", e.getMessage());
+                }
+            }
+            migratedTeachers = count;
+            log.info("  迁移 {} 个规划师", migratedTeachers);
+        } catch (Exception e) {
+            log.error("迁移规划师出错", e);
+            errors.add("迁移规划师出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  5. 规划师-家庭关系迁移
+    // ================================================================
+    private void migrateGuideFamilies() {
+        log.info("--- 5/9: 迁移规划师-家庭关系 ---");
+        try {
+            // 通过订单中的 responser(规划师 user_id)推断关系
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT DISTINCT go.responser, go.client_id " +
+                "FROM crmgiftorder go " +
+                "WHERE go.responser IS NOT NULL AND go.client_id IS NOT NULL"
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    long sfmsTeacherUserId = ((Number) row.get("responser")).longValue();
+                    long sfmsClientId = ((Number) row.get("client_id")).longValue();
+
+                    Long teacherUserId = findTeacherUserId(sfmsTeacherUserId);
+                    Long familyId = sfmsClientIdToFamilyId.get(sfmsClientId);
+                    if (teacherUserId == null || familyId == null) continue;
+
+                    GuideFamily gf = new GuideFamily();
+                    gf.setGuideId(teacherUserId);
+                    gf.setFamilyId(familyId);
+                    gf.setServiceType("consulting");
+                    gf.setStatus("binding");
+                    gf.setBoundAt(new Date());
+                    try {
+                        guideFamilyMapper.insert(gf);
+                        count++;
+                    } catch (Exception ignored) {
+                        // 唯一键冲突,跳过
+                    }
+                } catch (Exception e) {
+                    log.warn("迁移规划师-家庭关系失败: {}", e.getMessage());
+                }
+            }
+            log.info("  迁移 {} 条规划师-家庭关系", count);
+        } catch (Exception e) {
+            log.error("迁移规划师-家庭关系出错", e);
+            errors.add("迁移规划师-家庭关系出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  6. 订单迁移
+    // ================================================================
+    private void migratePackageOrders() {
+        log.info("--- 6/9: 迁移订单 ---");
+        try {
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT go.crm_gift_order_id, go.order_no, go.order_status, go.fee_total, " +
+                "go.coupon, go.responser, go.client_id, go.create_time, " +
+                "go.grade, go.school_show_label " +
+                "FROM crmgiftorder go " +
+                "WHERE go.client_id IS NOT NULL"
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    long sfmsClientId = ((Number) row.get("client_id")).longValue();
+                    Long userId = sfmsClientIdToChildUserId.get(sfmsClientId);
+                    Long familyId = sfmsClientIdToFamilyId.get(sfmsClientId);
+                    if (userId == null || familyId == null) continue;
+
+                    String status = mapOrderStatus((String) row.get("order_status"));
+                    BigDecimal fee = row.get("fee_total") != null
+                        ? BigDecimal.valueOf(((Number) row.get("fee_total")).longValue()).divide(BigDecimal.valueOf(100), 2, BigDecimal.ROUND_HALF_UP)
+                        : BigDecimal.ZERO;
+
+                    PackageOrder order = new PackageOrder();
+                    order.setOrderNo((String) row.get("order_no"));
+                    order.setUserId(userId);
+                    order.setFamilyId(familyId);
+                    order.setPackageName("测评套餐");
+                    order.setPrice(fee);
+                    order.setStatus(status);
+                    if (row.get("create_time") != null) {
+                        order.setCreatedAt(parseDateTime((String) row.get("create_time")));
+                    }
+                    packageOrderMapper.insert(order);
+                    count++;
+                } catch (Exception e) {
+                    log.warn("迁移订单失败: {}", e.getMessage());
+                }
+            }
+            migratedOrders = count;
+            log.info("  迁移 {} 个订单", migratedOrders);
+        } catch (Exception e) {
+            log.error("迁移订单出错", e);
+            errors.add("迁移订单出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  7. 测评结果迁移
+    // ================================================================
+    private void migrateAssessmentResults() {
+        log.info("--- 7/9: 迁移测评结果 ---");
+        try {
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT cr.crm_contact_rec_id, cr.client_id, cr.order_id, " +
+                "cr.meet_time, cr.comm_cont, cr.description, cr.responser, " +
+                "cr.rec_pic, cr.rec_video, cr.rec_audio " +
+                "FROM crmcontactrec cr " +
+                "WHERE cr.client_id IS NOT NULL AND cr.comm_cont IS NOT NULL"
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    long sfmsClientId = ((Number) row.get("client_id")).longValue();
+                    Long childUserId = sfmsClientIdToChildUserId.get(sfmsClientId);
+                    if (childUserId == null) continue;
+
+                    // 找 child_id
+                    Child child = childMapper.selectOne(
+                        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Child>()
+                            .eq(Child::getUserId, childUserId)
+                            .last("LIMIT 1")
+                    );
+                    if (child == null) continue;
+
+                    // 找 teacher_id
+                    Long teacherUserId = null;
+                    if (row.get("responser") != null) {
+                        teacherUserId = findTeacherUserId(((Number) row.get("responser")).longValue());
+                    }
+
+                    DanAssessmentResult result = new DanAssessmentResult();
+                    result.setChildId(child.getId());
+                    result.setTeacherId(teacherUserId != null ? teacherUserId : 0L);
+                    result.setDanLevel("C"); // sfms 没有明确 DAN 等级,默认 C
+                    result.setAnalysisReport((String) row.get("comm_cont"));
+                    result.setProcessDesc((String) row.get("description"));
+                    result.setStatus("completed");
+                    if (row.get("meet_time") != null) {
+                        result.setAssessmentDate(parseDate((String) row.get("meet_time")));
+                    }
+
+                    // 媒体字段
+                    result.setPics((String) row.get("rec_pic"));
+                    result.setVideos((String) row.get("rec_video"));
+                    result.setAudio((String) row.get("rec_audio"));
+
+                    danAssessmentResultMapper.insert(result);
+                    count++;
+                } catch (Exception e) {
+                    log.warn("迁移测评结果失败: {}", e.getMessage());
+                }
+            }
+            migratedResults = count;
+            log.info("  迁移 {} 条测评结果", migratedResults);
+        } catch (Exception e) {
+            log.error("迁移测评结果出错", e);
+            errors.add("迁移测评结果出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  8. 成长档案迁移(带快照)
+    // ================================================================
+    private void migrateGrowthRecords() {
+        log.info("--- 8/9: 迁移成长档案 ---");
+        try {
+            // 从 crmclientinfo 中的孩子快照字段 + 最近的测评结果创建成长档案
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT ci.crm_client_info_id, ci.name, ci.cell_phone, ci.birth_day, " +
+                "ci.sex, ci.company_name_show_label, " +
+                "cip_grade.value AS grade, cip_class.value AS grade_class, " +
+                "cip_height.value AS height, cip_weight.value AS weight, " +
+                "cip_nation.value AS nation, cip_degree.value AS parent_degree, " +
+                "cip_family.value AS family_status, cip_single.value AS single_child, " +
+                "cip_score.value AS school_score, cip_address.value AS address, " +
+                "ci.province_show_label, ci.city_show_label, ci.district_show_label " +
+                "FROM crmclientinfo ci " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'grade') cip_grade ON cip_grade.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'gradeClass') cip_class ON cip_class.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'height') cip_height ON cip_height.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'weight') cip_weight ON cip_weight.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'nation') cip_nation ON cip_nation.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'parentDegree') cip_degree ON cip_degree.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'familyStatus') cip_family ON cip_family.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'singleChild') cip_single ON cip_single.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'schoolScore') cip_score ON cip_score.client_id = ci.crm_client_info_id " +
+                "LEFT JOIN (SELECT client_id, value FROM crm_client_info_properties WHERE CODE = 'address') cip_address ON cip_address.client_id = ci.crm_client_info_id "
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    long sfmsClientId = ((Number) row.get("crm_client_info_id")).longValue();
+                    Long childUserId = sfmsClientIdToChildUserId.get(sfmsClientId);
+                    Long familyId = sfmsClientIdToFamilyId.get(sfmsClientId);
+                    if (childUserId == null || familyId == null) continue;
+
+                    Child child = childMapper.selectOne(
+                        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Child>()
+                            .eq(Child::getUserId, childUserId)
+                            .last("LIMIT 1")
+                    );
+                    if (child == null) continue;
+
+                    // 找家长ID(family 中的第一个 parent)
+                    User parent = userMapper.selectOne(
+                        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
+                            .eq(User::getFamilyId, familyId)
+                            .eq(User::getRole, "parent")
+                            .last("LIMIT 1")
+                    );
+
+                    GrowthRecord record = new GrowthRecord();
+                    record.setChildId(child.getId());
+                    record.setParentId(parent != null ? parent.getId() : 0L);
+                    record.setRecordTitle("历史成长档案 - " + (String) row.get("name"));
+                    record.setChildAge(calcAge((String) row.get("birth_day")));
+                    record.setHeight((String) row.get("height"));
+                    record.setWeight((String) row.get("weight"));
+                    record.setDanLevel("C");
+                    record.setStatus("completed");
+                    record.setExternalSync(true);
+                    record.setExternalSource("sfms");
+
+                    // 构建快照摘要
+                    StringBuilder summary = new StringBuilder();
+                    if (row.get("grade") != null) summary.append("年级:").append(row.get("grade")).append("; ");
+                    if (row.get("grade_class") != null) summary.append("班级:").append(row.get("grade_class")).append("; ");
+                    if (row.get("school_score") != null) summary.append("成绩:").append(row.get("school_score")).append("; ");
+                    if (row.get("family_status") != null) summary.append("家庭:").append(row.get("family_status")).append("; ");
+                    if (row.get("nation") != null) summary.append("民族:").append(row.get("nation")).append("; ");
+                    if (row.get("parent_degree") != null) summary.append("父母学历:").append(row.get("parent_degree")).append("; ");
+                    String summaryStr = summary.toString();
+                    if (!summaryStr.isEmpty()) {
+                        record.setAssessmentSummary(summaryStr);
+                    }
+
+                    growthRecordMapper.insert(record);
+                    count++;
+                } catch (Exception e) {
+                    log.warn("迁移成长档案失败: {}", e.getMessage());
+                }
+            }
+            migratedRecords = count;
+            log.info("  迁移 {} 条成长档案", migratedRecords);
+        } catch (Exception e) {
+            log.error("迁移成长档案出错", e);
+            errors.add("迁移成长档案出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  9. 成长方案迁移
+    // ================================================================
+    private void migrateGrowthPlans() {
+        log.info("--- 9/9: 迁移成长方案 ---");
+        try {
+            List<Map<String, Object>> rows = sfms.queryForList(
+                "SELECT gp.crm_client_gp_id, gp.client_id, gp.order_id, " +
+                "gp.plan_name, gp.plan_desc, gp.plan_status " +
+                "FROM crmclientgp gp " +
+                "WHERE gp.client_id IS NOT NULL"
+            );
+            int count = 0;
+            for (Map<String, Object> row : rows) {
+                try {
+                    long sfmsClientId = ((Number) row.get("client_id")).longValue();
+                    Long childUserId = sfmsClientIdToChildUserId.get(sfmsClientId);
+                    if (childUserId == null) continue;
+
+                    Child child = childMapper.selectOne(
+                        new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<Child>()
+                            .eq(Child::getUserId, childUserId)
+                            .last("LIMIT 1")
+                    );
+                    if (child == null) continue;
+
+                    GrowthPlan plan = new GrowthPlan();
+                    plan.setChildId(child.getId());
+                    plan.setPlanTitle((String) row.get("plan_name"));
+                    plan.setPlanContent((String) row.get("plan_desc"));
+                    plan.setStatus("active".equals(row.get("plan_status")) ? "active" : "draft");
+                    growthPlanService.createPlan(plan);
+                    count++;
+                } catch (Exception e) {
+                    log.warn("迁移成长方案失败: {}", e.getMessage());
+                }
+            }
+            migratedPlans = count;
+            log.info("  迁移 {} 条成长方案", migratedPlans);
+        } catch (Exception e) {
+            log.error("迁移成长方案出错", e);
+            errors.add("迁移成长方案出错: " + e.getMessage());
+        }
+    }
+
+    // ================================================================
+    //  辅助方法
+    // ================================================================
+
+    private Long findTeacherUserId(long sfmsTeacherUserId) {
+        // 从 sfms sysuser 的反向查找
+        try {
+            Map<String, Object> su = sfms.queryForMap(
+                "SELECT _open_id FROM sysuser WHERE user_id = ?", sfmsTeacherUserId
+            );
+            String openid = (String) su.get("_open_id");
+            if (openid != null) {
+                String key = openid + "_teacher";
+                Long uid = sfmsOpenIdToTeacherUserId.get(key);
+                if (uid != null) return uid;
+                User u = userMapper.selectOne(
+                    new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<User>()
+                        .eq(User::getOpenid, key)
+                        .last("LIMIT 1")
+                );
+                if (u != null) {
+                    sfmsOpenIdToTeacherUserId.put(key, u.getId());
+                    return u.getId();
+                }
+            }
+        } catch (Exception e) {
+            log.warn("查找规划师用户ID失败 sfmsUserId={}", sfmsTeacherUserId);
+        }
+        return null;
+    }
+
+    private int calcAge(String birthdayStr) {
+        if (birthdayStr == null || birthdayStr.isEmpty()) return 0;
+        try {
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
+            Date birth = sdf.parse(birthdayStr);
+            Calendar cal = Calendar.getInstance();
+            int thisYear = cal.get(Calendar.YEAR);
+            cal.setTime(birth);
+            int birthYear = cal.get(Calendar.YEAR);
+            return thisYear - birthYear;
+        } catch (Exception e) {
+            return 0;
+        }
+    }
+
+    private String mapOrderStatus(String sfmsStatus) {
+        if (sfmsStatus == null) return "pending";
+        switch (sfmsStatus) {
+            case "accept": return "pending";
+            case "paid":
+            case "pick": return "paid";
+            case "cancel": return "cancelled";
+            default: return "pending";
+        }
+    }
+
+    private Date parseDate(String str) {
+        if (str == null || str.isEmpty()) return null;
+        try {
+            String d = str.length() > 10 ? str.substring(0, 10) : str;
+            return new SimpleDateFormat("yyyy-MM-dd").parse(d);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private Date parseDateTime(String str) {
+        if (str == null || str.isEmpty()) return null;
+        try {
+            return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
+        } catch (Exception e) {
+            return parseDate(str);
+        }
+    }
+
+    private String generateInviteCode() {
+        String chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
+        StringBuilder code = new StringBuilder();
+        Random rand = new Random();
+        for (int i = 0; i < 8; i++) {
+            code.append(chars.charAt(rand.nextInt(chars.length())));
+        }
+        return code.toString();
+    }
+
+    private Map<String, Object> buildResult() {
+        Map<String, Object> result = new LinkedHashMap<>();
+        result.put("success", errors.isEmpty());
+        result.put("migratedFamilies", migratedFamilies);
+        result.put("migratedParents", migratedParents);
+        result.put("migratedChildren", migratedChildren);
+        result.put("migratedTeachers", migratedTeachers);
+        result.put("migratedOrders", migratedOrders);
+        result.put("migratedResults", migratedResults);
+        result.put("migratedRecords", migratedRecords);
+        result.put("migratedPlans", migratedPlans);
+        result.put("total", migratedFamilies + migratedParents + migratedChildren + migratedTeachers
+            + migratedOrders + migratedResults + migratedRecords + migratedPlans);
+        if (!errors.isEmpty()) {
+            result.put("errors", errors);
+        }
+        return result;
+    }
+}

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/DatabaseSyncService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.DatabaseSyncServiceInterface;
+
 import javax.annotation.Resource;
 
 import com.zxyj.entity.*;
@@ -67,7 +69,7 @@ public class DatabaseSyncService {
         JAVA_TYPE_TO_SQL_TYPE.put(java.math.BigDecimal.class, "DECIMAL(10,2)");
     }
 
-    @PostConstruct
+    // @PostConstruct
     public void syncDatabaseSchema() {
         log.info("开始同步数据库表结构...");
         long startTime = System.currentTimeMillis();

+ 24 - 0
zxyj-backend/src/main/java/com/zxyj/service/GrowthPlanService.java

@@ -0,0 +1,24 @@
+package com.zxyj.service;
+
+import com.zxyj.service.api.GrowthPlanServiceInterface;
+
+import com.zxyj.entity.GrowthPlan;
+import com.zxyj.mapper.GrowthPlanMapper;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.Date;
+
+@Service
+public class GrowthPlanService {
+
+    @Resource
+    private GrowthPlanMapper growthPlanMapper;
+
+    public GrowthPlan createPlan(GrowthPlan plan) {
+        plan.setCreatedAt(new Date());
+        plan.setUpdatedAt(new Date());
+        growthPlanMapper.insert(plan);
+        return plan;
+    }
+}

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/GrowthRecordService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.GrowthRecordServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.entity.DanAssessmentResult;
 import com.zxyj.entity.GrowthRecord;

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

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.GuideApplicationServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.Guide;

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

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.GuideFamilyRelationServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.GuideFamilyRelation;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuideFamilyService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.GuideFamilyServiceInterface;
 import javax.annotation.Resource;
 
 import com.zxyj.entity.GuideFamily;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuideHierarchyService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.GuideHierarchyServiceInterface;
 import javax.annotation.Resource;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuideInviteCodeService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.GuideInviteCodeServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.GuideInviteCode;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/GuidePackageService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.GuidePackageServiceInterface;
 import javax.annotation.Resource;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -16,7 +18,7 @@ import java.util.List;
  * 指导师套餐服务
  */
 @Service
-public class GuidePackageService extends ServiceImpl<GuidePackageMapper, GuidePackage> {
+public class GuidePackageService extends ServiceImpl<GuidePackageMapper, GuidePackage> implements GuidePackageServiceInterface {
 
     @Resource
     private TaskTemplatePackageMapper taskTemplatePackageMapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/GuidePackageTemplateService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.GuidePackageTemplateServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.GuidePackageTemplate;

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

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.GuideServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.Guide;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/LoginAttemptService.java

@@ -1,11 +1,13 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.LoginAttemptServiceInterface;
+
 import org.springframework.stereotype.Service;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
 
 @Service
-public class LoginAttemptService {
+public class LoginAttemptService implements LoginAttemptServiceInterface {
 
     private static final int MAX_ATTEMPTS = 5;
     private static final long LOCK_DURATION_MS = 15 * 60 * 1000L;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/MediaRecordService.java

@@ -1,11 +1,13 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.MediaRecordServiceInterface;
+
 import com.zxyj.entity.MediaRecord;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
-public interface MediaRecordService {
+public interface MediaRecordService extends MediaRecordServiceInterface {
 
     /**
      * 上传媒体文件(图片/视频/音频)

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/MembershipService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.MembershipServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.dto.*;
 import com.zxyj.entity.*;
@@ -13,7 +15,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 @Service
-public class MembershipService {
+public class MembershipService implements MembershipServiceInterface {
 
     @Resource
     private MembershipLevelMapper levelMapper;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/MiniGameService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.MiniGameServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.entity.Child;
 import com.zxyj.entity.MiniGame;
@@ -19,7 +21,7 @@ import java.util.Map;
 
 @Slf4j
 @Service
-public class MiniGameService {
+public class MiniGameService implements MiniGameServiceInterface {
 
     @Resource
     private MiniGameMapper miniGameMapper;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/PackagePaymentService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.PackagePaymentServiceInterface;
 import javax.annotation.Resource;
 
 import com.alibaba.fastjson.JSON;
@@ -19,7 +21,7 @@ import java.util.Map;
 import java.util.UUID;
 
 @Service
-public class PackagePaymentService {
+public class PackagePaymentService implements PackagePaymentServiceInterface {
 
     private static final Logger log = LoggerFactory.getLogger(PackagePaymentService.class);
 

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/PackagePurchaseService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.PackagePurchaseServiceInterface;
 import javax.annotation.Resource;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@@ -23,7 +25,7 @@ import java.util.stream.Collectors;
  * 套餐购买服务
  */
 @Service
-public class PackagePurchaseService {
+public class PackagePurchaseService implements PackagePurchaseServiceInterface {
 
     @Resource
     private GuideInviteCodeService guideInviteCodeService;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/PaymentService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.PaymentServiceInterface;
 import javax.annotation.Resource;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/PointsService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.PointsServiceInterface;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -15,7 +17,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 @Service
-public class PointsService {
+public class PointsService implements PointsServiceInterface {
 
     @Resource
     private ChildMapper childMapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/RewardWishlistService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.RewardWishlistServiceInterface;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.dto.CreateRewardWishlistDTO;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/ServiceContentService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.ServiceContentServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.ServiceContent;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/ServiceTypeService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.ServiceTypeServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.zxyj.entity.ServiceType;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/StorageService.java

@@ -1,8 +1,10 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.StorageServiceInterface;
+
 import org.springframework.web.multipart.MultipartFile;
 
-public interface StorageService {
+public interface StorageService extends StorageServiceInterface {
 
     /**
      * 存储文件并返回访问URL

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/StreetService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.StreetServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.entity.Street;
 import com.zxyj.mapper.StreetMapper;
@@ -13,7 +15,7 @@ import java.util.stream.Collectors;
  * 街道服务 - 支持四级地址和区域回退匹配
  */
 @Service
-public class StreetService {
+public class StreetService implements StreetServiceInterface {
 
     @Resource
     private StreetMapper streetMapper;

+ 3 - 1
zxyj-backend/src/main/java/com/zxyj/service/TaskPlanService.java

@@ -1,4 +1,6 @@
 package com.zxyj.service;
+
+import com.zxyj.service.api.TaskPlanServiceInterface;
 import javax.annotation.Resource;
 
 import com.zxyj.entity.TaskPlanInstance;
@@ -18,7 +20,7 @@ import java.util.List;
 import java.util.stream.Collectors;
 
 @Service
-public class TaskPlanService {
+public class TaskPlanService implements TaskPlanServiceInterface {
 
     @Resource
     private TaskPlanInstanceMapper instanceMapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/TaskReminderService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.TaskReminderServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.entity.Task;
 import com.zxyj.mapper.TaskMapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/TaskTemplatePackageService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.TaskTemplatePackageServiceInterface;
+
 import com.zxyj.entity.TaskTemplatePackage;
 import com.zxyj.entity.TaskTemplateItem;
 import com.zxyj.mapper.TaskTemplatePackageMapper;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/TeacherMessageService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.TeacherMessageServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zxyj.entity.TeacherMessage;

+ 1 - 0
zxyj-backend/src/main/java/com/zxyj/service/TeacherService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.dto.*;
 import com.zxyj.entity.*;
 import com.zxyj.mapper.*;
+import com.zxyj.service.api.TeacherServiceInterface;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/UserOperationLogService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.UserOperationLogServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.zxyj.entity.UserOperationLog;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/WishService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.WishServiceInterface;
+
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.zxyj.dto.CreateWishDTO;
 import com.zxyj.dto.WishDTO;

+ 2 - 0
zxyj-backend/src/main/java/com/zxyj/service/ZkxzSyncService.java

@@ -1,5 +1,7 @@
 package com.zxyj.service;
 
+import com.zxyj.service.api.ZkxzSyncServiceInterface;
+
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Value;

+ 14 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentAppointmentServiceInterface.java

@@ -0,0 +1,14 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.AssessmentAppointment;
+import com.zxyj.entity.AssessmentOrder;
+import java.util.List;
+
+public interface AssessmentAppointmentServiceInterface {
+    AssessmentAppointment createAppointment(Long userId, Long childId, Long guideId, Long packageId, String appointmentDate, String appointmentTime, String notes);
+    boolean confirmAppointment(Long appointmentId, Long guideId);
+    boolean cancelAppointment(Long appointmentId);
+    List<AssessmentAppointment> getByUserId(Long userId);
+    List<AssessmentAppointment> getByGuideId(Long guideId);
+    AssessmentAppointment getById(Long appointmentId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentOrderServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.AssessmentOrder;
+import java.math.BigDecimal;
+import java.util.List;
+
+public interface AssessmentOrderServiceInterface {
+    AssessmentOrder createOrder(Long familyId, Long userId, Long childId, Long guideId, Long packageId, String guideName, String packageName, Long totalPrice, Long discountAmount);
+    List<AssessmentOrder> getByFamilyId(Long familyId);
+    List<AssessmentOrder> getByGuideId(Long guideId);
+    AssessmentOrder getByOrderNo(String orderNo);
+    boolean updateOrderStatus(String orderNo, String status);
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentPermissionServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.AssessmentAppointment;
+import java.util.List;
+
+public interface AssessmentPermissionServiceInterface {
+    boolean canViewAssessment(Long userId, Long assessmentId);
+    boolean canEditAssessment(Long userId, Long assessmentId);
+    AssessmentAppointment getAssessmentById(Long assessmentId);
+}

+ 29 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/AssessmentServiceInterface.java

@@ -0,0 +1,29 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.AssessmentMaterial;
+import com.zxyj.entity.AssessmentPointsConfig;
+import com.zxyj.entity.AssessmentRecord;
+import com.zxyj.entity.DanAssessmentResult;
+import com.zxyj.entity.FamilyAssessmentConfig;
+import java.util.List;
+
+public interface AssessmentServiceInterface {
+    AssessmentMaterial getActiveMaterial();
+    List<AssessmentMaterial> getMaterialList();
+    AssessmentMaterial createMaterial(String title, String content, String coverImage);
+    AssessmentPointsConfig getPointsConfig();
+    boolean updatePointsConfig(Integer pointsValue, Double pointsValueYuan);
+    FamilyAssessmentConfig getFamilyConfig(Long familyId);
+    boolean enableFamilyAssessment(Long familyId, Long materialId, Integer remindMonths, Long createdBy);
+    boolean disableFamilyAssessment(Long familyId);
+    List<FamilyAssessmentConfig> getEnabledConfigs();
+    List<FamilyAssessmentConfig> getFamiliesByTeacher(Long teacherId);
+    AssessmentRecord createRecord(Long familyId, Long childId, Long materialId);
+    boolean completeRecord(Long recordId);
+    List<AssessmentRecord> getFamilyRecords(Long familyId);
+    AssessmentRecord getCurrentAssessment(Long familyId);
+    DanAssessmentResult recordResult(DanAssessmentResult result);
+    List<DanAssessmentResult> getResultsByGuide(Long guideId);
+    DanAssessmentResult getByAppointmentId(Long appointmentId);
+    List<DanAssessmentResult> getAccessibleResults(Long userId, String role);
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/BindInviteServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import java.math.BigDecimal;
+import java.util.Map;
+
+public interface BindInviteServiceInterface {
+    Map<String, Object> generateInvite(Long guideId);
+    boolean confirmBind(String inviteToken, Long guideId, Long familyId, String serviceType, BigDecimal servicePrice);
+    Map<String, Object> validateToken(String inviteToken);
+}

+ 7 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/DataMigrationServiceInterface.java

@@ -0,0 +1,7 @@
+package com.zxyj.service.api;
+
+import java.util.Map;
+
+public interface DataMigrationServiceInterface {
+    Map<String, Object> runMigration();
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/DatabaseSyncServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import java.util.List;
+
+public interface DatabaseSyncServiceInterface {
+    boolean syncSchema();
+    boolean syncData(String tableName);
+    List<String> getPendingSyncTables();
+    boolean executeMigration(String migrationName);
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GrowthPlanServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import java.util.Map;
+
+public interface GrowthPlanServiceInterface {
+    Map<String, Object> generatePlan(Long childId);
+    Map<String, Object> getPlanByChildId(Long childId);
+    boolean updatePlan(Long childId, Map<String, Object> planData);
+    boolean evaluateProgress(Long childId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GrowthRecordServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GrowthRecord;
+import java.util.List;
+
+public interface GrowthRecordServiceInterface {
+    GrowthRecord createRecord(GrowthRecord record);
+    GrowthRecord getRecordById(Long recordId);
+    List<GrowthRecord> getRecordsByChildId(Long childId);
+    List<GrowthRecord> getRecordsByDateRange(Long childId, String startDate, String endDate);
+    boolean updateRecord(GrowthRecord record);
+    boolean deleteRecord(Long recordId);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuideApplicationServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuideApplication;
+import java.util.List;
+
+public interface GuideApplicationServiceInterface {
+    GuideApplication apply(String phone, String inviteCode);
+    List<GuideApplication> getPendingApplications();
+    boolean approveApplication(Long applicationId, Long approverId);
+    boolean rejectApplication(Long applicationId, String reason);
+    GuideApplication getApplicationByPhone(String phone);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuideFamilyRelationServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuideFamilyRelation;
+import java.util.List;
+
+public interface GuideFamilyRelationServiceInterface {
+    boolean bindFamily(Long guideId, Long familyId);
+    boolean unbindFamily(Long guideId, Long familyId);
+    List<GuideFamilyRelation> getByGuideId(Long guideId);
+    List<GuideFamilyRelation> getByFamilyId(Long familyId);
+    boolean isBound(Long guideId, Long familyId);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuideFamilyServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuideFamily;
+import java.util.List;
+
+public interface GuideFamilyServiceInterface {
+    GuideFamily createFamily(GuideFamily family);
+    GuideFamily getFamilyById(Long familyId);
+    List<GuideFamily> getFamiliesByGuide(Long guideId);
+    boolean updateFamily(GuideFamily family);
+    boolean deleteFamily(Long familyId);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuideHierarchyServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuideHierarchy;
+import java.util.List;
+
+public interface GuideHierarchyServiceInterface {
+    boolean addChildGuide(Long parentId, Long childId);
+    boolean removeChildGuide(Long parentId, Long childId);
+    List<GuideHierarchy> getChildren(Long parentId);
+    GuideHierarchy getByUserId(Long userId);
+    Integer getTeamCount(Long guideId);
+}

+ 11 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuideInviteCodeServiceInterface.java

@@ -0,0 +1,11 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuideInviteCode;
+import java.util.List;
+
+public interface GuideInviteCodeServiceInterface {
+    GuideInviteCode generateCode(Long creatorId);
+    GuideInviteCode getByCode(String code);
+    boolean useCode(String code, Long userId);
+    List<GuideInviteCode> getByCreatorId(Long creatorId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuidePackageServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuidePackage;
+import java.math.BigDecimal;
+import java.util.List;
+
+public interface GuidePackageServiceInterface {
+    boolean addGuidePackage(Long guideId, Long packageId, BigDecimal price, BigDecimal commissionRate);
+    List<GuidePackage> getGuidePackages(Long guideId);
+    GuidePackage getGuidePackage(Long guideId, Long packageId);
+    boolean updateGuidePackagePrice(Long guideId, Long packageId, BigDecimal price);
+    boolean deleteGuidePackage(Long guideId, Long packageId);
+}

+ 14 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuidePackageTemplateServiceInterface.java

@@ -0,0 +1,14 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.GuidePackageTemplate;
+import java.math.BigDecimal;
+import java.util.List;
+
+public interface GuidePackageTemplateServiceInterface {
+    GuidePackageTemplate createTemplate(GuidePackageTemplate template);
+    GuidePackageTemplate getTemplateById(Long templateId);
+    List<GuidePackageTemplate> getAllTemplates();
+    List<GuidePackageTemplate> getActiveTemplates();
+    boolean updateTemplate(GuidePackageTemplate template);
+    boolean deleteTemplate(Long templateId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/GuideServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.Guide;
+import java.util.List;
+
+public interface GuideServiceInterface {
+    Guide registerGuide(Guide guide);
+    Guide getGuideById(Long guideId);
+    Guide getGuideByUserId(Long userId);
+    List<Guide> getAllGuides();
+    boolean updateGuide(Guide guide);
+    boolean approveGuide(Long guideId, Long approverId);
+}

+ 9 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/LoginAttemptServiceInterface.java

@@ -0,0 +1,9 @@
+package com.zxyj.service.api;
+
+public interface LoginAttemptServiceInterface {
+    boolean isBlocked(String key);
+    void recordFailure(String key);
+    void clearSuccess(String key);
+    int getRemainingAttempts(String key);
+    long getLockRemainingTime(String key);
+}

+ 34 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/MediaRecordServiceInterface.java

@@ -0,0 +1,34 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.MediaRecord;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.util.List;
+
+public interface MediaRecordServiceInterface {
+
+    /**
+     * 上传媒体文件(图片/视频/音频)
+     */
+    MediaRecord uploadMedia(MultipartFile file, Long taskId, Long creatorId, String description);
+
+    /**
+     * 保存文字记录
+     */
+    MediaRecord saveTextRecord(Long taskId, Long creatorId, String content);
+
+    /**
+     * 获取任务的所有媒体记录
+     */
+    List<MediaRecord> getMediaByTaskId(Long taskId);
+
+    /**
+     * 删除媒体记录
+     */
+    boolean deleteMedia(Long mediaId, Long userId);
+
+    /**
+     * 将媒体记录关联到任务
+     */
+    boolean attachMediaToTask(Long taskId, List<Long> mediaIds);
+}

+ 17 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/MembershipServiceInterface.java

@@ -0,0 +1,17 @@
+package com.zxyj.service.api;
+
+import com.zxyj.dto.FamilyMembershipDTO;
+import com.zxyj.dto.MembershipLevelDTO;
+import com.zxyj.dto.PaymentOrderDTO;
+
+import java.util.List;
+
+public interface MembershipServiceInterface {
+    List<MembershipLevelDTO> getAllLevels();
+    FamilyMembershipDTO getFamilyMembership(Long familyId);
+    MembershipLevelDTO getCurrentLevel(Long familyId);
+    boolean hasFeature(Long familyId, String feature);
+    PaymentOrderDTO createOrder(Long familyId, String levelCode, String paymentType);
+    boolean processPaymentCallback(String orderNo, String transactionId, String payMethod);
+    boolean canUseFeature(Long familyId, String feature);
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/MiniGameServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.MiniGame;
+import java.util.List;
+
+public interface MiniGameServiceInterface {
+    List<MiniGame> getEnabledGames();
+    MiniGame getGameByCode(String gameCode);
+    List<MiniGame> getAllGames();
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/PackagePaymentServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.PackageOrder;
+
+import java.util.Map;
+
+public interface PackagePaymentServiceInterface {
+    PackageOrder createOrder(Long userId, Long familyId, Long packageId, String payMethod);
+    Map<String, Object> createWechatPayOrder(Long orderId, String openid);
+    boolean handlePayNotify(String xmlData);
+    PackageOrder getOrderById(Long orderId);
+    boolean cancelOrder(Long orderId);
+}

+ 14 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/PackagePurchaseServiceInterface.java

@@ -0,0 +1,14 @@
+package com.zxyj.service.api;
+
+import com.zxyj.dto.PackagePurchaseDTO;
+import com.zxyj.entity.PackageOrder;
+
+import java.util.List;
+import java.util.Map;
+
+public interface PackagePurchaseServiceInterface {
+    Map<String, Object> getPackagePrice(Long packageId, Long guideId, String inviteCode);
+    List<Map<String, Object>> getAvailableGuides(Long packageId);
+    PackageOrder createOrder(PackagePurchaseDTO dto);
+    PackageOrder getOrder(String orderNo);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/PaymentServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.PackageOrder;
+
+import java.util.Map;
+
+public interface PaymentServiceInterface {
+    Map<String, Object> createWechatPayOrder(String orderNo, String description, Integer amount);
+    Map<String, Object> createAlipayOrder(String orderNo, String description, Integer amount);
+    boolean handlePaymentCallback(String orderNo, String transactionId, String payMethod);
+    PackageOrder getOrderStatus(String orderNo);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/PointsServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.zxyj.entity.PointsLog;
+
+import java.util.Map;
+
+public interface PointsServiceInterface {
+    Map<String, Object> getBalance(Long childId);
+    Page<PointsLog> getPointsLogs(Long childId, Integer page, Integer size);
+    Map<String, Object> adjustPoints(Long childId, Integer amount, String reason, String password, Long userId);
+}

+ 11 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/RewardWishlistServiceInterface.java

@@ -0,0 +1,11 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.RewardWishlist;
+import java.util.List;
+
+public interface RewardWishlistServiceInterface {
+    RewardWishlist addToWishlist(Long childId, Long rewardId);
+    List<RewardWishlist> getChildWishlist(Long childId);
+    boolean removeFromWishlist(Long childId, Long rewardId);
+    boolean isInWishlist(Long childId, Long rewardId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/ServiceContentServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.ServiceContent;
+import java.util.List;
+
+public interface ServiceContentServiceInterface {
+    ServiceContent createContent(ServiceContent serviceContent);
+    ServiceContent getById(Long contentId);
+    List<ServiceContent> getContentsByType(Long typeId);
+    List<ServiceContent> getActiveContents();
+    boolean updateContent(ServiceContent serviceContent);
+    boolean deleteContent(Long contentId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/ServiceTypeServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.ServiceType;
+import java.util.List;
+
+public interface ServiceTypeServiceInterface {
+    ServiceType createType(ServiceType serviceType);
+    ServiceType getById(Long typeId);
+    List<ServiceType> getAllTypes();
+    List<ServiceType> getActiveTypes();
+    boolean updateType(ServiceType serviceType);
+    boolean deleteType(Long typeId);
+}

+ 21 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/StorageServiceInterface.java

@@ -0,0 +1,21 @@
+package com.zxyj.service.api;
+
+import org.springframework.web.multipart.MultipartFile;
+
+public interface StorageServiceInterface {
+
+    /**
+     * 存储文件并返回访问URL
+     */
+    String storeFile(MultipartFile file);
+
+    /**
+     * 删除文件
+     */
+    void deleteFile(String fileUrl);
+
+    /**
+     * 获取文件完整路径
+     */
+    String getFilePath(String filename);
+}

+ 17 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/TaskPlanServiceInterface.java

@@ -0,0 +1,17 @@
+package com.zxyj.service.api;
+
+import com.zxyj.dto.ApplyPackageDTO;
+import com.zxyj.entity.TaskPlanInstance;
+import com.zxyj.entity.TaskPlanItem;
+
+import java.util.List;
+
+public interface TaskPlanServiceInterface {
+    TaskPlanInstance applyPackage(ApplyPackageDTO dto, Long familyId);
+    List<TaskPlanInstance> getFamilyPlans(Long familyId);
+    TaskPlanInstance getPlanById(Long id);
+    List<TaskPlanItem> getPlanItems(Long instanceId);
+    boolean cancelPlan(Long id);
+    boolean updateCompletedDays(Long id, int days);
+    List<TaskPlanInstance> getChildPlans(Long childId);
+}

+ 18 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/TaskReminderServiceInterface.java

@@ -0,0 +1,18 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.Task;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+public interface TaskReminderServiceInterface {
+    List<Task> getUpcomingTasks(Long childId, int minutesBefore);
+    List<Task> getTasksNeedReminder(Long childId);
+    List<Task> getOverdueTasks(Long childId);
+    Map<String, Object> getTaskHistoryWithFilter(Long childId, String status, Date startDate, Date endDate, Integer page, Integer size);
+    Map<String, Object> getTaskStatistics(Long childId, Date startDate, Date endDate);
+    List<Map<String, Object>> getTaskStatsByCategory(Long childId);
+    List<Map<String, Object>> getTaskStatsByDate(Long childId, int days);
+    Map<String, Object> getFamilyTaskStatistics(Long familyId);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/TaskTemplatePackageServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.TaskTemplatePackage;
+import java.util.List;
+
+public interface TaskTemplatePackageServiceInterface {
+    TaskTemplatePackage createTemplate(TaskTemplatePackage template);
+    TaskTemplatePackage getTemplateById(Long templateId);
+    List<TaskTemplatePackage> getTemplatesByCreator(Long creatorId);
+    List<TaskTemplatePackage> getPublicTemplates();
+    boolean updateTemplate(TaskTemplatePackage template);
+    boolean deleteTemplate(Long templateId);
+}

+ 12 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/TeacherMessageServiceInterface.java

@@ -0,0 +1,12 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.TeacherMessage;
+import java.util.List;
+
+public interface TeacherMessageServiceInterface {
+    Long sendMessage(Long familyId, Long parentId, Long teacherId, String content);
+    boolean replyMessage(Long messageId, Long teacherId, String replyContent);
+    List<TeacherMessage> getMessagesByTeacher(Long teacherId, Integer page, Integer size);
+    List<TeacherMessage> getMessagesByParent(Long parentId);
+    boolean markAsRead(Long messageId);
+}

+ 20 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/TeacherServiceInterface.java

@@ -0,0 +1,20 @@
+package com.zxyj.service.api;
+
+import com.zxyj.dto.AssessmentAppointmentDTO;
+import com.zxyj.dto.ChildInfoDTO;
+import com.zxyj.dto.CreateAppointmentDTO;
+import com.zxyj.dto.CreateAssessmentResultDTO;
+import com.zxyj.dto.DanAssessmentResultDTO;
+import com.zxyj.dto.TeacherDTO;
+import java.util.List;
+
+public interface TeacherServiceInterface {
+    TeacherDTO getTeacherInfo(Long userId);
+    TeacherDTO createTeacher(Long userId, TeacherDTO dto);
+    List<AssessmentAppointmentDTO> getAppointments(Long teacherId, String status);
+    AssessmentAppointmentDTO createAppointment(Long teacherId, CreateAppointmentDTO dto);
+    AssessmentAppointmentDTO updateAppointmentStatus(Long appointmentId, String status);
+    DanAssessmentResultDTO createAssessmentResult(Long teacherId, CreateAssessmentResultDTO dto);
+    List<DanAssessmentResultDTO> getChildAssessmentHistory(Long childId);
+    List<ChildInfoDTO> getAuthorizedChildren(Long teacherId);
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/UserOperationLogServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.UserOperationLog;
+import java.util.List;
+
+public interface UserOperationLogServiceInterface {
+    void logOperation(Long userId, String operation, String detail);
+    List<UserOperationLog> getLogsByUserId(Long userId);
+    List<UserOperationLog> getLogsByOperation(String operation);
+}

+ 13 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/WishServiceInterface.java

@@ -0,0 +1,13 @@
+package com.zxyj.service.api;
+
+import com.zxyj.entity.Wish;
+import java.util.List;
+
+public interface WishServiceInterface {
+    Wish createWish(Wish wish);
+    List<Wish> getFamilyWishes(Long familyId);
+    Wish getWishById(Long wishId);
+    boolean updateWish(Wish wish);
+    boolean deleteWish(Long wishId);
+    boolean exchangeWish(Long wishId, Long childId);
+}

+ 10 - 0
zxyj-backend/src/main/java/com/zxyj/service/api/ZkxzSyncServiceInterface.java

@@ -0,0 +1,10 @@
+package com.zxyj.service.api;
+
+import java.util.Map;
+
+public interface ZkxzSyncServiceInterface {
+    boolean syncUserData(Long userId);
+    boolean syncFamilyData(Long familyId);
+    Map<String, Object> getSyncStatus(Long userId);
+    boolean fullSync();
+}