|
@@ -18,6 +18,11 @@ import java.util.HashMap;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
import com.etotem.cfc.util.SortUtil;
|
|
import com.etotem.cfc.util.SortUtil;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
|
|
|
|
+import com.etotem.cfc.entity.ButlerAssignment;
|
|
|
|
|
+import com.etotem.cfc.mapper.ButlerAssignmentMapper;
|
|
|
|
|
+import com.etotem.cfc.service.MemberSubscriptionService;
|
|
|
|
|
+import com.etotem.cfc.entity.MemberSubscription;
|
|
|
|
|
|
|
|
@Service
|
|
@Service
|
|
|
public class ButlerService {
|
|
public class ButlerService {
|
|
@@ -33,6 +38,12 @@ public class ButlerService {
|
|
|
@Resource
|
|
@Resource
|
|
|
private UserService userService;
|
|
private UserService userService;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private ButlerAssignmentMapper butlerAssignmentMapper;
|
|
|
|
|
+
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private MemberSubscriptionService memberSubscriptionService;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 申请成为管家
|
|
* 申请成为管家
|
|
|
* @param userId 申请人用户 ID
|
|
* @param userId 申请人用户 ID
|
|
@@ -325,4 +336,162 @@ public class ButlerService {
|
|
|
|
|
|
|
|
return butler.getUserId();
|
|
return butler.getUserId();
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 家庭端自助选择 ====================
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 可选管家列表(浏览开放,不校验会员等级)
|
|
|
|
|
+ * 条件:approved + tier=L2 + accepting=1 + 有余量;返回脱敏档案
|
|
|
|
|
+ */
|
|
|
|
|
+ public Result<List<Map<String, Object>>> getAvailableButlers() {
|
|
|
|
|
+ LambdaQueryWrapper<ButlerProfile> query = new LambdaQueryWrapper<>();
|
|
|
|
|
+ query.eq(ButlerProfile::getStatus, "approved")
|
|
|
|
|
+ .eq(ButlerProfile::getTier, "L2")
|
|
|
|
|
+ .eq(ButlerProfile::getAccepting, 1)
|
|
|
|
|
+ .apply("member_count < max_members")
|
|
|
|
|
+ .orderByAsc(ButlerProfile::getCreatedAt);
|
|
|
|
|
+ SortUtil.applySort(query);
|
|
|
|
|
+ List<ButlerProfile> profiles = butlerProfileMapper.selectList(query);
|
|
|
|
|
+
|
|
|
|
|
+ List<Long> userIds = new java.util.ArrayList<>();
|
|
|
|
|
+ for (ButlerProfile p : profiles) {
|
|
|
|
|
+ userIds.add(p.getUserId());
|
|
|
|
|
+ }
|
|
|
|
|
+ Map<Long, User> userMap = new HashMap<>();
|
|
|
|
|
+ if (!userIds.isEmpty()) {
|
|
|
|
|
+ for (User u : userMapper.selectBatchIds(userIds)) {
|
|
|
|
|
+ userMap.put(u.getId(), u);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> result = new java.util.ArrayList<>();
|
|
|
|
|
+ for (ButlerProfile p : profiles) {
|
|
|
|
|
+ User u = userMap.get(p.getUserId());
|
|
|
|
|
+ Map<String, Object> item = new HashMap<>();
|
|
|
|
|
+ item.put("butlerUserId", p.getUserId());
|
|
|
|
|
+ item.put("nickname", u != null ? u.getNickname() : "管家");
|
|
|
|
|
+ item.put("avatar", u != null ? u.getAvatar() : "");
|
|
|
|
|
+ item.put("description", p.getDescription());
|
|
|
|
|
+ item.put("certificates", p.getCertificates());
|
|
|
|
|
+ item.put("remainingSlots", (p.getMaxMembers() == null ? 0 : p.getMaxMembers())
|
|
|
|
|
+ - (p.getMemberCount() == null ? 0 : p.getMemberCount()));
|
|
|
|
|
+ result.add(item);
|
|
|
|
|
+ }
|
|
|
|
|
+ return Result.success(result);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 当前家庭的管家绑定信息(无绑定时 data 为 null)
|
|
|
|
|
+ */
|
|
|
|
|
+ public Result<Map<String, Object>> getMyButler(Long userId) {
|
|
|
|
|
+ Long familyId = requireFamilyId(userId);
|
|
|
|
|
+ if (familyId == null) {
|
|
|
|
|
+ return Result.error("未找到家庭信息");
|
|
|
|
|
+ }
|
|
|
|
|
+ ButlerAssignment assignment = butlerAssignmentMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerAssignment>()
|
|
|
|
|
+ .eq(ButlerAssignment::getFamilyId, familyId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (assignment == null) {
|
|
|
|
|
+ return Result.success(null);
|
|
|
|
|
+ }
|
|
|
|
|
+ return Result.success(buildButlerSummary(assignment));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 自助绑定/更换管家:L2 校验 + 条件扣容 + INSERT/UPDATE 双路径
|
|
|
|
|
+ */
|
|
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
|
|
+ public Result<Map<String, Object>> selectButler(Long userId, Long butlerUserId) {
|
|
|
|
|
+ Long familyId = requireFamilyId(userId);
|
|
|
|
|
+ if (familyId == null) {
|
|
|
|
|
+ return Result.error("未找到家庭信息");
|
|
|
|
|
+ }
|
|
|
|
|
+ // ① L2 有效订阅校验
|
|
|
|
|
+ MemberSubscription sub = memberSubscriptionService.getActiveSubscription(familyId);
|
|
|
|
|
+ boolean active = sub != null && sub.getExpireTime() != null
|
|
|
|
|
+ && sub.getExpireTime().after(new Date());
|
|
|
|
|
+ if (!active || !"L2".equals(sub.getLevel())) {
|
|
|
|
|
+ return Result.error("该功能面向久久一生(L2)会员家庭");
|
|
|
|
|
+ }
|
|
|
|
|
+ // ② 目标管家校验
|
|
|
|
|
+ ButlerProfile butler = butlerProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerProfile>()
|
|
|
|
|
+ .eq(ButlerProfile::getUserId, butlerUserId)
|
|
|
|
|
+ .eq(ButlerProfile::getStatus, "approved")
|
|
|
|
|
+ .eq(ButlerProfile::getTier, "L2")
|
|
|
|
|
+ .eq(ButlerProfile::getAccepting, 1)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (butler == null) {
|
|
|
|
|
+ return Result.error("该管家不可选择");
|
|
|
|
|
+ }
|
|
|
|
|
+ // ③ 条件原子扣容(防并发超卖)
|
|
|
|
|
+ LambdaUpdateWrapper<ButlerProfile> incr = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ incr.eq(ButlerProfile::getId, butler.getId())
|
|
|
|
|
+ .apply("member_count < max_members")
|
|
|
|
|
+ .setSql("member_count = member_count + 1");
|
|
|
|
|
+ int affected = butlerProfileMapper.update(null, incr);
|
|
|
|
|
+ if (affected == 0) {
|
|
|
|
|
+ return Result.error("该管家名额已满");
|
|
|
|
|
+ }
|
|
|
|
|
+ // ④ assignments INSERT/UPDATE 双路径(激活自动分配只加计数不建行)
|
|
|
|
|
+ ButlerAssignment existing = butlerAssignmentMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerAssignment>()
|
|
|
|
|
+ .eq(ButlerAssignment::getFamilyId, familyId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ if (existing != null) {
|
|
|
|
|
+ // 旧管家计数回退
|
|
|
|
|
+ LambdaUpdateWrapper<ButlerProfile> decr = new LambdaUpdateWrapper<>();
|
|
|
|
|
+ decr.eq(ButlerProfile::getUserId, existing.getButlerUserId())
|
|
|
|
|
+ .setSql("member_count = GREATEST(member_count - 1, 0)");
|
|
|
|
|
+ butlerProfileMapper.update(null, decr);
|
|
|
|
|
+
|
|
|
|
|
+ existing.setButlerUserId(butlerUserId);
|
|
|
|
|
+ existing.setLevel(sub.getLevel());
|
|
|
|
|
+ existing.setAssignedAt(new Date());
|
|
|
|
|
+ butlerAssignmentMapper.updateById(existing);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ ButlerAssignment na = new ButlerAssignment();
|
|
|
|
|
+ na.setFamilyId(familyId);
|
|
|
|
|
+ na.setButlerUserId(butlerUserId);
|
|
|
|
|
+ na.setSubscriptionId(sub.getId());
|
|
|
|
|
+ na.setLevel(sub.getLevel());
|
|
|
|
|
+ na.setAssignedAt(new Date());
|
|
|
|
|
+ butlerAssignmentMapper.insert(na);
|
|
|
|
|
+ }
|
|
|
|
|
+ log.info("Family {} selected butler {}, level={}", familyId, butlerUserId, sub.getLevel());
|
|
|
|
|
+
|
|
|
|
|
+ ButlerAssignment saved = butlerAssignmentMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerAssignment>()
|
|
|
|
|
+ .eq(ButlerAssignment::getFamilyId, familyId)
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ return Result.success(buildButlerSummary(saved));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * userId -> familyId(与 MembershipController 同源:users.family_id)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Long requireFamilyId(Long userId) {
|
|
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
|
|
+ return user != null ? user.getFamilyId() : null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 组装绑定摘要(脱敏:不含联系方式)
|
|
|
|
|
+ */
|
|
|
|
|
+ private Map<String, Object> buildButlerSummary(ButlerAssignment assignment) {
|
|
|
|
|
+ Map<String, Object> summary = new HashMap<>();
|
|
|
|
|
+ summary.put("butlerUserId", assignment.getButlerUserId());
|
|
|
|
|
+ summary.put("level", assignment.getLevel());
|
|
|
|
|
+ summary.put("assignedAt", assignment.getAssignedAt());
|
|
|
|
|
+ User bu = userMapper.selectById(assignment.getButlerUserId());
|
|
|
|
|
+ summary.put("nickname", bu != null ? bu.getNickname() : "管家");
|
|
|
|
|
+ summary.put("avatar", bu != null ? bu.getAvatar() : "");
|
|
|
|
|
+ ButlerProfile bp = butlerProfileMapper.selectOne(
|
|
|
|
|
+ new LambdaQueryWrapper<ButlerProfile>()
|
|
|
|
|
+ .eq(ButlerProfile::getUserId, assignment.getButlerUserId())
|
|
|
|
|
+ .last("LIMIT 1"));
|
|
|
|
|
+ summary.put("description", bp != null ? bp.getDescription() : "");
|
|
|
|
|
+ return summary;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|