CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
244 lines
8.4 KiB
JavaScript
244 lines
8.4 KiB
JavaScript
/**
|
||
* 用户资料详情页 U21
|
||
*
|
||
* 重构要点(遵循 AGENTS.md 不变量):
|
||
* - 数据获取:recommendationService.getUserDetail(API_INVENTORY 行 43),不再直连 api 模块
|
||
* - 喜欢:matchStore.like(mutualMatch=true 时跳转会话页,AGENTS 第 3/6 条)
|
||
* - 拉黑:blockUser 跨 Store 编排(MatchStore + MessageStore 同步停用,AGENTS 第 4 条)
|
||
* - 举报:跳转 report 页,不在本页处理
|
||
* - 敏感数据:profile.birthday/身份证/手机号 仅服务端持有,前端不缓存(AGENTS 第 8 条)
|
||
* - Required 不可放宽:本页只读展示,无任何放宽择偶条件的入口(AGENTS 第 2 条)
|
||
*/
|
||
const { matchStore, appStore, blockUser } = require('../../stores');
|
||
const { recommendationService } = require('../../services');
|
||
const { to, DANGER_ACTION, RELATION_STATUS, BUSINESS_ERROR_CODE, profileLabelOf, yesNoLabel, futureDanyangLabel } = require('../../constants');
|
||
|
||
Page({
|
||
data: {
|
||
userId: '',
|
||
profile: null,
|
||
loading: true,
|
||
loadError: '',
|
||
/** @type {import('../../types').RelationStatus} */
|
||
relationStatus: RELATION_STATUS.NEUTRAL,
|
||
isLiked: false,
|
||
isMutual: false,
|
||
canChat: false,
|
||
actionLoading: false,
|
||
statusBarHeight: 20,
|
||
navBarHeight: 44,
|
||
showBlockDialog: false,
|
||
blockAction: DANGER_ACTION.BLOCK,
|
||
/** 派生的展示字段 */
|
||
displayName: '',
|
||
ageText: '',
|
||
metaLine: '',
|
||
badges: [],
|
||
basicInfo: [],
|
||
attitudeInfo: [],
|
||
assistanceSubmitted: false,
|
||
},
|
||
|
||
onLoad(options) {
|
||
const userId = options.id || '';
|
||
if (!userId) {
|
||
this.setData({ loading: false, loadError: '用户不存在' });
|
||
return;
|
||
}
|
||
// 状态栏高度由 app.js 注入到全局,这里读一次
|
||
try {
|
||
const sysInfo = wx.getSystemInfoSync ? wx.getSystemInfoSync() : null;
|
||
if (sysInfo) {
|
||
this.setData({
|
||
statusBarHeight: sysInfo.statusBarHeight || 20,
|
||
navBarHeight: 44,
|
||
});
|
||
}
|
||
} catch (e) { /* 使用默认值 */ }
|
||
|
||
this.setData({ userId });
|
||
this.loadProfile();
|
||
},
|
||
|
||
async loadProfile() {
|
||
this.setData({ loading: true, loadError: '' });
|
||
try {
|
||
const user = await recommendationService.getUserDetail(this.data.userId);
|
||
this.setData({ loading: false, profile: user });
|
||
this._deriveDisplay(user);
|
||
} catch (err) {
|
||
this.setData({
|
||
loading: false,
|
||
loadError: (err && err.message) || '加载失败',
|
||
});
|
||
appStore.toastError(err, '加载用户资料失败');
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 把后端 User 字段派生为展示用结构,避免 wxml 内做条件拼接
|
||
*/
|
||
_deriveDisplay(user) {
|
||
if (!user) return;
|
||
const relationStatus = user.relationStatus || RELATION_STATUS.NEUTRAL;
|
||
const isLiked = relationStatus === RELATION_STATUS.LIKED || relationStatus === RELATION_STATUS.MUTUAL_MATCH;
|
||
const isMutual = relationStatus === RELATION_STATUS.MUTUAL_MATCH;
|
||
// AGENTS 第 3 条:只有 Mutual 后才能聊天
|
||
const canChat = isMutual && relationStatus !== RELATION_STATUS.BLOCKED;
|
||
|
||
const badges = [];
|
||
if (user.verified || user.isRealNameVerified) {
|
||
badges.push({ kind: 'success', label: '已实名' });
|
||
}
|
||
if (user.reviewed || user.profileReviewStatus === 'approved') {
|
||
badges.push({ kind: 'info', label: '资料已审核' });
|
||
}
|
||
|
||
const metaParts = [];
|
||
if (user.heightCm) metaParts.push(`${user.heightCm}cm`);
|
||
if (user.regionName) metaParts.push(user.regionName);
|
||
const eduLabel = profileLabelOf('educationCode', user.educationCode);
|
||
if (eduLabel) metaParts.push(eduLabel);
|
||
const occLabel = profileLabelOf('occupationCode', user.occupationCode);
|
||
if (occLabel) metaParts.push(occLabel);
|
||
|
||
const basicInfo = [];
|
||
const maritalLabel = profileLabelOf('maritalStatus', user.maritalStatus);
|
||
if (maritalLabel) basicInfo.push({ label: '婚姻状况', value: maritalLabel });
|
||
if (user.childrenStatus || user.hasChildren !== undefined) {
|
||
basicInfo.push({ label: '子女情况', value: user.childrenStatus ? profileLabelOf('childrenStatus', user.childrenStatus) : (user.hasChildren ? '有孩' : '无孩') });
|
||
}
|
||
if (eduLabel) basicInfo.push({ label: '学历', value: eduLabel });
|
||
if (occLabel) basicInfo.push({ label: '职业', value: occLabel });
|
||
const smokingLabel = profileLabelOf('smokingStatus', user.smokingStatus);
|
||
if (smokingLabel) basicInfo.push({ label: '吸烟', value: smokingLabel });
|
||
const drinkingLabel = profileLabelOf('drinkingStatus', user.drinkingStatus);
|
||
if (drinkingLabel) basicInfo.push({ label: '饮酒', value: drinkingLabel });
|
||
const longTermLabel = yesNoLabel(user.longTermDanyang);
|
||
if (longTermLabel) basicInfo.push({ label: '定居意愿', value: longTermLabel, full: true });
|
||
const futureLabel = futureDanyangLabel(user.futureDanyang);
|
||
if (futureLabel) basicInfo.push({ label: '未来规划', value: futureLabel, full: true });
|
||
|
||
const attitudeInfo = [];
|
||
// relationshipGoal / marriagePlan 由后端返回中文文案(结婚 / 恋爱 等),未知码原样透传
|
||
if (user.relationshipGoal) attitudeInfo.push({ label: '寻找', value: user.relationshipGoal, full: true });
|
||
if (user.marriagePlan) attitudeInfo.push({ label: '结婚计划', value: user.marriagePlan, full: true });
|
||
|
||
this.setData({
|
||
relationStatus,
|
||
isLiked,
|
||
isMutual,
|
||
canChat,
|
||
displayName: user.displayName || '用户',
|
||
ageText: user.age ? `${user.age}岁` : '',
|
||
metaLine: metaParts.join(' · '),
|
||
badges,
|
||
basicInfo,
|
||
attitudeInfo,
|
||
});
|
||
},
|
||
|
||
onBack() {
|
||
wx.navigateBack();
|
||
},
|
||
|
||
onRetry() {
|
||
this.loadProfile();
|
||
},
|
||
|
||
/**
|
||
* 喜欢(AGENTS 第 3/5/6 条:用户本人操作,mutualMatch 时跳聊天)
|
||
*/
|
||
async onLike() {
|
||
const { userId, actionLoading, isLiked } = this.data;
|
||
if (!userId || actionLoading || isLiked) return;
|
||
this.setData({ actionLoading: true });
|
||
try {
|
||
const result = await matchStore.like(userId);
|
||
// 本地派生状态更新
|
||
if (result && result.mutualMatch) {
|
||
this.setData({
|
||
isLiked: true,
|
||
isMutual: true,
|
||
canChat: true,
|
||
relationStatus: RELATION_STATUS.MUTUAL_MATCH,
|
||
actionLoading: false,
|
||
});
|
||
appStore.toastSuccess('双向意向匹配成功,可查看公益服务');
|
||
} else {
|
||
this.setData({
|
||
isLiked: true,
|
||
relationStatus: RELATION_STATUS.LIKED,
|
||
actionLoading: false,
|
||
});
|
||
appStore.toastSuccess('已表达喜欢');
|
||
}
|
||
} catch (err) {
|
||
this.setData({ actionLoading: false });
|
||
if (err && err.code === BUSINESS_ERROR_CODE.DUPLICATE_ACTION) {
|
||
appStore.toast('已表达过喜欢');
|
||
} else {
|
||
appStore.toastError(err, '操作失败');
|
||
}
|
||
}
|
||
},
|
||
|
||
onApplyMatchmaker() {
|
||
const { userId, actionLoading, assistanceSubmitted } = this.data;
|
||
if (!userId || actionLoading || assistanceSubmitted) return;
|
||
wx.navigateTo({
|
||
url: `/pages/matchmaker-authorization/matchmaker-authorization?targetUserId=${encodeURIComponent(userId)}`,
|
||
});
|
||
},
|
||
|
||
|
||
/**
|
||
* 举报:跳转 report 页,不在本页处理(AGENTS 第 7 条:举报内容不在此暴露)
|
||
*/
|
||
onReport() {
|
||
const { userId } = this.data;
|
||
if (!userId) return;
|
||
wx.navigateTo({ url: to.report(userId) });
|
||
},
|
||
|
||
/**
|
||
* 拉黑入口:弹出 DangerActionDialog 二次确认(AGENTS 第 4 条)
|
||
*/
|
||
onBlock() {
|
||
if (!this.data.userId) return;
|
||
this.setData({ showBlockDialog: true });
|
||
},
|
||
|
||
onConfirmBlock() {
|
||
this.setData({ showBlockDialog: false });
|
||
this._doBlock();
|
||
},
|
||
|
||
onCancelBlock() {
|
||
this.setData({ showBlockDialog: false });
|
||
},
|
||
|
||
/**
|
||
* 执行拉黑:调用跨 Store 编排 blockUser
|
||
* - MatchStore:从推荐/Mutual 列表移除
|
||
* - MessageStore:会话立即标记不可发消息
|
||
*/
|
||
async _doBlock() {
|
||
const { userId, actionLoading } = this.data;
|
||
if (!userId || actionLoading) return;
|
||
this.setData({ actionLoading: true });
|
||
try {
|
||
appStore.showLoading('处理中…');
|
||
await blockUser(userId, '用户主动拉黑');
|
||
appStore.hideLoading();
|
||
appStore.toastSuccess('已拉黑');
|
||
// 拉黑后离开当前页(AGENTS 第 4 条:Block 即时禁止互动)
|
||
setTimeout(() => wx.navigateBack(), 800);
|
||
} catch (err) {
|
||
appStore.hideLoading();
|
||
this.setData({ actionLoading: false });
|
||
appStore.toastError(err, '拉黑失败');
|
||
}
|
||
},
|
||
});
|