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

chore: auto bump version and changelog [skip ci]

iwt 2 месяцев назад
Родитель
Сommit
3da4b6ec10

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

@@ -9340,5 +9340,13 @@ private void runMigration100() {
 		} catch (Exception ex) {
 		} catch (Exception ex) {
 			log.warn("添加payload_json列失败: {}", ex.getMessage());
 			log.warn("添加payload_json列失败: {}", ex.getMessage());
 		}
 		}
+		// 迁移243: user_intents 表添加跳过次数和最近跳过时间
+		try {
+			jdbcTemplate.execute("ALTER TABLE user_intents ADD COLUMN skip_count INT DEFAULT 0 COMMENT '跳过意图问卷次数'");
+			jdbcTemplate.execute("ALTER TABLE user_intents ADD COLUMN last_skip_at DATETIME DEFAULT NULL COMMENT '最近一次跳过意图问卷时间'");
+			log.info("已添加skip_count和last_skip_at列到user_intents表");
+		} catch (Exception ex) {
+			log.warn("添加user_intents跳过列失败: {}", ex.getMessage());
+		}
 	}
 	}
 }
 }

+ 22 - 0
cfc-backend/src/main/java/com/etotem/cfc/controller/UserIntentController.java

@@ -97,6 +97,28 @@ public class UserIntentController {
         return Result.success(intent);
         return Result.success(intent);
     }
     }
 
 
