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

feat: 心页面添加心理报告卡片 - 完整EMID四维条形图展示

Xiaogang Liao 2 месяцев назад
Родитель
Сommit
e5d21e3cf1

+ 110 - 0
BOOT-INF/classes/application.yml

@@ -0,0 +1,110 @@
+server:
+  port: 9082
+  tomcat:
+    headers:
+      X-Content-Type-Options: nosniff
+      X-Frame-Options: DENY
+      X-XSS-Protection: 1; mode=block
+      Strict-Transport-Security: max-age=31536000; includeSubDomains
+
+spring:
+  servlet:
+    multipart:
+      max-file-size: 50MB
+      max-request-size: 50MB
+  application:
+    name: cfc-backend
+  datasource:
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://192.168.16.251:3306/zxyj?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true
+    username: zxyj
+    password: zxyj@123
+    hikari:
+      maximum-pool-size: 50
+      minimum-idle: 10
+      connection-timeout: 30000
+      idle-timeout: 600000
+      max-lifetime: 1800000
+      pool-name: CfcHikariPool
+
+sfms:
+  datasource:
+    enabled: false  # 本地开发禁用 SFMS 数据源,需要迁移时改为 true
+    driver-class-name: com.mysql.cj.jdbc.Driver
+    url: jdbc:mysql://192.168.16.251:3306/zxyj?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true&createDatabaseIfNotExist=true
+    username: zxyj
+    password: zxyj@123
+
+mybatis-plus:
+  mapper-locations: classpath*:/mapper/**/*.xml
+  type-aliases-package: com.etotem.cfc.entity
+  configuration:
+    map-underscore-to-camel-case: true
+    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
+  global-config:
+    db-config:
+      id-type: auto
+      logic-delete-field: deleted
+      logic-delete-value: 1
+      logic-not-delete-value: 0
+
+jwt:
+  secret: cfc-secret-key-2026-spring-boot-jwt-token
+  expiration: 86400000
+
+zkxz:
+  enabled: false
+  sync-api-url: https://api.zkxz.org.cn/api/
+  login-url: member/login
+  sync-client-url: user/create
+  list-client-url: user/list
+  sync-teacher-url: teacher/create
+  user-report: user/report
+  report-info: report/info
+  zx-user: ""
+  zx-pass: ""
+
+wechat:
+  appid: wx5ba8038ef16fb245
+  secret: 3dae525c6f5e010cc034f59abbd1b1cc
+  mch-id: 1703255039
+  mch-key: wx5ba8038ef16fb245wx5ba8038ef16f
+  api-v3-key: wx5ba8038ef16fb245wx5ba8038ef16f
+  mch-serial-no: 53C2A906094D725BC350A3414AADCA6D79882AAF
+  private-key-path: cert/apiclient_key.pem
+  notify-url: http://cfc.bianwoyou.com.cn/api/payment/notify
+  login-url: https://api.weixin.qq.com/sns/jscode2session
+  phone-url: https://api.weixin.qq.com/wxa/business/getuserphonenumber
+  test-mode: false  # 测试模式:跳过微信API调用,使用模拟数据
+  test-phone: "13800138000"  # 测试模式使用的手机号
+  alert-template-id: ${WECHAT_ALERT_TEMPLATE_ID:}
+  promotion-tier-template-id: ${WECHAT_PROMOTION_TIER_TEMPLATE_ID:}
+  # 小程序码环境:dev=development / test=trial / prod=release
+  env-version: ${WECHAT_ENV_VERSION:release}
+
+dify:
+  base-url: http://dify.bianwoyou.cn/v1
+  api-key: app-rtNSAHG2NFzRsXCGkeZlVNR9
+  nutrition-api-key: app-OnHzTiI6EULUfLyceCDqJkQu  # 精准营养助手
+  tongue-api-key: ""  # 舌诊分析,空字符串=mock模式
+  callback-secret: dify-callback-secret  # Dify Workflow HTTP回调认证密钥
+
+math:
+  verify-mode: dify  # dify | eval; dify=走Dify工作流, eval=服务端计算(兜底)
+  workflow-id: ""    # Dify 数学判题工作流 ID(留空则使用 eval 兜底)
+
+upload:
+  base-dir: /data/cfc-uploads
+  allowed-types: image/jpeg,image/png,image/gif,image/webp
+
+shop:
+  order:
+    payment-timeout-minutes: 30  # 订单超时时间(分钟)
+
+logging:
+  level:
+    com.etotem.cfc: debug
+
+# Admin 登录配置
+admin:
+  skip-captcha: true  # 测试环境跳过验证码,方便自动化测试

+ 17 - 0
cfc-frontend/pages/ai/chat.vue

@@ -738,6 +738,23 @@ export default {
   gap: 12rpx;
 }
 
