/** * ActivityStore(规范 §131) * * 职责: * - 维护活动列表(首页 + 我的活动) * - 维护活动报名状态 / 互选意向(AGENTS 第 6 条:单向意向保密) * - 维护我报名的活动集合,用于徽标与"我的活动"页 * * 不变量(AGENTS.md): * - 第 1 条:用户永久免费——活动 feeType=paid 字段仅作展示,前端不实现任何支付入口。 * - 第 6 条:活动单向意向保密——表达意向走 submitFeedback 的 interestTargetUserIds, * 其返回的 newMutuals 仅含双向匹配成功的人;未匹配的人不在前端可见; * Store 不缓存"谁单独选了我"的字段。 */ const BaseStore = require('./baseStore'); const { activityService } = require('../services'); const { ACTIVITY_STATUS, REGISTRATION_STATUS } = require('../constants'); class ActivityStore extends BaseStore { constructor() { super({ /** @type {import('../types').Activity[]} */ activities: [], activitiesCursor: null, activitiesHasMore: true, activitiesLoading: false, /** @type {import('../types').Activity[]} */ myActivities: [], myActivitiesCursor: null, myActivitiesHasMore: true, myActivitiesLoading: false, /** @type {Record} 活动详情缓存 */ detailCache: {}, /** @type {Record} activityId -> 报名状态 */ registrationStatusByActivity: {}, /** 我报名成功且未结束的活动数(徽标用) */ myActiveCount: 0, lastError: null, }); } /** 拉取活动列表(首页) */ async fetchActivities(params = {}) { if (this.state.activitiesLoading) return; this.setState({ activitiesLoading: true, lastError: null }); try { const result = await activityService.getActivities({ status: ACTIVITY_STATUS.REGISTRATION_OPEN, ...params, }); this.setState({ activities: result.items || [], activitiesCursor: result.nextCursor || null, activitiesHasMore: !!result.nextCursor, activitiesLoading: false, }); return result; } catch (err) { this.setState({ activitiesLoading: false, lastError: err }); throw err; } } /** 加载更多活动 */ async loadMoreActivities() { if (this.state.activitiesLoading || !this.state.activitiesCursor) return; this.setState({ activitiesLoading: true }); try { const result = await activityService.getActivities({ cursor: this.state.activitiesCursor, pageSize: 20, }); this.setState({ activities: [...this.state.activities, ...(result.items || [])], activitiesCursor: result.nextCursor || null, activitiesHasMore: !!result.nextCursor, activitiesLoading: false, }); } catch (err) { this.setState({ activitiesLoading: false, lastError: err }); throw err; } } /** 拉取我的活动 */ async fetchMyActivities(params = {}) { if (this.state.myActivitiesLoading) return; this.setState({ myActivitiesLoading: true, lastError: null }); try { const result = await activityService.getMyActivities(params); const items = result.items || []; this.setState({ myActivities: items, myActivitiesCursor: result.nextCursor || null, myActivitiesHasMore: !!result.nextCursor, myActivitiesLoading: false, myActiveCount: items.filter( (a) => a.registrationStatus === REGISTRATION_STATUS.APPROVED && a.status !== ACTIVITY_STATUS.ENDED && a.status !== ACTIVITY_STATUS.CANCELLED, ).length, }); return result; } catch (err) { this.setState({ myActivitiesLoading: false, lastError: err }); throw err; } } /** 加载更多我的活动 */ async loadMoreMyActivities() { if (this.state.myActivitiesLoading || !this.state.myActivitiesCursor) return; this.setState({ myActivitiesLoading: true }); try { const result = await activityService.getMyActivities({ cursor: this.state.myActivitiesCursor, pageSize: 20, }); this.setState({ myActivities: [...this.state.myActivities, ...(result.items || [])], myActivitiesCursor: result.nextCursor || null, myActivitiesHasMore: !!result.nextCursor, myActivitiesLoading: false, }); } catch (err) { this.setState({ myActivitiesLoading: false, lastError: err }); throw err; } } /** 拉取活动详情(优先取缓存) */ async fetchActivityDetail(activityId, { force = false } = {}) { if (!force && this.state.detailCache[activityId]) { return this.state.detailCache[activityId]; } const detail = await activityService.getActivityDetail(activityId); this.setState({ detailCache: { ...this.state.detailCache, [activityId]: detail }, registrationStatusByActivity: { ...this.state.registrationStatusByActivity, [activityId]: detail.registrationStatus, }, }); return detail; } /** * 报名活动 * @param {string} activityId * @param {{ remark?: string }} [payload] */ async register(activityId, payload) { const result = await activityService.register(activityId, payload); this.setState({ registrationStatusByActivity: { ...this.state.registrationStatusByActivity, [activityId]: REGISTRATION_STATUS.PENDING, }, }); return result; } /** * 提交活动反馈(含表达意向) * * 原有一个 `submitInterests(activityId, likedUserIds)` 已删除:它请求 * `POST /activities/{id}/interests` —— **后端没有这个端点**(Phase 16 把 * 表达意向合并进反馈流程,即本方法的 `interestTargetUserIds`),调用必然 404。 * 全仓已确认它没有任何页面调用方,属 Phase 16 前的设计残留。 * 若将来确实需要独立的"提交互选"接口,须由产品先定义语义再补后端端点, * 不应让前端留一个指向 404 的方法。 */ async submitFeedback(activityId, payload) { return activityService.submitFeedback(activityId, payload); } } const activityStore = new ActivityStore(); module.exports = activityStore;