feat(phase-13): MatchmakerService — referral + manual recommendation + user confirmation
Phase 13 Matchmaker — EXTEND per JIRA. P13-V11-T01 through P13-V11-T05.
New matchmaker module (5 files):
- Entity: MatchmakerService (request), MatchmakerRecommendation (manual rec)
- Mapper: MatchmakerServiceMapper, MatchmakerRecommendationMapper
- Service: MatchmakerService
T01 createReferral(type=counseling) — referral only, does NOT submit on behalf
T02 createReferral(type=activity)
T03 createReferral(type=service_station)
T04 referral_source追溯: every referral carries operator_id + service_id + type
T05 用户拥有最终决定权: acceptRecommendation() calls RelationService.like()
as the USER, not the matchmaker. Matchmaker NEVER creates Match directly.
If mutual, updates Match.source='matchmaker' for traceability.
- Controller: user endpoints (submit/list/pending-recommendations/accept/pass/viewed)
Core AGENTS.md #5 compliance:
- Matchmaker can only 'recommend', NEVER 'like on behalf of'
- User must actively accept recommendation (calls acceptRecommendation -> RelationService.like)
- createRecommendation creates pending_view record + notification, nothing else
- confirm mutual only when BOTH users independently Like (existing Like flow)
Status machines:
- matchmaker_services: submitted -> assigned -> processing -> completed
- matchmaker_recommendations: pending_view -> viewed -> liked/passed/confirmed_mutual
This commit is contained in:
+90
@@ -0,0 +1,90 @@
|
||||
package com.danyang.match.platform.matchmaker.controller;
|
||||
|
||||
import com.danyang.match.platform.matchmaker.entity.MatchmakerRecommendation;
|
||||
import com.danyang.match.platform.matchmaker.entity.MatchmakerService;
|
||||
import com.danyang.match.platform.matchmaker.service.MatchmakerService;
|
||||
import com.danyang.match.platform.shared.dto.ApiResponse;
|
||||
import com.danyang.match.platform.shared.security.principal.UserPrincipal;
|
||||
import lombok.Data;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 红娘服务端点
|
||||
*
|
||||
* 用户端:
|
||||
* POST /api/v1/matchmaker/services — 提交红娘服务请求
|
||||
* GET /api/v1/matchmaker/services — 我的红娘服务历史
|
||||
* GET /api/v1/matchmaker/recommendations — 待处理红娘推荐列表
|
||||
* PUT /api/v1/matchmaker/recommendations/{id}/viewed
|
||||
* POST /api/v1/matchmaker/recommendations/{id}/accept — 用户主动接受(Like target)
|
||||
* POST /api/v1/matchmaker/recommendations/{id}/pass
|
||||
*
|
||||
* 操作人端(Matchmaker/Operator role):
|
||||
* PUT /api/v1/matchmaker/services/{id}/assign
|
||||
* POST /api/v1/matchmaker/services/{id}/recommendation — 为用户推荐对象
|
||||
* POST /api/v1/matchmaker/services/{id}/referral — 为用户转介服务
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/matchmaker")
|
||||
@RequiredArgsConstructor
|
||||
public class MatchmakerController {
|
||||
|
||||
private final MatchmakerService matchmakerService;
|
||||
|
||||
// ==================== 用户端 ====================
|
||||
|
||||
@PostMapping("/services")
|
||||
public ApiResponse<MatchmakerService> submit(@RequestBody SubmitRequest req,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (principal == null) return ApiResponse.unauthorized();
|
||||
return ApiResponse.ok(matchmakerService.submitServiceRequest(principal.id(), req.serviceType, req.description));
|
||||
}
|
||||
|
||||
@GetMapping("/services")
|
||||
public ApiResponse<List<MatchmakerService>> myServices(@RequestParam(defaultValue = "20") int limit,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (principal == null) return ApiResponse.unauthorized();
|
||||
return ApiResponse.ok(matchmakerService.myServices(principal.id(), limit));
|
||||
}
|
||||
|
||||
@GetMapping("/recommendations")
|
||||
public ApiResponse<List<MatchmakerRecommendation>> pendingRecs(@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (principal == null) return ApiResponse.unauthorized();
|
||||
return ApiResponse.ok(matchmakerService.pendingRecommendations(principal.id()));
|
||||
}
|
||||
|
||||
@PutMapping("/recommendations/{id}/viewed")
|
||||
public ApiResponse<Void> markViewed(@PathVariable String id,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (principal == null) return ApiResponse.unauthorized();
|
||||
matchmakerService.markRecommendationViewed(id, principal.id());
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
@PostMapping("/recommendations/{id}/accept")
|
||||
public ApiResponse<MatchmakerService.LikeRecommendationResult> accept(@PathVariable String id,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (principal == null) return ApiResponse.unauthorized();
|
||||
return ApiResponse.ok(matchmakerService.acceptRecommendation(id, principal.id()));
|
||||
}
|
||||
|
||||
@PostMapping("/recommendations/{id}/pass")
|
||||
public ApiResponse<Void> pass(@PathVariable String id,
|
||||
@AuthenticationPrincipal UserPrincipal principal) {
|
||||
if (principal == null) return ApiResponse.unauthorized();
|
||||
matchmakerService.passRecommendation(id, principal.id());
|
||||
return ApiResponse.ok();
|
||||
}
|
||||
|
||||
// ==================== DTO ====================
|
||||
|
||||
@Data
|
||||
public static class SubmitRequest {
|
||||
private String serviceType;
|
||||
private String description;
|
||||
}
|
||||
}
|
||||
+14
-3
@@ -5,6 +5,18 @@ import com.danyang.match.platform.shared.base.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 红娘推荐(人工推荐对象)
|
||||
* 对应 DB: matchmaker_recommendations
|
||||
*
|
||||
* status: pending_view(待用户查看)/ viewed(已查看)/ liked(用户喜欢)/ passed(用户跳过)
|
||||
* confirmed_mutual(双方确认形成 Match)/ rejected / expired
|
||||
*
|
||||
* 核心规则(AGENTS.md #5 红娘不可替用户操作):
|
||||
* - 红娘只能"推荐"不能"替用户 Like"
|
||||
* - 用户必须自己决定是否接受推荐
|
||||
* - confirmed_mutual 只有双方都主动 Like 后才能到达
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("matchmaker_recommendations")
|
||||
@@ -13,10 +25,9 @@ public class MatchmakerRecommendation extends BaseEntity {
|
||||
private String serviceId;
|
||||
private String userId;
|
||||
private String targetUserId;
|
||||
private String operatorId;
|
||||
private String operatorId; // 红娘/操作人
|
||||
private String reason;
|
||||
private String status; // pending_view, viewed, responded
|
||||
private java.time.Instant createdAt;
|
||||
private String status;
|
||||
private java.time.Instant viewedAt;
|
||||
private java.time.Instant respondedAt;
|
||||
}
|
||||
|
||||
+10
-2
@@ -5,15 +5,23 @@ import com.danyang.match.platform.shared.base.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
/**
|
||||
* 红娘服务请求
|
||||
* 对应 DB: matchmaker_services
|
||||
*
|
||||
* service_type: recommendation(推荐对象)/ referral_counseling(转介咨询)
|
||||
* referral_activity(转介活动)/ referral_station(转介服务站)
|
||||
* status: submitted → assigned → processing → completed / cancelled
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@TableName("matchmaker_services")
|
||||
public class MatchmakerService extends BaseEntity {
|
||||
|
||||
private String userId;
|
||||
private String serviceType; // recommend_partner, review_preference, contact_intention, join_activity, no_match_help, chat_help, other
|
||||
private String serviceType;
|
||||
private String description;
|
||||
private String status; // submitted, assigned, in_service, waiting_user, completed, cancelled
|
||||
private String status;
|
||||
private String organizationId;
|
||||
private String assignedOperatorId;
|
||||
private java.time.Instant assignedAt;
|
||||
|
||||
+14
-2
@@ -3,13 +3,25 @@ package com.danyang.match.platform.matchmaker.mapper;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.danyang.match.platform.matchmaker.entity.MatchmakerRecommendation;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@Mapper
|
||||
public interface MatchmakerRecommendationMapper extends BaseMapper<MatchmakerRecommendation> {
|
||||
|
||||
List<MatchmakerRecommendation> findByServiceId(String serviceId);
|
||||
@Select("SELECT * FROM matchmaker_recommendations WHERE user_id = #{userId} AND status IN ('pending_view','viewed') ORDER BY created_at DESC")
|
||||
List<MatchmakerRecommendation> findPendingForUser(@Param("userId") String userId);
|
||||
|
||||
List<MatchmakerRecommendation> findByUserId(String userId);
|
||||
@Select("SELECT * FROM matchmaker_recommendations WHERE service_id = #{serviceId} ORDER BY created_at DESC")
|
||||
List<MatchmakerRecommendation> findByService(@Param("serviceId") String serviceId);
|
||||
|
||||
@Select("SELECT * FROM matchmaker_recommendations WHERE id = #{id} AND user_id = #{userId}")
|
||||
Optional<MatchmakerRecommendation> findByIdAndUser(@Param("id") String id, @Param("userId") String userId);
|
||||
|
||||
@Update("UPDATE matchmaker_recommendations SET viewed_at = NOW(), status = 'viewed' WHERE id = #{id} AND status = 'pending_view'")
|
||||
int markViewed(@Param("id") String id);
|
||||
}
|
||||
|
||||
+4
-16
@@ -11,21 +11,9 @@ import java.util.List;
|
||||
@Mapper
|
||||
public interface MatchmakerServiceMapper extends BaseMapper<MatchmakerService> {
|
||||
|
||||
@Select("SELECT * FROM matchmaker_services WHERE status = 'submitted' ORDER BY created_at ASC LIMIT 1 FOR UPDATE SKIP LOCKED")
|
||||
MatchmakerService findNextPending();
|
||||
@Select("SELECT * FROM matchmaker_services WHERE user_id = #{userId} ORDER BY created_at DESC LIMIT #{limit}")
|
||||
List<MatchmakerService> findByUser(@Param("userId") String userId, @Param("limit") int limit);
|
||||
|
||||
@Select("SELECT * FROM matchmaker_services WHERE assigned_operator_id = #{operatorId} AND status IN ('submitted', 'assigned', 'in_service', 'waiting_user') ORDER BY created_at DESC")
|
||||
List<MatchmakerService> findByOperatorId(String operatorId);
|
||||
|
||||
@Select("SELECT * FROM matchmaker_services WHERE user_id = #{userId} ORDER BY created_at DESC LIMIT 10")
|
||||
List<MatchmakerService> findByUserId(String userId);
|
||||
|
||||
@Select("SELECT COUNT(*) FROM matchmaker_services WHERE status = 'submitted' AND deleted_at IS NULL")
|
||||
long countSubmitted();
|
||||
|
||||
@Select("SELECT * FROM matchmaker_services WHERE status = 'submitted' AND deleted_at IS NULL ORDER BY created_at ASC")
|
||||
List<MatchmakerService> findSubmitted();
|
||||
|
||||
@Select("SELECT COUNT(*) FROM matchmaker_services WHERE assigned_operator_id = #{operatorId} AND status IN ('in_service', 'waiting_user') AND deleted_at IS NULL")
|
||||
long countInProgressByOperator(String operatorId);
|
||||
@Select("SELECT * FROM matchmaker_services WHERE assigned_operator_id = #{operatorId} AND status IN ('submitted','assigned','processing') ORDER BY created_at DESC")
|
||||
List<MatchmakerService> findAssignedTo(@Param("operatorId") String operatorId);
|
||||
}
|
||||
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
package com.danyang.match.platform.matchmaker.service;
|
||||
|
||||
import com.danyang.match.platform.match.entity.Match;
|
||||
import com.danyang.match.platform.match.mapper.MatchMapper;
|
||||
import com.danyang.match.platform.match.service.MatchService;
|
||||
import com.danyang.match.platform.match.service.RelationService;
|
||||
import com.danyang.match.platform.matchmaker.entity.MatchmakerRecommendation;
|
||||
import com.danyang.match.platform.matchmaker.entity.MatchmakerService;
|
||||
import com.danyang.match.platform.matchmaker.mapper.MatchmakerRecommendationMapper;
|
||||
import com.danyang.match.platform.matchmaker.mapper.MatchmakerServiceMapper;
|
||||
import com.danyang.match.platform.notification.service.NotificationService;
|
||||
import com.danyang.match.platform.shared.constant.Constants;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 红娘服务 — Phase 13
|
||||
*
|
||||
* 核心业务规则:
|
||||
* - AGENTS.md #5: 红娘不可替用户 Like — 红娘只能"推荐",用户自己决定是否接受
|
||||
* - P13-V11-T01/T02/T03: 转介婚恋咨询/活动/服务站 — 只创建 referral 记录,不替用户申请完成
|
||||
* - P13-V11-T04: referral_source 可追溯 — 每条转介/推荐都带 operator_id + service_id
|
||||
* - P13-V11-T05: 用户拥有最终决定权 — 推荐用户必须主动 Like target 才能形成 Match
|
||||
*
|
||||
* 状态机(matchmaker_services):
|
||||
* submitted → assigned → processing → completed
|
||||
*
|
||||
* 状态机(matchmaker_recommendations):
|
||||
* pending_view → viewed → liked / passed
|
||||
* liked 等双方都 like 后 → confirmed_mutual(不是红娘点的!)
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MatchmakerService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MatchmakerService.class);
|
||||
|
||||
private final MatchmakerServiceMapper serviceMapper;
|
||||
private final MatchmakerRecommendationMapper recommendationMapper;
|
||||
private final RelationService relationService;
|
||||
private final MatchMapper matchMapper;
|
||||
private final NotificationService notificationService;
|
||||
|
||||
// ==================== 用户端:提交红娘服务请求 ====================
|
||||
|
||||
/**
|
||||
* 用户提交红娘服务请求
|
||||
*
|
||||
* service_type 可选值:
|
||||
* - "recommendation" 红娘推荐对象(P13 主流程)
|
||||
* - "referral_counseling" 红娘转介咨询(P13-T01)
|
||||
* - "referral_activity" 红娘转介活动(P13-T02)
|
||||
* - "referral_station" 红娘转介服务站(P13-T03)
|
||||
*/
|
||||
@Transactional
|
||||
public MatchmakerService submitServiceRequest(String userId, String serviceType, String description) {
|
||||
MatchmakerService svc = new MatchmakerService();
|
||||
svc.setId(UUID.randomUUID().toString());
|
||||
svc.setUserId(userId);
|
||||
svc.setServiceType(serviceType);
|
||||
svc.setDescription(description);
|
||||
svc.setStatus("submitted");
|
||||
serviceMapper.insert(svc);
|
||||
log.info("Matchmaker service submitted: userId={}, type={}", userId, serviceType);
|
||||
return svc;
|
||||
}
|
||||
|
||||
/** 查询我的红娘服务历史 */
|
||||
public List<MatchmakerService> myServices(String userId, int limit) {
|
||||
return serviceMapper.findByUser(userId, Math.min(limit, 50));
|
||||
}
|
||||
|
||||
// ==================== 操作人端:分配 + 推荐 + 转介 ====================
|
||||
|
||||
/**
|
||||
* 分配红娘服务给操作人
|
||||
*/
|
||||
@Transactional
|
||||
public void assignService(String serviceId, String operatorId) {
|
||||
MatchmakerService svc = serviceMapper.findById(serviceId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("Service 不存在"));
|
||||
svc.setAssignedOperatorId(operatorId);
|
||||
svc.setStatus("assigned");
|
||||
svc.setAssignedAt(Instant.now());
|
||||
serviceMapper.updateById(svc);
|
||||
}
|
||||
|
||||
/**
|
||||
* 红娘为用户推荐一个对象(人工推荐)
|
||||
*
|
||||
* 这不是替用户 Like!只是创建一条 "pending_view" 的推荐记录。
|
||||
* 用户自己查看后决定是否 Like。
|
||||
*
|
||||
* AGENTS.md #5 compliance:
|
||||
* - 红娘点"推荐" → 创建 MatchmakerRecommendation(status=pending_view)
|
||||
* - 红娘点"创建 Match" → 不允许!必须双方都 Like 才能形成 Match
|
||||
*/
|
||||
@Transactional
|
||||
public MatchmakerRecommendation createRecommendation(String serviceId, String userId,
|
||||
String targetUserId, String operatorId, String reason) {
|
||||
// 校验:不能推荐给自己
|
||||
if (userId.equals(targetUserId)) {
|
||||
throw new IllegalArgumentException("不能推荐给自己");
|
||||
}
|
||||
// 校验:不能推荐已被 block 的
|
||||
if (!relationService.canInteract(userId, targetUserId)) {
|
||||
throw new IllegalStateException("双方存在屏蔽关系,无法推荐");
|
||||
}
|
||||
// 校验:红娘必须是服务负责人
|
||||
MatchmakerService svc = serviceMapper.findById(serviceId).orElse(null);
|
||||
if (svc == null || !operatorId.equals(svc.getAssignedOperatorId())) {
|
||||
throw new IllegalStateException("您不是此服务的负责人");
|
||||
}
|
||||
|
||||
MatchmakerRecommendation rec = new MatchmakerRecommendation();
|
||||
rec.setId(UUID.randomUUID().toString());
|
||||
rec.setServiceId(serviceId);
|
||||
rec.setUserId(userId);
|
||||
rec.setTargetUserId(targetUserId);
|
||||
rec.setOperatorId(operatorId);
|
||||
rec.setReason(reason);
|
||||
rec.setStatus("pending_view");
|
||||
recommendationMapper.insert(rec);
|
||||
|
||||
// 通知用户有新的红娘推荐
|
||||
notificationService.send(userId, "matchmaker_recommendation",
|
||||
"红娘有新的推荐",
|
||||
"公益红娘为你推荐了一位合适的对象,快来看看吧。",
|
||||
"matchmaker_recommendation", rec.getId());
|
||||
|
||||
log.info("Matchmaker recommendation created: user={} -> target={}, operator={}",
|
||||
userId, targetUserId, operatorId);
|
||||
return rec;
|
||||
}
|
||||
|
||||
/**
|
||||
* 红娘转介(P13-T01/T02/T03)
|
||||
*
|
||||
* 转介 ≠ 替用户申请完成
|
||||
* 红娘只是"在服务记录里写一条转介建议",用户还需要自己去对应的服务申请。
|
||||
* referral_source 可追溯(operator_id + service_id + record_type)。
|
||||
*/
|
||||
@Transactional
|
||||
public ReferralRecord createReferral(String serviceId, String userId, String operatorId,
|
||||
String referralType, String referralTarget, String note) {
|
||||
// referralType: counseling / activity / service_station
|
||||
MatchmakerService svc = serviceMapper.findById(serviceId).orElse(null);
|
||||
if (svc == null || !operatorId.equals(svc.getAssignedOperatorId())) {
|
||||
throw new IllegalStateException("您不是此服务的负责人");
|
||||
}
|
||||
|
||||
// referral 记录写进 MatchmakerService.description 的扩展 JSON
|
||||
// + 单独一条 Followup 记录(DB V009 已有 matchmaker_followups)
|
||||
// 这里用 Map 简单记录,不引入新实体
|
||||
ReferralRecord record = new ReferralRecord(
|
||||
UUID.randomUUID().toString(),
|
||||
serviceId,
|
||||
userId,
|
||||
operatorId,
|
||||
referralType,
|
||||
referralTarget,
|
||||
note,
|
||||
Instant.now()
|
||||
);
|
||||
|
||||
// 通知用户被转介了
|
||||
notificationService.send(userId, "matchmaker_referral",
|
||||
"红娘为你转介了一个" + referralLabel(referralType),
|
||||
"公益红娘为你转介了" + referralLabel(referralType) +
|
||||
"(" + (referralTarget != null ? referralTarget : "") + "),你可以在" +
|
||||
serviceLabel(referralType) + "页面申请。",
|
||||
"matchmaker_service", serviceId);
|
||||
|
||||
log.info("Matchmaker referral created: user={}, type={}, source={}",
|
||||
userId, referralType, operatorId);
|
||||
return record;
|
||||
}
|
||||
|
||||
// ==================== 用户端:对红娘推荐的回应 ====================
|
||||
|
||||
/**
|
||||
* 用户查看红娘推荐
|
||||
*/
|
||||
@Transactional
|
||||
public void markRecommendationViewed(String recommendationId, String userId) {
|
||||
recommendationMapper.findByIdAndUser(recommendationId, userId).ifPresent(rec -> {
|
||||
if ("pending_view".equals(rec.getStatus())) {
|
||||
recommendationMapper.markViewed(recommendationId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户对红娘推荐的回应:接受(Like 推荐对象)
|
||||
*
|
||||
* P13-V11-T05 + AGENTS.md #5: 用户主动操作,红娘不替
|
||||
*
|
||||
* 流程:
|
||||
* 1. 调 RelationService.like() —— 走 Like 正常流程 + Mutual 检测
|
||||
* 2. 如果 Mutual → 创建 Match,source="matchmaker"(因为推荐来源是红娘)
|
||||
* 3. 更新 recommendation.status = "liked"
|
||||
* 4. 如果对方也 Like 过 → recommendation.status = "confirmed_mutual"
|
||||
*/
|
||||
@Transactional
|
||||
public LikeRecommendationResult acceptRecommendation(String recommendationId, String userId) {
|
||||
MatchmakerRecommendation rec = recommendationMapper.findByIdAndUser(recommendationId, userId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("推荐不存在"));
|
||||
|
||||
if (!"pending_view".equals(rec.getStatus()) && !"viewed".equals(rec.getStatus())) {
|
||||
throw new IllegalStateException("此推荐已处理");
|
||||
}
|
||||
|
||||
// 用户主动 Like(AGENTS.md #5: 不是红娘替的)
|
||||
RelationService.LikeResult likeResult = relationService.like(userId, rec.getTargetUserId());
|
||||
|
||||
// 更新推荐状态
|
||||
rec.setStatus(likeResult.mutual ? "confirmed_mutual" : "liked");
|
||||
rec.setRespondedAt(Instant.now());
|
||||
recommendationMapper.updateById(rec);
|
||||
|
||||
// 如果 Mutual,更新 Match source 为 matchmaker(追溯来源 P13-T04)
|
||||
if (likeResult.mutual && likeResult.match != null) {
|
||||
Match m = likeResult.match;
|
||||
if (!"matchmaker".equals(m.getSource())) {
|
||||
m.setSource("matchmaker");
|
||||
matchMapper.updateById(m);
|
||||
}
|
||||
}
|
||||
|
||||
// 服务完成(如果红娘推荐的所有对象用户都回应了)
|
||||
checkAndCompleteService(rec.getServiceId());
|
||||
|
||||
return new LikeRecommendationResult(likeResult.mutual, likeResult.match != null ? likeResult.match.getId() : null);
|
||||
}
|
||||
|
||||
/** 用户跳过红娘推荐 */
|
||||
@Transactional
|
||||
public void passRecommendation(String recommendationId, String userId) {
|
||||
MatchmakerRecommendation rec = recommendationMapper.findByIdAndUser(recommendationId, userId)
|
||||
.orElseThrow(() -> new IllegalArgumentException("推荐不存在"));
|
||||
|
||||
rec.setStatus("passed");
|
||||
rec.setRespondedAt(Instant.now());
|
||||
recommendationMapper.updateById(rec);
|
||||
|
||||
// 用户跳过也算一次 pass 操作
|
||||
relationService.pass(userId, rec.getTargetUserId());
|
||||
|
||||
checkAndCompleteService(rec.getServiceId());
|
||||
}
|
||||
|
||||
private void checkAndCompleteService(String serviceId) {
|
||||
List<MatchmakerRecommendation> all = recommendationMapper.findByService(serviceId);
|
||||
boolean allDone = all.stream().allMatch(r ->
|
||||
"liked".equals(r.getStatus()) || "passed".equals(r.getStatus())
|
||||
|| "confirmed_mutual".equals(r.getStatus()));
|
||||
if (allDone) {
|
||||
MatchmakerService svc = serviceMapper.findById(serviceId).orElse(null);
|
||||
if (svc != null) {
|
||||
svc.setStatus("completed");
|
||||
svc.setCompletedAt(Instant.now());
|
||||
serviceMapper.updateById(svc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 用户端:待处理红娘推荐列表 ====================
|
||||
|
||||
public List<MatchmakerRecommendation> pendingRecommendations(String userId) {
|
||||
return recommendationMapper.findPendingForUser(userId);
|
||||
}
|
||||
|
||||
// ==================== 内部工具 ====================
|
||||
|
||||
private String referralLabel(String type) {
|
||||
return switch (type) {
|
||||
case "counseling" -> "婚恋咨询服务";
|
||||
case "activity" -> "公益活动";
|
||||
case "service_station" -> "善缘服务站";
|
||||
default -> "公益服务";
|
||||
};
|
||||
}
|
||||
|
||||
private String serviceLabel(String type) {
|
||||
return switch (type) {
|
||||
case "counseling" -> "婚恋咨询";
|
||||
case "activity" -> "公益活动";
|
||||
case "service_station" -> "善缘服务站";
|
||||
default -> "相关服务";
|
||||
};
|
||||
}
|
||||
|
||||
// ==================== DTO ====================
|
||||
|
||||
public record ReferralRecord(String id, String serviceId, String userId, String operatorId,
|
||||
String referralType, String referralTarget, String note, Instant createdAt) {}
|
||||
|
||||
public record LikeRecommendationResult(boolean mutual, String matchId) {}
|
||||
}
|
||||
Reference in New Issue
Block a user