+/* 任务卡片列表 */
+.task-list {
+  border-top: 1rpx solid var(--border-light, #E2E8F0);
+  padding-top: 16rpx;
+  display: flex;
+  flex-direction: column;
+  gap: 12rpx;
+}
+
+/* 报告来源标识 */
+.source-label {
+  margin-top: 16rpx;
+  font-size: 22rpx;
+  color: var(--muted, #94A3B8);
+  text-align: right;
+}
+
 .rec-card {
   display: flex;
   flex-direction: row;

+ 111 - 0
cfc-frontend/pages/mind/index.vue

@@ -1400,6 +1400,117 @@ export default {
   margin-left: 8rpx;
 }
 
+/* ===== EMI 心理报告卡片 ===== */
+.emi-card {
+  margin: 30rpx;
+  background: #fff;
+  border-radius: 24rpx;
+  padding: 30rpx;
+  box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.06);
+}
+.emi-header {
+  margin-bottom: 24rpx;
+}
+.emi-title {
+  font-size: 30rpx;
+  font-weight: bold;
+  color: #333;
+  display: block;
+}
+.emi-overall-wrap {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 32rpx 0;
+  background: linear-gradient(135deg, #FF6B35, #FFD700);
+  border-radius: 16rpx;
+  margin: 20rpx 0;
+  color: #fff;
+}
+.emi-overall-label {
+  font-size: 26rpx;
+  margin-bottom: 6rpx;
+}
+.emi-overall-value {
+  font-size: 64rpx;
+  font-weight: bold;
+  margin-bottom: 6rpx;
+}
+.emi-overall-level {
+  font-size: 24rpx;
+  padding: 4rpx 16rpx;
+  border-radius: 12rpx;
+  background: rgba(255,255,255,0.2);
+}
+.emi-dimensions {
+  display: flex;
+  flex-direction: column;
+  gap: 20rpx;
+}
+.emi-dimension {
+  display: flex;
+  flex-direction: column;
+}
+.emi-dim-header {
+  display: flex;
+  flex-direction: row;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 8rpx;
+}
+.emi-dim-label {
+  font-size: 26rpx;
+  color: #666;
+}
+.emi-dim-values {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+}
+.emi-dim-score {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #FF6B35;
+  margin-right: 12rpx;
+}
+.emi-dim-level {
+  font-size: 20rpx;
+  padding: 2rpx 12rpx;
+  border-radius: 8rpx;
+}
+.emi-dim-bar {
+  height: 14rpx;
+  background: #f0f0f0;
+  border-radius: 7rpx;
+  overflow: hidden;
+}
+.emi-dim-bar-fill {
+  height: 100%;
+  border-radius: 7rpx;
+  transition: width 0.5s;
+}
+.emi-footer {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  padding-top: 20rpx;
+  border-top: 1rpx solid #f0f0f0;
+  margin-top: 20rpx;
+}
+.emi-footer-text {
+  font-size: 24rpx;
+  color: #F97316;
+  font-weight: 500;
+}
+.emi-footer-icon {
+  font-size: 24rpx;
+  margin-left: 8rpx;
+}
+.level-excellent { color: #10B981; background: #ECFDF5; }
+.level-good { color: #3B82F6; background: #EFF6FF; }
+.level-average { color: #F97316; background: #FFF7ED; }
+.level-low { color: #EF4444; background: #FEF2F2; }
+
 /* 雷达图内的空状态 */
 .placeholder-content {
   display: flex;

+ 59 - 0
docs/superpowers/plans/2026-07-19-report-upload-purchase-links.md

@@ -0,0 +1,59 @@
+# 报告上传页购买链接 实施计划
+
+> **For agentic workers:** Use subagent-driven-development or executing-plans to implement this plan task-by-task.
+
+**Goal:** 在报告上传页面(健康报告 + DAN 测评)的类型选择区下方添加同类检测服务购买推荐
+
+**Architecture:** 前端调用已有 `POST /api/product/list` API(支持 `domain` + `productType` 过滤),展示匹配维度的测评商品卡片。无需后端改动。
+
+**Tech Stack:** uni-app Vue 2 (Options API), 微信小程序
+
+## Global Constraints
+
+- 禁止可选链 `?.`(用 `&&` 替代)
+- 禁止 CSS Grid(用 flexbox)
+- Vue 2 Options API
+- 调用 `POST /api/product/list` 传入 `{ domain, productType: 'assessment', size: 4 }`
+- 报告类型→domain 映射: `physical_exam/gut_flora/tongue → body`, `mind → mind`, `wisdom → wisdom`
+- 商品卡片点击跳转 `/pages/discover/product-detail/product-detail?id={id}`
+
+---
+
+### Task 1: health/report-upload.vue 添加购买推荐
+
+**Files:**
+- Modify: `cfc-frontend/pages/health/report-upload.vue`
+
+**Interfaces:**
+- Consumes: `productList` from `../../utils/api.js`
+- Produces: 类型选择器下方出现 "为家人购买检测服务" 推荐区
+
+- [x] **Step 1: 在 import 中添加 productList**
+- [x] **Step 2: 在 data() 中添加推荐商品状态**
+- [x] **Step 3: 添加报告类型→domain 映射常量**
+- [x] **Step 4: 添加 loadRecommendProducts 方法**
+- [x] **Step 5: 在 selectType 中触发推荐加载**
+- [x] **Step 6: 在 onLoad 中加载初始推荐**
+- [x] **Step 7: 在模板中添加推荐商品卡片区**
+- [x] **Step 8: 添加 goToProduct 方法和 formatPriceWithSymbol**
+- [x] **Step 9: 添加推荐区样式**
+- [x] **Step 10: 验证无语法错误**
+
+### Task 2: dan-assessment/report-upload.vue 添加购买推荐
+
+**Files:**
+- Modify: `cfc-frontend/pages/dan-assessment/report-upload.vue`
+
+**Interfaces:**
+- Consumes: `productList` from `../../utils/api.js`
+- Produces: 维度选择器下方出现 "为家人购买检测服务" 推荐区
+
+- [ ] **Step 1: 在 import 中添加 productList**
+- [ ] **Step 2: 在 data() 中添加推荐商品状态**
+- [ ] **Step 3: 添加 REPORT_DOMAIN_MAP 常量和 loadRecommendProducts**
+- [ ] **Step 4: 在 dimension 选择时触发推荐**
+- [ ] **Step 5: 在 onLoad 中加载初始推荐**
+- [ ] **Step 6: 在 Step 1 的模板中插入推荐卡片区**
+- [ ] **Step 7: 添加 goToProduct 方法和 formatPriceWithSymbol**
+- [ ] **Step 8: 添加推荐区样式**
+- [ ] **Step 9: 验证无语法错误**