+    @Operation(summary = "用户跳过意图问卷(记录次数,最多提醒3次)")
+    @PostMapping("/skip")
+    public Result<UserIntent> skip(@RequestAttribute("userId") Long userId) {
+        UserIntent intent = findIntent(userId);
+        Date now = new Date();
+        if (intent == null) {
+            intent = new UserIntent();
+            intent.setUserId(userId);
+            intent.setSkipCount(1);
+            intent.setLastSkipAt(now);
+            intent.setCreatedAt(now);
+            intent.setUpdatedAt(now);
+            userIntentMapper.insert(intent);
+        } else {
+            intent.setSkipCount((intent.getSkipCount() == null ? 0 : intent.getSkipCount()) + 1);
+            intent.setLastSkipAt(now);
+            intent.setUpdatedAt(now);
+            userIntentMapper.updateById(intent);
+        }
+        return Result.success(intent);
+    }
+
     private UserIntent findIntent(Long userId) {
     private UserIntent findIntent(Long userId) {
         return userIntentMapper.selectOne(
         return userIntentMapper.selectOne(
                 new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<UserIntent>()
                 new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<UserIntent>()

+ 6 - 0
cfc-backend/src/main/java/com/etotem/cfc/entity/UserIntent.java

@@ -47,4 +47,10 @@ public class UserIntent implements Serializable {
     private Date createdAt;
     private Date createdAt;
 
 
     private Date updatedAt;
     private Date updatedAt;
+
+    /** 跳过意图问卷次数 */
+    private Integer skipCount;
+
+    /** 最近一次跳过时间 */
+    private Date lastSkipAt;
 }
 }

+ 2 - 0
cfc-backend/src/main/resources/schema.sql

@@ -4866,6 +4866,8 @@ CREATE TABLE IF NOT EXISTS user_intents (
   survey_template_id BIGINT DEFAULT NULL COMMENT '最近作答问卷模板ID',
   survey_template_id BIGINT DEFAULT NULL COMMENT '最近作答问卷模板ID',
   survey_submitted_at DATETIME DEFAULT NULL COMMENT '最近问卷提交时间',
   survey_submitted_at DATETIME DEFAULT NULL COMMENT '最近问卷提交时间',
   survey_result_json TEXT COMMENT '问卷答案→推荐结果快照',
   survey_result_json TEXT COMMENT '问卷答案→推荐结果快照',
+  skip_count INT DEFAULT 0 COMMENT '跳过意图问卷次数',
+  last_skip_at DATETIME DEFAULT NULL COMMENT '最近一次跳过意图问卷时间',
   created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
   created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
   updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
   UNIQUE KEY uk_user (user_id)
   UNIQUE KEY uk_user (user_id)

+ 137 - 0
cfc-frontend/pages/health/intent-quiz.vue

@@ -0,0 +1,137 @@
+<template>
+  <view class="page">
+    <view class="header">
+      <text class="title">想从哪里开始?</text>
+      <text class="desc">选一个最符合你当下状态的方式,我们来为你定制专属的健康方案</text>
+    </view>
+
+    <!-- 随便看看 -->
+    <view class="card" @click="handleA">
+      <view class="card-icon" style="background:rgba(249,115,22,0.12)">
+        <text class="icon-text">🎯</text>
+      </view>
+      <view class="card-body">
+        <text class="card-title">随便看看</text>
+        <text class="card-desc">先到处逛逛,了解浠艾福和各式服务</text>
+      </view>
+      <text class="card-arrow">›</text>
+    </view>
+
+    <!-- 先了解自己 -->
+    <view class="card" @click="handleB">
+      <view class="card-icon" style="background:rgba(14,165,233,0.12)">
+        <text class="icon-text">📋</text>
+      </view>
+      <view class="card-body">
+        <text class="card-title">先了解自己</text>
+        <text class="card-desc">完成智能问卷,系统为你推荐适合的五维方向</text>
+      </view>
+      <text class="card-arrow">›</text>
+    </view>
+
+    <!-- 解决问题 -->
+    <view class="card" @click="handleC">
+      <view class="card-icon" style="background:rgba(16,185,129,0.12)">
+        <text class="icon-text">🔍</text>
+      </view>
+      <view class="card-body">
+        <text class="card-title">解决问题</text>
+        <text class="card-desc">选择你关心的问题,获得完整解决方案</text>
+      </view>
+      <text class="card-arrow">›</text>
+    </view>
+
+    <!-- 以后再说 -->
+    <view class="skip-btn" @click="handleSkip">
+      <text class="skip-text">以后再说</text>
+    </view>
+  </view>
+</template>
+
+<script>
+import { getUserIntent, chooseUserIntent, skipUserIntent } from '../../utils/api.js'
+
+export default {
+  data() {
+    return {
+      loading: false
+    }
+  },
+  onLoad() {
+    this.checkExistingIntent()
+  },
+  methods: {
+    checkExistingIntent() {
+      var self = this
+      getUserIntent().then(function(res) {
+        if (res.code === 200 && res.data && res.data.intentType) {
+          var intent = res.data
+          if (intent.intentType === 'A') {
+            uni.navigateBack({ delta: 1 })
+          } else if (intent.intentType === 'B') {
+            uni.navigateTo({ url: '/pages/health/ai-questionnaire' })
+          } else if (intent.intentType === 'C') {
+            uni.navigateTo({ url: '/pages/problem/flow-intro?code=' + (intent.problemDomainCode || '') })
+          }
+        }
+      }).catch(function() {})
+    },
+    handleA() {
+      var self = this
+      self.loading = true
+      chooseUserIntent({ intentType: 'A' }).then(function(res) {
+        self.loading = false
+        if (res.code === 200) {
+          uni.navigateBack({ delta: 1 })
+        }
+      }).catch(function() {
+        self.loading = false
+        uni.navigateBack({ delta: 1 })
+      })
+    },
+    handleB() {
+      uni.navigateTo({ url: '/pages/health/ai-questionnaire' })
+    },
+    handleC() {
+      uni.navigateTo({ url: '/pages/problem/domain-list' })
+    },
+    handleSkip() {
+      var self = this
+      self.loading = true
+      skipUserIntent().then(function(res) {
+        self.loading = false
+        if (res.code === 200 && res.data) {
+          var skipCount = res.data.skipCount || 0
+          if (skipCount >= 3) {
+            uni.setStorageSync('intent_skip_max', '1')
+          }
+          uni.setStorageSync('intent_skipped_at', Date.now())
+          uni.showToast({ title: '已记录,下次再来吧', icon: 'none' })
+          setTimeout(function() { uni.navigateBack({ delta: 1 }) }, 800)
+        }
+      }).catch(function() {
+        self.loading = false
+        uni.setStorageSync('intent_skipped_at', Date.now())
+        uni.navigateBack({ delta: 1 })
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.page { min-height: 100vh; background: #F8F8F8; padding: 0 32rpx 48rpx; }
+.header { margin-top: 64rpx; margin-bottom: 40rpx; }
+.title { font-size: 40rpx; font-weight: bold; color: #1a1a1a; display: block; line-height: 1.3; }
+.desc { font-size: 26rpx; color: #888; margin-top: 12rpx; display: block; line-height: 1.5; }
+.card { display: flex; align-items: center; background: #fff; border-radius: 24rpx; padding: 32rpx 28rpx; margin-bottom: 20rpx; box-shadow: 0 2rpx 16rpx rgba(0,0,0,0.05); }
+.card:active { opacity: 0.85; }
+.card-icon { width: 88rpx; height: 88rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; flex-shrink: 0; }
+.icon-text { font-size: 40rpx; }
+.card-body { flex: 1; min-width: 0; }
+.card-title { font-size: 30rpx; font-weight: 600; color: #1a1a1a; display: block; }
+.card-desc { font-size: 24rpx; color: #888; margin-top: 6rpx; display: block; line-height: 1.4; }
+.card-arrow { font-size: 32rpx; color: #ccc; flex-shrink: 0; }
+.skip-btn { margin-top: 48rpx; text-align: center; padding: 24rpx 0; }
+.skip-text { font-size: 26rpx; color: #aaa; }
+</style>

+ 1 - 0
cfc-frontend/utils/api.js

@@ -2697,6 +2697,7 @@ export const getProblemDomainList = () => request('/api/problem-domain/list', 'P
 // 用户意图
 // 用户意图
 export const getUserIntent = () => request('/api/user-intent/get', 'POST', {})
 export const getUserIntent = () => request('/api/user-intent/get', 'POST', {})
 export const chooseUserIntent = (data) => request('/api/user-intent/choose', 'POST', data)
 export const chooseUserIntent = (data) => request('/api/user-intent/choose', 'POST', data)
+export const skipUserIntent = () => request('/api/user-intent/skip', 'POST', {})
 export const updateUserIntentStage = (journeyStage) => request('/api/user-intent/update-stage', 'POST', { journeyStage })
 export const updateUserIntentStage = (journeyStage) => request('/api/user-intent/update-stage', 'POST', { journeyStage })
 // 站内通知
 // 站内通知
 export const getNotificationList = () => request('/api/notification/list', 'POST', {})
 export const getNotificationList = () => request('/api/notification/list', 'POST', {})

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-d0126783a905c646de641ab2e38be65c533e7e32
+5a9db18986e9622ed773e27c4c76cab61c35fd43

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
 {
   "name": "cfc-web",
   "name": "cfc-web",
-  "version": "1.0.1080",
+  "version": "1.0.1081",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "cfc-web",
       "name": "cfc-web",
-      "version": "1.0.1080",
+      "version": "1.0.1081",
       "dependencies": {
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "cfc-web",
   "name": "cfc-web",
-  "version": "1.0.1081",
+  "version": "1.0.1082",
   "private": true,
   "private": true,
   "scripts": {
   "scripts": {
     "dev": "vue-cli-service serve",
     "dev": "vue-cli-service serve",

+ 16 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,22 @@
 
 
 ---
 ---
 
 
+## v1.0.1082 (2026-08-15)
+
+### Bug 修复
+- 报告详情页异常数量修复 + 指标自组装异常统计补齐
+- 修复动态菜单子菜单名称为空、无子菜单的一级菜单展开箭头问题
+
+### 其他
+-   仅 high/low/abnormal/偏高/偏低/异常/缺乏/不足/注意/高风险 计入异常数
+- - assembleBlocksFromPayload 生成 indicator 块时携带 abnormalCount
+- - report-blocks-renderer indicator 块标题行显示异常数量
+- 
+- - 模板: el-submenu 增加 children.length > 0 条件(无子节点时不渲染展开箭头)
+- - key/index: 补充 id 兜底,避免 path 为空时的渲染异常
+- 
+
+
 ## v1.0.1081 (2026-08-15)
 ## v1.0.1081 (2026-08-15)
 
 
 ### 新功能
 ### 新功能

+ 17 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 # 更新日志
 
 
-> 当前版本: v1.0.1081
+> 当前版本: v1.0.1082
 
 
 ## 历史版本
 ## 历史版本
 
 
@@ -8,6 +8,22 @@
 
 
 ---
 ---
 
 
+## v1.0.1082 (2026-08-15)
+
+### Bug 修复
+- 报告详情页异常数量修复 + 指标自组装异常统计补齐
+- 修复动态菜单子菜单名称为空、无子菜单的一级菜单展开箭头问题
+
+### 其他
+-   仅 high/low/abnormal/偏高/偏低/异常/缺乏/不足/注意/高风险 计入异常数
+- - assembleBlocksFromPayload 生成 indicator 块时携带 abnormalCount
+- - report-blocks-renderer indicator 块标题行显示异常数量
+- 
+- - 模板: el-submenu 增加 children.length > 0 条件(无子节点时不渲染展开箭头)
+- - key/index: 补充 id 兜底,避免 path 为空时的渲染异常
+- 
+
+
 ## v1.0.1081 (2026-08-15)
 ## v1.0.1081 (2026-08-15)
 
 
 ### 新功能
 ### 新功能