Просмотр исходного кода

feat(P0): 场景3减肥套餐主线 — 打卡返积分+产品数据+报告自动出方案

Sisyphus 1 месяц назад
Родитель
Сommit
46fd7bd559

+ 2 - 0
cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

@@ -1076,6 +1076,8 @@ log.info("已添加template_id列到tasks表");
             insertProductSeed("肠道菌群检测套装", "家用检测 + 报告解读", "physical", 59900, "body", "budget");
             insertProductSeed("肠道菌群检测套装", "家用检测 + 报告解读", "physical", 59900, "body", "budget");
             insertProductSeed("儿童多模态认知测评", "在线测评 + 个性化建议", "digital", 9900, "wisdom", "none");
             insertProductSeed("儿童多模态认知测评", "在线测评 + 个性化建议", "digital", 9900, "wisdom", "none");
             insertProductSeed("成长规划师1对1咨询", "60分钟专业规划", "service", 50000, "wealth", "budget");
             insertProductSeed("成长规划师1对1咨询", "60分钟专业规划", "service", 50000, "wealth", "budget");
+            insertProductSeed("21天科学减重套餐", "基于肠道菌群的个性化减重方案,21天找到适合自己体质的减重方式,不再反弹", "physical", 49900, "body", "all");
+            insertProductSeed("28天血糖平稳管理套餐", "餐后血糖生活方式管理,基于菌群的个性化饮食方案,帮助糖前期人群平稳血糖", "physical", 49900, "body", "all");
             log.info("Product 种子数据已加载");
             log.info("Product 种子数据已加载");
         } catch (Exception e) {
         } catch (Exception e) {
             log.warn("Product 种子数据初始化失败: {}", e.getMessage());
             log.warn("Product 种子数据初始化失败: {}", e.getMessage());

+ 10 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/HealthMealController.java

@@ -12,6 +12,7 @@ import javax.annotation.Resource;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
+import com.etotem.cfc.service.PointsService;
 import com.etotem.cfc.util.SortUtil;
 import com.etotem.cfc.util.SortUtil;
 
 
 @Tag(name = "饮食打卡")
 @Tag(name = "饮食打卡")
@@ -22,6 +23,9 @@ public class HealthMealController {
     @Resource
     @Resource
     private HealthMealRecordMapper healthMealRecordMapper;
     private HealthMealRecordMapper healthMealRecordMapper;
 
 
+    @Resource
+    private PointsService pointsService;
+
     @Operation(summary = "获取饮食打卡列表")
     @Operation(summary = "获取饮食打卡列表")
     @PostMapping("/list")
     @PostMapping("/list")
     public Result<List<HealthMealRecord>> list(
     public Result<List<HealthMealRecord>> list(
@@ -40,6 +44,12 @@ public class HealthMealController {
     public Result<HealthMealRecord> create(@RequestBody HealthMealRecord record) {
     public Result<HealthMealRecord> create(@RequestBody HealthMealRecord record) {
         record.setCreatedAt(new Date());
         record.setCreatedAt(new Date());
         healthMealRecordMapper.insert(record);
         healthMealRecordMapper.insert(record);
+        // 饮食打卡返积分:每次记录奖励 5 积分(不影响打卡主流程)
+        try {
+            pointsService.awardCheckinPoints(record.getMemberId(), 5, "饮食打卡");
+        } catch (Exception e) {
+            // 积分发放失败不影响打卡记录
+        }
         return Result.success(record);
         return Result.success(record);
     }
     }
 }
 }

+ 13 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/DailyCheckinService.java

@@ -6,16 +6,21 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import org.springframework.stereotype.Service;
 import org.springframework.stereotype.Service;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
 import java.util.Date;
 import java.util.Date;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 
 
+@Slf4j
 @Service
 @Service
 public class DailyCheckinService {
 public class DailyCheckinService {
 
 
     @Resource
     @Resource
     private DailyCheckinMapper dailyCheckinMapper;
     private DailyCheckinMapper dailyCheckinMapper;
 
 
+    @Resource
+    private PointsService pointsService;
+
     public List<DailyHealthCheckin> getCheckinsByMember(Long memberId) {
     public List<DailyHealthCheckin> getCheckinsByMember(Long memberId) {
         return dailyCheckinMapper.selectList(new LambdaQueryWrapper<DailyHealthCheckin>()
         return dailyCheckinMapper.selectList(new LambdaQueryWrapper<DailyHealthCheckin>()
                 .eq(DailyHealthCheckin::getMemberId, memberId)
                 .eq(DailyHealthCheckin::getMemberId, memberId)
@@ -30,6 +35,14 @@ public class DailyCheckinService {
         checkin.setCreatedAt(new Date());
         checkin.setCreatedAt(new Date());
         checkin.setUpdatedAt(new Date());
         checkin.setUpdatedAt(new Date());
         dailyCheckinMapper.insert(checkin);
         dailyCheckinMapper.insert(checkin);
+
+        // 打卡返积分:每日健康打卡奖励 10 积分(不影响打卡主流程)
+        try {
+            pointsService.awardCheckinPoints(checkin.getMemberId(), 10, "每日健康打卡");
+            log.info("成员{}完成每日打卡,奖励10积分", checkin.getMemberId());
+        } catch (Exception e) {
+            log.warn("打卡积分发放失败,不影响打卡记录: {}", e.getMessage());
+        }
         return checkin;
         return checkin;
     }
     }
 
 

+ 8 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/PointsService.java

@@ -126,6 +126,14 @@ public class PointsService implements PointsServiceInterface {
         return newSystemPoints;
         return newSystemPoints;
     }
     }
 
 
+    /**
+     * 打卡返积分(复用 awardSystemPoints 逻辑,仅 category 不同)
+     */
+    @Transactional
+    public int awardCheckinPoints(Long memberId, int amount, String reason) {
+        return awardSystemPoints(memberId, amount, reason);
+    }
+
     /**
     /**
      * 心愿兑换扣除积分(家长审批通过后调用)
      * 心愿兑换扣除积分(家长审批通过后调用)
      * @param memberId 孩子ID
      * @param memberId 孩子ID

+ 23 - 1
cfc-frontend/pages/health/health-plan-summary.vue

@@ -162,7 +162,8 @@ export default {
       taskSubmitted: false,
       taskSubmitted: false,
       taskSubmittedCount: 0,
       taskSubmittedCount: 0,
       healthStatus: null,
       healthStatus: null,
-      dietPrefs: null
+      dietPrefs: null,
+      autoExecute: false
     }
     }
   },
   },
   computed: {
   computed: {
@@ -179,8 +180,17 @@ export default {
       this.selectedMemberIds = this.initialMemberIds.slice()
       this.selectedMemberIds = this.initialMemberIds.slice()
     }
     }
     this.subjectId = options.subjectId || ''
     this.subjectId = options.subjectId || ''
+    this.autoExecute = options.autoExecute === '1'
+    this.loadFamilyReports()
     this.loadExecutors()
     this.loadExecutors()
     this.loadHealthContext()
     this.loadHealthContext()
+    // 自动出方案模式:等待异步加载完成后执行
+    if (this.autoExecute) {
+      var self = this
+      setTimeout(function() {
+        self.doAutoExecuteFlow()
+      }, 2500)
+    }
   },
   },
   onShow: function() {
   onShow: function() {
     this.loadHealthContext()
     this.loadHealthContext()
@@ -263,6 +273,14 @@ export default {
       sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6)
       sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6)
       return d.getTime() < sixMonthsAgo.getTime()
       return d.getTime() < sixMonthsAgo.getTime()
     },
     },
+    doAutoExecuteFlow: function() {
+      if (this.selectedMemberIds.length === 0) return
+      // 自动填充默认目标(菌群报告场景)
+      if (!this.userGoal || this.userGoal.trim().length === 0) {
+        this.userGoal = '基于菌群检测报告的个性化健康管理方案'
+      }
+      this.doGeneratePlan()
+    },
     doGeneratePlan: function() {
     doGeneratePlan: function() {
       var self = this
       var self = this
       self.loading = true
       self.loading = true
@@ -284,6 +302,10 @@ export default {
           self.planContent = answer
           self.planContent = answer
           self.parseTasksFromPlan(answer)
           self.parseTasksFromPlan(answer)
           self.step = 3
           self.step = 3
+          // 自动出方案模式:方案生成后自动提交任务
+          if (self.autoExecute && self.parsedTasks.length > 0) {
+            setTimeout(function() { self.submitTasks() }, 500)
+          }
         } else {
         } else {
           uni.showToast({ title: '生成失败,请重试', icon: 'none' })
           uni.showToast({ title: '生成失败,请重试', icon: 'none' })
         }
         }

+ 4 - 5
cfc-frontend/pages/health/report-confirm.vue

@@ -814,11 +814,10 @@ export default {
           var reportType = res.data && res.data.reportType || ''
           var reportType = res.data && res.data.reportType || ''
           setTimeout(function() {
           setTimeout(function() {
             if (reportId) {
             if (reportId) {
-              if (reportType === 'gut_flora') {
-                uni.redirectTo({ url: '/pages/health/report-detail?reportType=gut_flora&reportId=' + reportId })
-              } else {
-                uni.redirectTo({ url: '/pages/health/health-plan-summary?initialMemberIds=' + (postData.subjectId || '') + '&subjectId=' + (postData.subjectId || '') })
-              }
+              // 所有报告确认入库后统一进入方案制定流程(菌群报告自动出方案)
+              var subjectId = postData.subjectId || ''
+              var autoParam = reportType === 'gut_flora' ? '&autoExecute=1' : ''
+              uni.redirectTo({ url: '/pages/health/health-plan-summary?initialMemberIds=' + subjectId + '&subjectId=' + subjectId + autoParam })
             } else {
             } else {
               uni.navigateBack()
               uni.navigateBack()
             }
             }