Quellcode durchsuchen

Merge branch 'cfclub' of http://git.iwintrue.com/liaoxg/cfc into cfclub

asus vor 1 Monat
Ursprung
Commit
01412b9a56

+ 3 - 0
cfc-backend/src/main/java/com/etotem/cfc/dto/FamilyMemberVO.java

@@ -69,4 +69,7 @@ public class FamilyMemberVO {
 
     /** 连续打卡天数 */
     private Integer streakDays;
+
+    /** 当前登录用户是否为家庭管理员(creatorId === userId),用于前端控制编辑权限 */
+    private Boolean isAdmin;
 }

+ 6 - 0
cfc-backend/src/main/java/com/etotem/cfc/service/FamilyMemberService.java

@@ -162,6 +162,11 @@ public class FamilyMemberService {
         Long familyId = user.getFamilyId();
         List<FamilyMemberVO> result = new ArrayList<>();
 
+        // 查询家庭创建者(管理员),用于下发 isAdmin 给前端控制编辑权限
+        Family family = familyMapper.selectById(familyId);
+        Long creatorId = (family != null) ? family.getCreatorId() : null;
+        boolean isAdmin = creatorId != null && creatorId.equals(userId);
+
         // 查询 family_members 表
         List<FamilyMember> familyMembers = familyMemberMapper.selectList(
                 new LambdaQueryWrapper<FamilyMember>().eq(FamilyMember::getFamilyId, familyId));
@@ -180,6 +185,7 @@ public class FamilyMemberService {
             vo.setId(fm.getId());
             vo.setSource("family_member");
             vo.setUserId(fm.getUserId());
+            vo.setIsAdmin(isAdmin);
             vo.setNickname(fm.getNickname());
             vo.setPhone(fm.getPhone());
             vo.setAvatar(fm.getAvatar());

+ 27 - 2
cfc-frontend/components/FamilyMemberStrip.vue

@@ -43,10 +43,18 @@
         <text class="action-item-icon">🔄</text>
         <text class="action-item-text">切换到该成员视角</text>
       </view>
-      <view class="action-item" @click="editMember">
+      <view class="action-item" @click="editMember" v-if="canEdit">
         <text class="action-item-icon">✏️</text>
         <text class="action-item-text">编辑资料</text>
       </view>
+      <view class="action-item" @click="questionnaireMember">
+        <text class="action-item-icon">📋</text>
+        <text class="action-item-text">关系问卷</text>
+      </view>
+      <view class="action-item" @click="interactionMember">
+        <text class="action-item-icon">💬</text>
+        <text class="action-item-text">记录互动</text>
+      </view>
       <view class="action-item" @click="callMember" v-if="actionMember && actionMember.phone">
         <text class="action-item-icon">📞</text>
         <text class="action-item-text">拨打电话</text>
@@ -83,6 +91,9 @@ export default {
     canSwitch: function() {
       return this.actionMember && this.actionMember.userId
     },
+    canEdit: function() {
+      return this.isParent && this.actionMember && !this.actionMember.userId && this.actionMember.source === 'family_member'
+    },
     canRemove: function() {
       return this.isParent && this.actionMember && !this.actionMember.isSelf
     }
@@ -110,7 +121,21 @@ export default {
       var member = this.actionMember
       this.closeMenu()
       if (member) {
-        uni.navigateTo({ url: '/pages/profile-extra/family-members?editId=' + member.id })
+        uni.navigateTo({ url: '/pages/family/add-member?memberId=' + member.id })
+      }
+    },
+    questionnaireMember: function() {
+      var member = this.actionMember
+      this.closeMenu()
+      if (member) {
+        uni.navigateTo({ url: '/pages/action-detail/relationship-questionnaire?memberId=' + member.id })
+      }
+    },
+    interactionMember: function() {
+      var member = this.actionMember
+      this.closeMenu()
+      if (member) {
+        uni.navigateTo({ url: '/pages/action-detail/interaction-log?memberId=' + member.id })
       }
     },
     callMember: function() {

+ 34 - 7
cfc-frontend/pages/family/add-member.vue

@@ -1,7 +1,7 @@
 <template>
 <view class="container">
 <view class="form-container">
-<view class="page-title">添加家庭成员</view>
+<view class="page-title">{{ isEdit ? '编辑成员' : '添加家庭成员' }}</view>
 
 <!-- 昵称(必填) -->
 <view class="form-item required">
@@ -87,7 +87,7 @@
 </template>
 
 <script>
-import { addFamilyMember } from '../../utils/api.js'
+import { addFamilyMember, updateFamilyMember } from '../../utils/api.js'
 import GenerationPicker from '../../components/GenerationPicker.vue'
 
 export default {
@@ -95,6 +95,8 @@ components: { GenerationPicker },
 data() {
 return {
 submitting: false,
+isEdit: false,
+editMemberId: null,
 form: {
 nickname: '',
 generationLevel: '',
@@ -138,9 +140,17 @@ age--
 return age >= 0 ? age + '岁' : ''
 }
 },
-onLoad() {
-var today = new Date()
-this.todayDate = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
+onLoad(options) {
+  var today = new Date()
+  this.todayDate = today.getFullYear() + '-' + String(today.getMonth() + 1).padStart(2, '0') + '-' + String(today.getDate()).padStart(2, '0')
+  if (options && options.memberId) {
+    this.isEdit = true
+    this.editMemberId = Number(options.memberId)
+    if (options.nickname) this.form.nickname = decodeURIComponent(options.nickname)
+    if (options.gender) this.form.gender = options.gender
+    if (options.phone) this.form.phone = options.phone
+    if (options.birthday) this.form.birthday = options.birthday
+  }
 },
 methods: {
 onGenderChange(e) {
@@ -169,7 +179,7 @@ if (!this.form.nickname || !this.form.nickname.trim()) {
 uni.showToast({ title: '请输入成员昵称', icon: 'none' })
 return
 }
-if (!this.$refs.generationPicker || !this.$refs.generationPicker.selectedGen) {
+if (!this.isEdit && (!this.$refs.generationPicker || !this.$refs.generationPicker.selectedGen)) {
   uni.showToast({ title: '请选择辈分关系', icon: 'none' })
   return
 }
@@ -188,6 +198,22 @@ return
 
 try {
 this.submitting = true
+if (this.isEdit) {
+  var updatePayload = {
+    memberId: this.editMemberId,
+    nickname: this.form.nickname.trim()
+  }
+  if (this.form.gender) updatePayload.gender = this.form.gender
+  if (this.form.birthday) updatePayload.birthday = this.form.birthday
+  if (this.form.phone) updatePayload.phone = this.form.phone
+  var res = await updateFamilyMember(updatePayload)
+  if (res.code === 200) {
+    uni.showToast({ title: '已更新', icon: 'success' })
+    setTimeout(function() { uni.navigateBack() }, 1500)
+  } else {
+    uni.showToast({ title: res.message || '更新失败', icon: 'none' })
+  }
+} else {
 var genLevel = (this.$refs.generationPicker && this.$refs.generationPicker.selectedGen) || this.form.generationLevel
 var genPeerType = (this.$refs.generationPicker && this.$refs.generationPicker.selectedPeer) || this.form.peerType
 var payload = {
@@ -219,8 +245,9 @@ setTimeout(function() { uni.navigateBack() }, 1500)
 } else {
 uni.showToast({ title: res.message || '添加失败', icon: 'none' })
 }
+}
 } catch (e) {
-uni.showToast({ title: '添加失败', icon: 'none' })
+uni.showToast({ title: this.isEdit ? '更新失败' : '添加失败', icon: 'none' })
 } finally {
 this.submitting = false
 }

+ 3 - 2
cfc-frontend/pages/profile-extra/family-members.vue

@@ -202,6 +202,7 @@ export default {
       this.showMemberDetailModal = true
     },
     canEditMember(member) {
+      if (!this.isAdmin) return false
       if (member.userId) return false
       return member.source === 'family_member'
     },
@@ -283,11 +284,11 @@ export default {
     },
     doQuestionnaire(member) {
       this.showMemberDetailModal = false
-      uni.navigateTo({ url: '/pages/action/relationship-questionnaire?memberId=' + member.id })
+      uni.navigateTo({ url: '/pages/action-detail/relationship-questionnaire?memberId=' + member.id })
     },
     recordInteraction(member) {
       this.showMemberDetailModal = false
-      uni.navigateTo({ url: '/pages/action/interaction-log?memberId=' + member.id })
+      uni.navigateTo({ url: '/pages/action-detail/interaction-log?memberId=' + member.id })
     },
     closeMemberDetail() {
       this.showMemberDetailModal = false

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-ab9f4a440002b06134bf2e4d9f9e5f55b98fec4a
+3a946d5685240376d24d6ecd3c8803be92aa5422

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

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

+ 1 - 1
cfc-web/package.json

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

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

@@ -4,6 +4,27 @@
 
 ---
 
+## v1.0.743 (2026-08-02)
+
+### 新功能
+- 前端接入小程序虚拟支付
+- 虚拟支付超时兜底任务 pending订单query_order确认+超时关单,防success回调丢失
+- 订阅订单改虚拟支付
+- 会员订单改虚拟支付 createOrder返回VirtualPayParams,发货推送复用processPaymentCallback,修正等级硬编码FAMILY
+- 测评订单改虚拟支付 payOrder返回VirtualPayParams保持pending,服务端计价,发货推送markPaid+预约确认+佣金
+- VirtualPayController 接收虚拟支付消息推送 /api/internal/virtual-pay/notify(公开) + ios-refund-query,返回微信ErrCode协议
+- VirtualPayService paySig签名/参数生成/发货退款推送处理+sessionKey持久化 JDK原生HMAC-SHA256零新依赖,双签名(paySig+signature),前缀路由,迁移144
+
+### 其他
+- 
+- 
+- 
+
+### Bug 修复
+- 更新VirtualPayServiceTest发货推送断言为handleVirtualPaySuccess路由
+- 虚拟支付Task4评审修复 sessionKey防序列化泄露/发货推送幂等/缺参ack/签名断言
+
+
 ## v1.0.741 (2026-08-02)
 
 ### Bug 修复

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

@@ -1,6 +1,6 @@
 # 更新日志
 
-> 当前版本: v1.0.741
+> 当前版本: v1.0.743
 
 ## 历史版本
 
@@ -8,6 +8,27 @@
 
 ---
 
+## v1.0.743 (2026-08-02)
+
+### 新功能
+- 前端接入小程序虚拟支付
+- 虚拟支付超时兜底任务 pending订单query_order确认+超时关单,防success回调丢失
+- 订阅订单改虚拟支付
+- 会员订单改虚拟支付 createOrder返回VirtualPayParams,发货推送复用processPaymentCallback,修正等级硬编码FAMILY
+- 测评订单改虚拟支付 payOrder返回VirtualPayParams保持pending,服务端计价,发货推送markPaid+预约确认+佣金
+- VirtualPayController 接收虚拟支付消息推送 /api/internal/virtual-pay/notify(公开) + ios-refund-query,返回微信ErrCode协议
+- VirtualPayService paySig签名/参数生成/发货退款推送处理+sessionKey持久化 JDK原生HMAC-SHA256零新依赖,双签名(paySig+signature),前缀路由,迁移144
+
+### 其他
+- 
+- 
+- 
+
+### Bug 修复
+- 更新VirtualPayServiceTest发货推送断言为handleVirtualPaySuccess路由
+- 虚拟支付Task4评审修复 sessionKey防序列化泄露/发货推送幂等/缺参ack/签名断言
+
+
 ## v1.0.741 (2026-08-02)
 
 ### Bug 修复