2026-09-09 22:05:00 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* MatchmakerStore(规范 §131)
|
|
|
|
|
|
*
|
|
|
|
|
|
* 职责:
|
|
|
|
|
|
* - 维护我申请的红娘服务列表
|
|
|
|
|
|
* - 维护红娘推荐列表(仅展示,最终 Like/Pass 走 matchStore)
|
|
|
|
|
|
* - 维护当前进行中的服务详情
|
|
|
|
|
|
*
|
|
|
|
|
|
* 不变量(AGENTS.md):
|
|
|
|
|
|
* - 第 5 条:红娘不可替用户 Like——本 Store 不暴露任何代用户 Like/Pass 的方法,
|
|
|
|
|
|
* 用户对红娘推荐做出选择时仍走 matchStore.like/pass(userId)。
|
|
|
|
|
|
* - 第 7 条:普通工作人员不能查看私人聊天——本 Store 无聊天接入。
|
|
|
|
|
|
* - 第 1 条:用户永久免费——红娘服务不引入任何"高级红娘/付费红娘"差异化。
|
|
|
|
|
|
*/
|
|
|
|
|
|
const BaseStore = require('./baseStore');
|
|
|
|
|
|
const { matchmakerService } = require('../services');
|
|
|
|
|
|
const { MATCHMAKER_SERVICE_STATUS } = require('../constants');
|
|
|
|
|
|
|
|
|
|
|
|
class MatchmakerStore extends BaseStore {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
super({
|
|
|
|
|
|
/** @type {import('../types').MatchmakerService[]} */
|
|
|
|
|
|
myServices: [],
|
|
|
|
|
|
servicesCursor: null,
|
|
|
|
|
|
servicesHasMore: true,
|
|
|
|
|
|
servicesLoading: false,
|
|
|
|
|
|
|
|
|
|
|
|
/** 当前进行中的服务(status != completed/cancelled) */
|
|
|
|
|
|
activeService: null,
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {import('../types').MatchmakerRecommendation[]} */
|
|
|
|
|
|
recommendations: [],
|
|
|
|
|
|
recommendationsCursor: null,
|
|
|
|
|
|
recommendationsHasMore: true,
|
|
|
|
|
|
recommendationsLoading: false,
|
|
|
|
|
|
|
|
|
|
|
|
lastError: null,
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-10-15 09:47:31 +08:00
|
|
|
|
/** 将 C 端服务实体字段统一为页面组件契约。 */
|
|
|
|
|
|
normalizeService(service) {
|
|
|
|
|
|
if (!service) return service;
|
|
|
|
|
|
return {
|
|
|
|
|
|
...service,
|
|
|
|
|
|
serviceId: service.serviceId || service.id,
|
|
|
|
|
|
requirement: service.requirement || service.description || '',
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-09 22:05:00 +08:00
|
|
|
|
/** 拉取我的红娘服务列表 */
|
|
|
|
|
|
async fetchMyServices(params = {}) {
|
|
|
|
|
|
if (this.state.servicesLoading) return;
|
|
|
|
|
|
this.setState({ servicesLoading: true, lastError: null });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await matchmakerService.getMyServices(params);
|
2026-10-15 09:47:31 +08:00
|
|
|
|
// C 端接口当前返回数组;兼容未来分页响应 { items, nextCursor }。
|
|
|
|
|
|
const items = (Array.isArray(result) ? result : (result.items || [])).map((item) => this.normalizeService(item));
|
2026-09-09 22:05:00 +08:00
|
|
|
|
const active = items.find(
|
|
|
|
|
|
(s) =>
|
|
|
|
|
|
s.status !== MATCHMAKER_SERVICE_STATUS.COMPLETED &&
|
|
|
|
|
|
s.status !== MATCHMAKER_SERVICE_STATUS.CANCELLED,
|
|
|
|
|
|
) || null;
|
|
|
|
|
|
this.setState({
|
|
|
|
|
|
myServices: items,
|
2026-10-15 09:47:31 +08:00
|
|
|
|
servicesCursor: Array.isArray(result) ? null : (result.nextCursor || null),
|
|
|
|
|
|
servicesHasMore: Array.isArray(result) ? false : !!result.nextCursor,
|
2026-09-09 22:05:00 +08:00
|
|
|
|
servicesLoading: false,
|
|
|
|
|
|
activeService: active,
|
|
|
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
this.setState({ servicesLoading: false, lastError: err });
|
|
|
|
|
|
throw err;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 加载更多服务 */
|
|
|
|
|
|
async loadMoreServices() {
|
|
|
|
|
|
if (this.state.servicesLoading || !this.state.servicesCursor) return;
|
|
|
|
|
|
this.setState({ servicesLoading: true });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await matchmakerService.getMyServices({
|
|
|
|
|
|
cursor: this.state.servicesCursor,
|
|
|
|
|
|
pageSize: 20,
|
|
|
|
|
|
});
|
|
|
|
|
|
this.setState({
|
2026-10-15 09:47:31 +08:00
|
|
|
|
myServices: [...this.state.myServices, ...(Array.isArray(result) ? result : (result.items || [])).map((item) => this.normalizeService(item))],
|
|
|
|
|
|
servicesCursor: Array.isArray(result) ? null : (result.nextCursor || null),
|
|
|
|
|
|
servicesHasMore: Array.isArray(result) ? false : !!result.nextCursor,
|
2026-09-09 22:05:00 +08:00
|
|
|
|
servicesLoading: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
this.setState({ servicesLoading: false, lastError: err });
|
|
|
|
|
|
throw err;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 申请红娘服务 */
|
|
|
|
|
|
async applyService(payload) {
|
|
|
|
|
|
const result = await matchmakerService.applyService(payload);
|
|
|
|
|
|
// 重新拉取列表以反映最新状态
|
|
|
|
|
|
await this.fetchMyServices();
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 拉取服务详情 */
|
|
|
|
|
|
async fetchServiceDetail(serviceId) {
|
|
|
|
|
|
return matchmakerService.getServiceDetail(serviceId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 拉取红娘推荐列表 */
|
|
|
|
|
|
async fetchRecommendations(params = {}) {
|
|
|
|
|
|
if (this.state.recommendationsLoading) return;
|
|
|
|
|
|
this.setState({ recommendationsLoading: true, lastError: null });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await matchmakerService.getRecommendations(params);
|
2026-10-15 09:47:31 +08:00
|
|
|
|
const items = Array.isArray(result) ? result : (result.items || []);
|
2026-09-09 22:05:00 +08:00
|
|
|
|
this.setState({
|
2026-10-15 09:47:31 +08:00
|
|
|
|
recommendations: items,
|
|
|
|
|
|
recommendationsCursor: Array.isArray(result) ? null : (result.nextCursor || null),
|
|
|
|
|
|
recommendationsHasMore: Array.isArray(result) ? false : !!result.nextCursor,
|
2026-09-09 22:05:00 +08:00
|
|
|
|
recommendationsLoading: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
this.setState({ recommendationsLoading: false, lastError: err });
|
|
|
|
|
|
throw err;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/** 加载更多红娘推荐 */
|
|
|
|
|
|
async loadMoreRecommendations() {
|
|
|
|
|
|
if (this.state.recommendationsLoading || !this.state.recommendationsCursor) return;
|
|
|
|
|
|
this.setState({ recommendationsLoading: true });
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await matchmakerService.getRecommendations({
|
|
|
|
|
|
cursor: this.state.recommendationsCursor,
|
|
|
|
|
|
pageSize: 20,
|
|
|
|
|
|
});
|
2026-10-15 09:47:31 +08:00
|
|
|
|
const items = Array.isArray(result) ? result : (result.items || []);
|
2026-09-09 22:05:00 +08:00
|
|
|
|
this.setState({
|
2026-10-15 09:47:31 +08:00
|
|
|
|
recommendations: [...this.state.recommendations, ...items],
|
|
|
|
|
|
recommendationsCursor: Array.isArray(result) ? null : (result.nextCursor || null),
|
|
|
|
|
|
recommendationsHasMore: Array.isArray(result) ? false : !!result.nextCursor,
|
2026-09-09 22:05:00 +08:00
|
|
|
|
recommendationsLoading: false,
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (err) {
|
|
|
|
|
|
this.setState({ recommendationsLoading: false, lastError: err });
|
|
|
|
|
|
throw err;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 对红娘推荐的用户表达 Like/Pass(实际走 matchStore)
|
|
|
|
|
|
* 调用方应直接调用 matchStore.like/pass(userId),本方法仅为统一入口的便利封装。
|
|
|
|
|
|
* 注意:本 Store 自身不替用户做决定,仅做接口转发(AGENTS 第 5 条)。
|
|
|
|
|
|
*/
|
|
|
|
|
|
async likeRecommendation(userId, matchStore) {
|
|
|
|
|
|
return matchStore.like(userId);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async passRecommendation(userId, matchStore) {
|
|
|
|
|
|
return matchStore.pass(userId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const matchmakerStore = new MatchmakerStore();
|
|
|
|
|
|
module.exports = matchmakerStore;
|