Преглед изворни кода

feat(family): 邀请令牌复用同家庭有效令牌,组件邀请入口改跳成员管理页

generate接口优先复用同家庭未过期未用尽的令牌(仅未过期记录计入10条额度,避免分享频繁创建耗尽配额);FamilyMemberStrip移除海报弹窗改为原生分享路径;API文档同步

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
liaoxg пре 2 месеци
родитељ
комит
a5df5c503a

+ 17 - 2
cfc-backend/src/main/java/com/etotem/cfc/service/FamilyInvitationService.java

@@ -83,12 +83,27 @@ public class FamilyInvitationService {
             throw new RuntimeException("家庭不存在");
         }
 
-        long activeCount = familyInvitationMapper.selectCount(
+        // 查询该用户所有 active 状态的邀请(仅统计未过期的,过期记录不再占用额度)
+        Date now = new Date();
+        List<FamilyInvitation> activeInvitations = familyInvitationMapper.selectList(
                 new LambdaQueryWrapper<FamilyInvitation>()
                         .eq(FamilyInvitation::getCreatedBy, userId)
                         .eq(FamilyInvitation::getStatus, "active")
+                        .and(w -> w.isNull(FamilyInvitation::getExpiresAt)
+                                .or().gt(FamilyInvitation::getExpiresAt, now))
         );
-        if (activeCount >= 10) {
+
+        // 优先复用同家庭、未过期、未用尽的邀请令牌(小程序分享场景下避免每次分享都新建记录导致额度耗尽)
+        for (FamilyInvitation inv : activeInvitations) {
+            boolean sameFamily = familyId != null && familyId.equals(inv.getFamilyId());
+            boolean notExhausted = inv.getMaxUseCount() == null || inv.getUsedCount() == null
+                    || inv.getUsedCount() < inv.getMaxUseCount();
+            if (sameFamily && notExhausted) {
+                return inv.getToken();
+            }
+        }
+
+        if (activeInvitations.size() >= 10) {
             throw new RuntimeException("您已生成过多邀请链接,请先吊销未使用的链接");
         }
 

+ 5 - 41
cfc-frontend/components/FamilyMemberStrip.vue

@@ -117,26 +117,16 @@
         <view class="empty-btn" @click="onCreateFamily">+ 创建家庭</view>
       </view>
     </view>
-
-    <!-- 邀请成员分享海报弹窗 -->
-    <SharePoster
-      :show="showPoster"
-      :referralCode="inviteCode"
-      :qrCodeBase64="inviteQrCode"
-      :nickname="posterNickname"
-      :userAvatar="posterAvatar"
-      @close="showPoster = false" />
   </view>
 </template>
 
 <script>
 import FamilyMemberCard from '@/components/FamilyMemberCard'
-import SharePoster from '@/components/SharePoster.vue'
-import { kickFamilyMemberById, createFamily, generateFamilyInviteQrCode, getPairwiseRelationships, getFamilyMemberList } from '@/utils/api'
+import { kickFamilyMemberById, createFamily, getPairwiseRelationships, getFamilyMemberList } from '@/utils/api'
 import { parseDate } from '@/utils/format.js'
 
 export default {
-  components: { FamilyMemberCard, SharePoster },
+  components: { FamilyMemberCard },
   props: {
     members: { type: Array, default: function() { return [] } },
     selectedMemberId: { type: Number, default: null },
@@ -149,12 +139,7 @@ export default {
       showActionMenu: false,
       actionMember: null,
       renderedMembers: [],
-      creatingFamily: false,
-      showPoster: false,
-      inviteCode: '',
-      inviteQrCode: '',
-      posterNickname: '',
-      posterAvatar: ''
+      creatingFamily: false
     }
   },
   watch: {
@@ -350,31 +335,10 @@ export default {
       this.closeMenu()
       uni.navigateTo({ url: '/pages/family/add-member' })
     },
-    // 邀请成员:弹窗展示分享海报(与 family-members.vue onInvite 一致的模式)
+    // 邀请成员:跳转家庭成员管理页,使用小程序原生分享(转发邀请卡片给微信好友)
     inviteMember: function() {
-      var self = this
       this.closeMenu()
-      var userInfo = uni.getStorageSync('userInfo')
-      this.posterNickname = uni.getStorageSync('nickname') || (userInfo && userInfo.nickname) || ''
-      this.posterAvatar = uni.getStorageSync('avatar') || (userInfo && userInfo.avatar) || ''
-      this.inviteCode = ''
-      this.inviteQrCode = ''
-      uni.showLoading({ title: '生成邀请...' })
-      generateFamilyInviteQrCode()
-        .then(function(res) {
-          uni.hideLoading()
-          if (res.code === 200 && res.data) {
-            self.inviteCode = res.data.inviteCode
-            self.inviteQrCode = res.data.qrCodeBase64
-            self.showPoster = true
-          } else {
-            uni.showToast({ title: res.message || '生成失败', icon: 'none' })
-          }
-        })
-        .catch(function() {
-          uni.hideLoading()
-          uni.showToast({ title: '生成失败', icon: 'none' })
-        })
+      uni.navigateTo({ url: '/pages/profile-extra/family-members' })
     },
     loadPairwise: function(centerMemberId) {
       var self = this

+ 1 - 1
docs/superpowers/api/API_REFERENCE.md

@@ -169,7 +169,7 @@ find cfc-backend/src/main/java -name "*XxxService.java" -o -name "*XxxController
 | `POST /api/family/invite/pending-requests` | 待审批列表 | — |
 | `POST /api/family/invite/my-request` | 我的申请 | — |
 | `POST /api/family/invite/cancel-request` | 取消申请 | — |
-| `POST /api/family/invite/generate` | 生成邀请令牌 | — |
+| `POST /api/family/invite/generate` | 生成邀请令牌(复用同家庭未过期未用尽的有效令牌;仅未过期记录计入 10 条额度) | — |
 | `POST /api/family/invite/validate` | 验证邀请令牌 | — |
 | `POST /api/family/invite/accept` | 接受邀请 | — |
 | `POST /api/family/invite/check-family` | 检查家庭状态 | — |