Commit 48c60a48 by luoqi

feat(persona): 性别特征(A.1.2,snapshot)

gender extractor:patient.gender 映射 M/男→男性、F/女→女性、其他→未知(照图,三值)。
注册表 spec + enum/label;来源可切(现 patient.gender,宿主给 client_gender 后切)。
本地 928:女 468 / 男 460,画像现役 5 特征(rfm/age_bracket/gender/dnc/entitlement)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
parent 19793597
......@@ -4,6 +4,7 @@ import { DoNotContactStatusFeatureExtractor } from './do-not-contact-status.feat
import { EntitlementStatusFeatureExtractor } from './entitlement-status.feature';
import { RfmFeatureExtractor } from './rfm.feature';
import { AgeBracketFeatureExtractor } from './age-bracket.feature';
import { GenderFeatureExtractor } from './gender.feature';
/**
* FeatureRegistry — 收集所有 PersonaFeature 提取器。
......@@ -20,9 +21,10 @@ export class FeatureRegistry {
constructor(
rfm: RfmFeatureExtractor,
age: AgeBracketFeatureExtractor,
gender: GenderFeatureExtractor,
dnc: DoNotContactStatusFeatureExtractor,
entitlement: EntitlementStatusFeatureExtractor,
) {
this.extractors = [rfm, age, dnc, entitlement];
this.extractors = [rfm, age, gender, dnc, entitlement];
}
}
import { Injectable } from '@nestjs/common';
import { PersonaFeatureKey } from '@pac/types';
import type {
FeatureExtractor,
FeatureExtractorContext,
PersonaFeatureDraft,
} from './feature.interface';
/**
* gender 性别(A.1.2)— 规则层,snapshot
*
* 口径(图):client_gender 直接映射 "M"/"男"→男性;"F"/"女"→女性;其他→未知。
* 数据来源:现 PAC patient.gender 自映射;宿主 CDP 给 client_gender 后切宿主值。
* 标签释义:客户生理性别,影响沟通话术与项目推荐。
* 三值标签(含未知)→ 始终打标签,不返回 null。
*/
@Injectable()
export class GenderFeatureExtractor implements FeatureExtractor {
readonly key = PersonaFeatureKey.GENDER;
private static map(raw: string | null): { code: string; zh: string } {
const s = String(raw ?? '').trim().toUpperCase();
if (s === 'M' || s === '男' || s === 'MALE') return { code: 'male', zh: '男性' };
if (s === 'F' || s === '女' || s === 'FEMALE') return { code: 'female', zh: '女性' };
return { code: 'unknown', zh: '未知' };
}
extract(ctx: FeatureExtractorContext): PersonaFeatureDraft {
const g = GenderFeatureExtractor.map(ctx.patient.gender);
return {
key: this.key,
description: g.zh,
score: null,
data: { gender: g.code, label: g.zh },
// 来自 patient 主档 gender(非 fact)
evidence: { factIds: [] },
};
}
}
......@@ -6,6 +6,7 @@ import { DoNotContactStatusFeatureExtractor } from './features/do-not-contact-st
import { EntitlementStatusFeatureExtractor } from './features/entitlement-status.feature';
import { RfmFeatureExtractor } from './features/rfm.feature';
import { AgeBracketFeatureExtractor } from './features/age-bracket.feature';
import { GenderFeatureExtractor } from './features/gender.feature';
@Module({
controllers: [PersonaController],
......@@ -15,6 +16,7 @@ import { AgeBracketFeatureExtractor } from './features/age-bracket.feature';
// W7:rfm 统一了旧 value/recall_risk/treatment_chain_status,三个旧 extractor 已摘除。
RfmFeatureExtractor,
AgeBracketFeatureExtractor,
GenderFeatureExtractor,
DoNotContactStatusFeatureExtractor,
EntitlementStatusFeatureExtractor,
],
......
......@@ -383,6 +383,7 @@ export const PersonaFeatureKey = {
RFM: 'rfm', // 价值分群(RFM 八象限 + 生命周期;data 带 segment 供圈人群)
// 规则层 · snapshot(从 birthDate 当下算)
AGE_BRACKET: 'age_bracket', // 年龄段(婴幼儿..老年,匹配适龄项目/家庭决策)
GENDER: 'gender', // 性别(男性/女性/未知,影响话术与项目推荐)
// v1 候选(规则路径,业务方反馈后逐步上)
ENTITLEMENT_STATUS: 'entitlement_status', // 权益身份(商保直付 / 医保 / 储值 / 私行;事实投影型,史+最近日期)
......
......@@ -40,6 +40,7 @@ export const planScenarioLabel = (key: string): string =>
export const PERSONA_FEATURE_META: Record<string, { label: string; tone: Tone }> = {
[PersonaFeatureKey.RFM]: { label: '价值分群', tone: 'indigo' },
[PersonaFeatureKey.AGE_BRACKET]: { label: '年龄段', tone: 'sky' },
[PersonaFeatureKey.GENDER]: { label: '性别', tone: 'slate' },
[PersonaFeatureKey.VALUE]: { label: '患者价值', tone: 'indigo' },
[PersonaFeatureKey.TREATMENT_CHAIN_STATUS]: { label: '治疗链状态', tone: 'amber' },
[PersonaFeatureKey.RECALL_RISK]: { label: '流失风险', tone: 'emerald' },
......
......@@ -88,4 +88,19 @@ export const PERSONA_FEATURE_SPECS: Record<string, PersonaFeatureSpec> = {
owner: 'pac-algo',
version: 1,
},
// ── A.1.2 性别(业务 CDP 口径)──
gender: {
key: 'gender',
nameZh: '性别',
tier: 'rule',
timeSemantics: 'snapshot',
labelValues: ['男性', '女性', '未知'],
dataSource: '现:PAC patient.gender 自映射;未来:宿主 CDP client_gender 直接取',
dataFields: ['client_gender'],
meaning: '客户生理性别,影响沟通话术与项目推荐',
algorithm: 'client_gender 直接映射:"M"/"男"→男性;"F"/"女"→女性;其他→未知',
owner: 'pac-algo',
version: 1,
},
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment