157 lines
5.1 KiB
JavaScript
157 lines
5.1 KiB
JavaScript
/**
|
||||
|
|
* 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,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/** 拉取我的红娘服务列表 */
|
|||
|
|
async fetchMyServices(params = {}) {
|
|||
|
|
if (this.state.servicesLoading) return;
|
|||
|
|
this.setState({ servicesLoading: true, lastError: null });
|
|||
|
|
try {
|
|||
|
|
const result = await matchmakerService.getMyServices(params);
|
|||
|
|
const items = result.items || [];
|
|||
|
|
const active = items.find(
|
|||
|
|
(s) =>
|
|||
|
|
s.status !== MATCHMAKER_SERVICE_STATUS.COMPLETED &&
|
|||
|
|
s.status !== MATCHMAKER_SERVICE_STATUS.CANCELLED,
|
|||
|
|
) || null;
|
|||
|
|
this.setState({
|
|||
|
|
myServices: items,
|
|||
|
|
servicesCursor: result.nextCursor || null,
|
|||
|
|
servicesHasMore: !!result.nextCursor,
|
|||
|
|
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({
|
|||
|
|
myServices: [...this.state.myServices, ...(result.items || [])],
|
|||
|
|
servicesCursor: result.nextCursor || null,
|
|||
|
|
servicesHasMore: !!result.nextCursor,
|
|||
|
|
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);
|
|||
|
|
this.setState({
|
|||
|
|
recommendations: result.items || [],
|
|||
|
|
recommendationsCursor: result.nextCursor || null,
|
|||
|
|
recommendationsHasMore: !!result.nextCursor,
|
|||
|
|
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,
|
|||
|
|
});
|
|||
|
|
this.setState({
|
|||
|
|
recommendations: [...this.state.recommendations, ...(result.items || [])],
|
|||
|
|
recommendationsCursor: result.nextCursor || null,
|
|||
|
|
recommendationsHasMore: !!result.nextCursor,
|
|||
|
|
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;
|