@quantabit/profile-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1151 @@
1
+ 'use strict';
2
+
3
+ var sdkConfig = require('@quantabit/sdk-config');
4
+ var React = require('react');
5
+
6
+ /**
7
+ * Profile SDK - API 客户端
8
+ * 用户画像系统后端接口封装
9
+ *
10
+ * 使用 BaseApiClient 基类简化代码
11
+ */
12
+
13
+
14
+ /**
15
+ * 画像 API 客户端
16
+ */
17
+ class ProfileApiClient extends sdkConfig.BaseApiClient {
18
+ constructor(config = {}) {
19
+ super('/profile', config);
20
+ }
21
+
22
+ // ============ 用户画像 ============
23
+
24
+ /**
25
+ * 获取我的画像
26
+ */
27
+ async getMyProfile() {
28
+ return this.get('/my');
29
+ }
30
+
31
+ /**
32
+ * 获取用户画像
33
+ * @param {string} userId - 用户 ID
34
+ */
35
+ async getUserProfile(userId) {
36
+ return this.get(`/users/${userId}`);
37
+ }
38
+
39
+ /**
40
+ * 更新用户画像
41
+ * @param {string} userId - 用户 ID
42
+ * @param {Object} updates - 更新数据
43
+ */
44
+ async updateUserProfile(userId, updates) {
45
+ return this.put(`/users/${userId}`, updates);
46
+ }
47
+
48
+ /**
49
+ * 批量获取用户画像
50
+ * @param {string[]} userIds - 用户 ID 列表
51
+ */
52
+ async batchGetProfiles(userIds) {
53
+ return this.post('/users/batch', {
54
+ user_ids: userIds
55
+ });
56
+ }
57
+
58
+ // ============ 标签管理 ============
59
+
60
+ /**
61
+ * 获取用户标签
62
+ * @param {string} userId - 用户 ID
63
+ */
64
+ async getUserTags(userId) {
65
+ return this.get(`/users/${userId}/tags`);
66
+ }
67
+
68
+ /**
69
+ * 获取我的标签
70
+ */
71
+ async getMyTags() {
72
+ return this.get('/my/tags');
73
+ }
74
+
75
+ /**
76
+ * 添加用户标签
77
+ * @param {string} userId - 用户 ID
78
+ * @param {string[]} tags - 标签列表
79
+ */
80
+ async addUserTags(userId, tags) {
81
+ return this.post(`/users/${userId}/tags`, {
82
+ tags
83
+ });
84
+ }
85
+
86
+ /**
87
+ * 给当前用户添加标签
88
+ * @param {Object|string} tag - 标签对象或标签 ID
89
+ */
90
+ async addTag(tag) {
91
+ return this.post('/my/tags', {
92
+ tag
93
+ });
94
+ }
95
+
96
+ /**
97
+ * 移除用户标签
98
+ * @param {string} userId - 用户 ID
99
+ * @param {string[]} tags - 标签列表
100
+ */
101
+ async removeUserTags(userId, tags) {
102
+ return this.delete(`/users/${userId}/tags`, {
103
+ tags
104
+ });
105
+ }
106
+
107
+ /**
108
+ * 移除当前用户标签
109
+ * @param {string} tagId - 标签 ID
110
+ */
111
+ async removeTag(tagId) {
112
+ return this.delete(`/my/tags/${tagId}`);
113
+ }
114
+
115
+ /**
116
+ * 获取所有标签
117
+ * @param {Object} params - 查询参数
118
+ */
119
+ async getTags(params = {}) {
120
+ return this.get('/tags', params);
121
+ }
122
+
123
+ /**
124
+ * 创建标签
125
+ * @param {Object} tag - 标签数据
126
+ */
127
+ async createTag(tag) {
128
+ return this.post('/tags', tag);
129
+ }
130
+
131
+ /**
132
+ * 获取标签统计
133
+ * @param {string} tagId - 标签 ID
134
+ */
135
+ async getTagStats(tagId) {
136
+ return this.get(`/tags/${tagId}/stats`);
137
+ }
138
+
139
+ // ============ 人群圈选 ============
140
+
141
+ /**
142
+ * 获取人群列表
143
+ * @param {Object} params - 查询参数
144
+ */
145
+ async getSegments(params = {}) {
146
+ return this.get('/segments', params);
147
+ }
148
+
149
+ /**
150
+ * 创建人群
151
+ * @param {Object} segment - 人群定义
152
+ */
153
+ async createSegment(segment) {
154
+ return this.post('/segments', segment);
155
+ }
156
+
157
+ /**
158
+ * 获取人群详情
159
+ * @param {string} segmentId - 人群 ID
160
+ */
161
+ async getSegment(segmentId) {
162
+ return this.get(`/segments/${segmentId}`);
163
+ }
164
+
165
+ /**
166
+ * 更新人群
167
+ * @param {string} segmentId - 人群 ID
168
+ * @param {Object} updates - 更新数据
169
+ */
170
+ async updateSegment(segmentId, updates) {
171
+ return this.put(`/segments/${segmentId}`, updates);
172
+ }
173
+
174
+ /**
175
+ * 删除人群
176
+ * @param {string} segmentId - 人群 ID
177
+ */
178
+ async deleteSegment(segmentId) {
179
+ return this.delete(`/segments/${segmentId}`);
180
+ }
181
+
182
+ /**
183
+ * 预估人群规模
184
+ * @param {Object} rules - 圈选规则
185
+ */
186
+ async estimateSegmentSize(rules) {
187
+ return this.post('/segments/estimate', {
188
+ rules
189
+ });
190
+ }
191
+
192
+ /**
193
+ * 获取人群用户
194
+ * @param {string} segmentId - 人群 ID
195
+ * @param {Object} params - 查询参数
196
+ */
197
+ async getSegmentUsers(segmentId, params = {}) {
198
+ return this.get(`/segments/${segmentId}/users`, params);
199
+ }
200
+
201
+ // ============ 规则引擎 ============
202
+
203
+ /**
204
+ * 获取可用属性
205
+ */
206
+ async getAvailableAttributes() {
207
+ return this.get('/attributes');
208
+ }
209
+
210
+ /**
211
+ * 获取属性值分布
212
+ * @param {string} attribute - 属性名
213
+ */
214
+ async getAttributeDistribution(attribute) {
215
+ return this.get(`/attributes/${attribute}/distribution`);
216
+ }
217
+
218
+ /**
219
+ * 验证规则
220
+ * @param {Object} rules - 规则定义
221
+ */
222
+ async validateRules(rules) {
223
+ return this.post('/rules/validate', {
224
+ rules
225
+ });
226
+ }
227
+
228
+ // ============ 画像分析 ============
229
+
230
+ /**
231
+ * 获取画像概览
232
+ * @param {Object} params - 查询参数
233
+ */
234
+ async getProfileOverview(params = {}) {
235
+ return this.get('/analytics/overview', params);
236
+ }
237
+
238
+ /**
239
+ * 获取用户分布
240
+ * @param {string} dimension - 维度
241
+ * @param {Object} params - 查询参数
242
+ */
243
+ async getUserDistribution(dimension, params = {}) {
244
+ return this.get('/analytics/distribution', {
245
+ dimension,
246
+ ...params
247
+ });
248
+ }
249
+
250
+ /**
251
+ * 获取用户趋势
252
+ * @param {Object} params - 查询参数
253
+ */
254
+ async getUserTrend(params = {}) {
255
+ return this.get('/analytics/trend', params);
256
+ }
257
+
258
+ // ============ 行为与偏好 ============
259
+
260
+ /**
261
+ * 获取我的行为画像
262
+ */
263
+ async getMyBehavior() {
264
+ return this.get('/my/behavior');
265
+ }
266
+
267
+ /**
268
+ * 获取用户行为画像
269
+ * @param {string} userId - 用户 ID
270
+ */
271
+ async getUserBehavior(userId) {
272
+ return this.get(`/users/${userId}/behavior`);
273
+ }
274
+
275
+ /**
276
+ * 记录当前用户行为
277
+ * @param {string} eventType - 行为类型
278
+ * @param {Object} data - 行为数据
279
+ */
280
+ async trackBehavior(eventType, data = {}) {
281
+ return this.post('/my/behavior', {
282
+ event_type: eventType,
283
+ data
284
+ });
285
+ }
286
+
287
+ /**
288
+ * 获取我的偏好设置
289
+ */
290
+ async getPreferences() {
291
+ return this.get('/my/preferences');
292
+ }
293
+
294
+ /**
295
+ * 更新我的偏好设置
296
+ * @param {Object} preferences - 偏好设置
297
+ */
298
+ async updatePreferences(preferences) {
299
+ return this.put('/my/preferences', preferences);
300
+ }
301
+
302
+ /**
303
+ * 获取我的人群归属
304
+ */
305
+ async getUserSegments() {
306
+ return this.get('/my/segments');
307
+ }
308
+
309
+ // ============ 导入导出 ============
310
+
311
+ /**
312
+ * 导出用户画像
313
+ * @param {Object} options - 导出选项
314
+ */
315
+ async exportProfiles(options) {
316
+ return this.post('/export', options);
317
+ }
318
+
319
+ /**
320
+ * 导入用户画像
321
+ * @param {Object} data - 导入数据
322
+ */
323
+ async importProfiles(data) {
324
+ return this.post('/import', data);
325
+ }
326
+
327
+ // ============ 隐私合规 (GDPR 画像数据保护) ============
328
+
329
+ /**
330
+ * 导出我的画像数据 — GDPR 第20条
331
+ * 包括画像、标签、行为数据、偏好设置
332
+ * @returns {Promise<object>} 用户画像完整数据包
333
+ */
334
+ async exportMyData() {
335
+ try {
336
+ const [profile, tags, preferences] = await Promise.allSettled([this.getMyProfile(), this.get('/my/tags'), this.get('/my/preferences')]);
337
+ return {
338
+ exportDate: new Date().toISOString(),
339
+ format: 'QBit Profile Export (GDPR Art. 20)',
340
+ profile: profile.status === 'fulfilled' ? profile.value?.data : null,
341
+ tags: tags.status === 'fulfilled' ? tags.value?.data : [],
342
+ preferences: preferences.status === 'fulfilled' ? preferences.value?.data : null
343
+ };
344
+ } catch (e) {
345
+ return {
346
+ error: e.message,
347
+ exportDate: new Date().toISOString()
348
+ };
349
+ }
350
+ }
351
+
352
+ /**
353
+ * 请求删除我的画像数据 — GDPR 第17条
354
+ * @param {string} reason - 删除原因
355
+ */
356
+ async requestDataDeletion(reason = '') {
357
+ return this.post('/my/gdpr/delete', {
358
+ reason
359
+ });
360
+ }
361
+
362
+ /**
363
+ * 获取我的隐私设置
364
+ */
365
+ async getPrivacySettings() {
366
+ return this.get('/my/privacy');
367
+ }
368
+
369
+ /**
370
+ * 更新我的隐私设置
371
+ * @param {Object} settings - 隐私偏好
372
+ */
373
+ async updatePrivacySettings(settings) {
374
+ return this.put('/my/privacy', settings);
375
+ }
376
+
377
+ /**
378
+ * 获取隐私数据声明
379
+ */
380
+ getDataDisclosure() {
381
+ return {
382
+ sdk: '@quantabit/profile-sdk',
383
+ privacyLevel: 'analytics',
384
+ consentRequired: true,
385
+ collected: [{
386
+ type: 'user_profile',
387
+ description: 'Name, avatar, bio, preferences',
388
+ retention: 'Until account deletion'
389
+ }, {
390
+ type: 'user_tags',
391
+ description: 'Behavioral and demographic tags',
392
+ retention: 'Until cleared or account deletion'
393
+ }, {
394
+ type: 'behavior_data',
395
+ description: 'User interaction patterns for segmentation',
396
+ retention: '1 year (rolling)'
397
+ }, {
398
+ type: 'segments',
399
+ description: 'User group membership',
400
+ retention: 'While segment is active'
401
+ }],
402
+ legalBasis: 'Consent (profiling, Art. 6(1)(a) GDPR)',
403
+ compliance: ['GDPR Art. 22 (Profiling)', 'GDPR Art. 20 (Portability)'],
404
+ gdprCapabilities: ['export', 'delete-request', 'privacy-settings'],
405
+ note: 'User profiling requires explicit consent. Users can opt out via privacy settings.'
406
+ };
407
+ }
408
+ }
409
+
410
+ // 创建默认实例
411
+ const profileApi = new ProfileApiClient();
412
+
413
+ /**
414
+ * Profile SDK - 类型定义
415
+ */
416
+
417
+ // 标签类型
418
+ const TagType = {
419
+ DEMOGRAPHIC: 'demographic',
420
+ // 人口属性
421
+ INTEREST: 'interest',
422
+ // 兴趣偏好
423
+ BEHAVIOR: 'behavior',
424
+ // 行为特征
425
+ LIFECYCLE: 'lifecycle',
426
+ // 生命周期
427
+ VALUE: 'value',
428
+ // 价值分层
429
+ CUSTOM: 'custom' // 自定义
430
+ };
431
+
432
+ // 标签来源
433
+ const TagSource = {
434
+ SYSTEM: 'system',
435
+ // 系统自动
436
+ MANUAL: 'manual',
437
+ // 人工打标
438
+ RULE: 'rule',
439
+ // 规则引擎
440
+ MODEL: 'model' // 模型预测
441
+ };
442
+
443
+ // 用户生命周期
444
+ const LifecycleStage = {
445
+ NEW: 'new',
446
+ ACTIVE: 'active',
447
+ MATURE: 'mature',
448
+ DECLINING: 'declining',
449
+ CHURNED: 'churned',
450
+ REVIVED: 'revived'
451
+ };
452
+
453
+ // 价值分层
454
+ const ValueTier = {
455
+ HIGH: 'high',
456
+ MEDIUM: 'medium',
457
+ LOW: 'low',
458
+ POTENTIAL: 'potential'
459
+ };
460
+
461
+ /**
462
+ * Profile SDK - 国际化
463
+ */
464
+
465
+ const SUPPORTED_LANGUAGES = ['en', 'zh', 'ja', 'ko'];
466
+ const messages = {
467
+ zh: {
468
+ // 画像
469
+ profile: '用户画像',
470
+ myProfile: '我的画像',
471
+ tags: '标签',
472
+ preferences: '偏好',
473
+ // 标签类型
474
+ demographic: '人口属性',
475
+ interest: '兴趣偏好',
476
+ behavior: '行为特征',
477
+ lifecycle: '生命周期',
478
+ custom: '自定义',
479
+ // 生命周期
480
+ new: '新用户',
481
+ active: '活跃用户',
482
+ loyal: '忠实用户',
483
+ dormant: '沉睡用户',
484
+ churned: '流失用户',
485
+ // 价值分层
486
+ vip: 'VIP',
487
+ high: '高价值',
488
+ medium: '中价值',
489
+ low: '低价值',
490
+ // 行为
491
+ lastActive: '最近活跃',
492
+ visitCount: '访问次数',
493
+ orderCount: '订单数',
494
+ totalSpent: '总消费',
495
+ avgOrderValue: '平均订单价值',
496
+ // 兴趣
497
+ categories: '偏好分类',
498
+ brands: '偏好品牌',
499
+ priceRange: '价格敏感度',
500
+ // 设置
501
+ privacySettings: '隐私设置',
502
+ dataCollection: '数据采集',
503
+ personalization: '个性化推荐',
504
+ enabled: '已开启',
505
+ disabled: '已关闭'
506
+ },
507
+ en: {
508
+ profile: 'User Profile',
509
+ myProfile: 'My Profile',
510
+ tags: 'Tags',
511
+ preferences: 'Preferences',
512
+ demographic: 'Demographics',
513
+ interest: 'Interests',
514
+ behavior: 'Behavior',
515
+ lifecycle: 'Lifecycle',
516
+ custom: 'Custom',
517
+ new: 'New',
518
+ active: 'Active',
519
+ loyal: 'Loyal',
520
+ dormant: 'Dormant',
521
+ churned: 'Churned',
522
+ vip: 'VIP',
523
+ high: 'High Value',
524
+ medium: 'Medium Value',
525
+ low: 'Low Value',
526
+ lastActive: 'Last Active',
527
+ visitCount: 'Visits',
528
+ orderCount: 'Orders',
529
+ totalSpent: 'Total Spent',
530
+ avgOrderValue: 'AOV',
531
+ categories: 'Categories',
532
+ brands: 'Brands',
533
+ priceRange: 'Price Sensitivity',
534
+ privacySettings: 'Privacy Settings',
535
+ dataCollection: 'Data Collection',
536
+ personalization: 'Personalization',
537
+ enabled: 'Enabled',
538
+ disabled: 'Disabled'
539
+ },
540
+ ja: {
541
+ profile: 'ユーザープロファイル',
542
+ myProfile: 'マイプロファイル',
543
+ tags: 'タグ',
544
+ preferences: '設定',
545
+ demographic: 'デモグラフィック',
546
+ interest: '興味',
547
+ behavior: '行動',
548
+ lifecycle: 'ライフサイクル',
549
+ custom: 'カスタム',
550
+ new: '新規',
551
+ active: 'アクティブ',
552
+ loyal: 'ロイヤル',
553
+ dormant: '休眠',
554
+ churned: '離脱',
555
+ vip: 'VIP',
556
+ high: '高価値',
557
+ medium: '中価値',
558
+ low: '低価値',
559
+ lastActive: '最終アクティブ',
560
+ visitCount: '訪問回数',
561
+ orderCount: '注文数',
562
+ totalSpent: '総消費額',
563
+ avgOrderValue: '平均注文額',
564
+ categories: 'カテゴリ',
565
+ brands: 'ブランド',
566
+ priceRange: '価格感度',
567
+ privacySettings: 'プライバシー設定',
568
+ dataCollection: 'データ収集',
569
+ personalization: 'パーソナライゼーション',
570
+ enabled: '有効',
571
+ disabled: '無効'
572
+ },
573
+ ko: {
574
+ profile: '사용자 프로필',
575
+ myProfile: '내 프로필',
576
+ tags: '태그',
577
+ preferences: '환경설정',
578
+ demographic: '인구통계',
579
+ interest: '관심사',
580
+ behavior: '행동',
581
+ lifecycle: '라이프사이클',
582
+ custom: '사용자 지정',
583
+ new: '신규',
584
+ active: '활성',
585
+ loyal: '충성',
586
+ dormant: '휴면',
587
+ churned: '이탈',
588
+ vip: 'VIP',
589
+ high: '고가치',
590
+ medium: '중가치',
591
+ low: '저가치',
592
+ lastActive: '최근 활동',
593
+ visitCount: '방문 횟수',
594
+ orderCount: '주문 수',
595
+ totalSpent: '총 지출',
596
+ avgOrderValue: '평균 주문 금액',
597
+ categories: '카테고리',
598
+ brands: '브랜드',
599
+ priceRange: '가격 민감도',
600
+ privacySettings: '개인정보 설정',
601
+ dataCollection: '데이터 수집',
602
+ personalization: '개인화',
603
+ enabled: '활성화됨',
604
+ disabled: '비활성화됨'
605
+ }
606
+ };
607
+ let currentLanguage = 'zh';
608
+ function setLanguage(lang) {
609
+ if (SUPPORTED_LANGUAGES.includes(lang)) currentLanguage = lang;
610
+ }
611
+ function getLanguage() {
612
+ return currentLanguage;
613
+ }
614
+ function t(key, params = {}) {
615
+ let text = (messages[currentLanguage] || messages.en)[key] || key;
616
+ Object.entries(params).forEach(([k, v]) => {
617
+ text = text.replace(new RegExp(`\\{${k}\\}`, 'g'), v);
618
+ });
619
+ return text;
620
+ }
621
+
622
+ /**
623
+ * Profile SDK - React Hooks
624
+ */
625
+
626
+
627
+ /**
628
+ * useUserProfile - 用户画像Hook
629
+ */
630
+ function useUserProfile(userId) {
631
+ const [profile, setProfile] = React.useState(null);
632
+ const [loading, setLoading] = React.useState(false);
633
+ const loadProfile = React.useCallback(async () => {
634
+ setLoading(true);
635
+ try {
636
+ const result = userId ? await profileApi.getUserProfile(userId) : await profileApi.getMyProfile();
637
+ setProfile(result);
638
+ } finally {
639
+ setLoading(false);
640
+ }
641
+ }, [userId]);
642
+ React.useEffect(() => {
643
+ loadProfile();
644
+ }, [userId]);
645
+ return {
646
+ profile,
647
+ loading,
648
+ refresh: loadProfile
649
+ };
650
+ }
651
+
652
+ /**
653
+ * useUserTags - 用户标签Hook
654
+ */
655
+ function useUserTags(userId) {
656
+ const [tags, setTags] = React.useState([]);
657
+ const [loading, setLoading] = React.useState(false);
658
+ const loadTags = React.useCallback(async () => {
659
+ setLoading(true);
660
+ try {
661
+ const result = userId ? await profileApi.getUserTags(userId) : await profileApi.getMyTags();
662
+ setTags(result.tags || []);
663
+ } finally {
664
+ setLoading(false);
665
+ }
666
+ }, [userId]);
667
+
668
+ // 添加标签
669
+ const addTag = React.useCallback(async tag => {
670
+ try {
671
+ await profileApi.addTag(tag);
672
+ loadTags();
673
+ return true;
674
+ } catch (e) {
675
+ return false;
676
+ }
677
+ }, [loadTags]);
678
+
679
+ // 移除标签
680
+ const removeTag = React.useCallback(async tagId => {
681
+ try {
682
+ await profileApi.removeTag(tagId);
683
+ loadTags();
684
+ return true;
685
+ } catch (e) {
686
+ return false;
687
+ }
688
+ }, [loadTags]);
689
+ React.useEffect(() => {
690
+ loadTags();
691
+ }, [userId]);
692
+
693
+ // 按类型分组
694
+ const tagsByType = tags.reduce((acc, tag) => {
695
+ const type = tag.type || 'custom';
696
+ if (!acc[type]) acc[type] = [];
697
+ acc[type].push(tag);
698
+ return acc;
699
+ }, {});
700
+ return {
701
+ tags,
702
+ tagsByType,
703
+ loading,
704
+ addTag,
705
+ removeTag,
706
+ refresh: loadTags
707
+ };
708
+ }
709
+
710
+ /**
711
+ * useUserBehavior - 用户行为Hook
712
+ */
713
+ function useUserBehavior(userId) {
714
+ const [behavior, setBehavior] = React.useState(null);
715
+ const [loading, setLoading] = React.useState(false);
716
+ const loadBehavior = React.useCallback(async () => {
717
+ setLoading(true);
718
+ try {
719
+ const result = userId ? await profileApi.getUserBehavior(userId) : await profileApi.getMyBehavior();
720
+ setBehavior(result);
721
+ } finally {
722
+ setLoading(false);
723
+ }
724
+ }, [userId]);
725
+
726
+ // 记录行为
727
+ const trackBehavior = React.useCallback(async (eventType, data) => {
728
+ try {
729
+ await profileApi.trackBehavior(eventType, data);
730
+ return true;
731
+ } catch (e) {
732
+ return false;
733
+ }
734
+ }, []);
735
+ React.useEffect(() => {
736
+ loadBehavior();
737
+ }, [userId]);
738
+ return {
739
+ behavior,
740
+ loading,
741
+ trackBehavior,
742
+ refresh: loadBehavior
743
+ };
744
+ }
745
+
746
+ /**
747
+ * useUserPreferences - 用户偏好Hook
748
+ */
749
+ function useUserPreferences() {
750
+ const [preferences, setPreferences] = React.useState(null);
751
+ const [loading, setLoading] = React.useState(false);
752
+ const [saving, setSaving] = React.useState(false);
753
+ const loadPreferences = React.useCallback(async () => {
754
+ setLoading(true);
755
+ try {
756
+ const result = await profileApi.getPreferences();
757
+ setPreferences(result);
758
+ } finally {
759
+ setLoading(false);
760
+ }
761
+ }, []);
762
+
763
+ // 更新偏好
764
+ const updatePreferences = React.useCallback(async updates => {
765
+ setSaving(true);
766
+ try {
767
+ const result = await profileApi.updatePreferences(updates);
768
+ setPreferences(result);
769
+ return true;
770
+ } catch (e) {
771
+ return false;
772
+ } finally {
773
+ setSaving(false);
774
+ }
775
+ }, []);
776
+ React.useEffect(() => {
777
+ loadPreferences();
778
+ }, []);
779
+ return {
780
+ preferences,
781
+ loading,
782
+ saving,
783
+ updatePreferences,
784
+ refresh: loadPreferences
785
+ };
786
+ }
787
+
788
+ /**
789
+ * usePrivacySettings - 隐私设置Hook
790
+ */
791
+ function usePrivacySettings() {
792
+ const [settings, setSettings] = React.useState({
793
+ dataCollection: true,
794
+ personalization: true
795
+ });
796
+ const [loading, setLoading] = React.useState(false);
797
+ const loadSettings = React.useCallback(async () => {
798
+ setLoading(true);
799
+ try {
800
+ const result = await profileApi.getPrivacySettings();
801
+ setSettings(result);
802
+ } finally {
803
+ setLoading(false);
804
+ }
805
+ }, []);
806
+ const updateSettings = React.useCallback(async (key, value) => {
807
+ try {
808
+ await profileApi.updatePrivacySettings({
809
+ [key]: value
810
+ });
811
+ setSettings(prev => ({
812
+ ...prev,
813
+ [key]: value
814
+ }));
815
+ return true;
816
+ } catch (e) {
817
+ return false;
818
+ }
819
+ }, []);
820
+ React.useEffect(() => {
821
+ loadSettings();
822
+ }, []);
823
+ return {
824
+ settings,
825
+ loading,
826
+ updateSettings,
827
+ refresh: loadSettings
828
+ };
829
+ }
830
+
831
+ /**
832
+ * useUserSegments - 用户分群Hook
833
+ */
834
+ function useUserSegments() {
835
+ const [segments, setSegments] = React.useState([]);
836
+ const [loading, setLoading] = React.useState(false);
837
+ const loadSegments = React.useCallback(async () => {
838
+ setLoading(true);
839
+ try {
840
+ const result = await profileApi.getUserSegments();
841
+ setSegments(result.segments || []);
842
+ } finally {
843
+ setLoading(false);
844
+ }
845
+ }, []);
846
+ React.useEffect(() => {
847
+ loadSegments();
848
+ }, []);
849
+ return {
850
+ segments,
851
+ loading,
852
+ refresh: loadSegments
853
+ };
854
+ }
855
+
856
+ /**
857
+ * Profile SDK - 用户画像组件
858
+ */
859
+
860
+
861
+ /**
862
+ * 生命周期阶段颜色
863
+ */
864
+ const lifecycleColors = {
865
+ [LifecycleStage.NEW]: '#3B82F6',
866
+ [LifecycleStage.ACTIVE]: '#10B981',
867
+ [LifecycleStage.LOYAL]: '#8B5CF6',
868
+ [LifecycleStage.DORMANT]: '#F59E0B',
869
+ [LifecycleStage.CHURNED]: '#EF4444'
870
+ };
871
+
872
+ /**
873
+ * 价值分层颜色
874
+ */
875
+ const valueColors = {
876
+ [ValueTier.VIP]: '#EC4899',
877
+ [ValueTier.HIGH]: '#8B5CF6',
878
+ [ValueTier.MEDIUM]: '#3B82F6',
879
+ [ValueTier.LOW]: '#6B7280'
880
+ };
881
+
882
+ /**
883
+ * UserTag 组件
884
+ */
885
+ function UserTag({
886
+ tag,
887
+ removable = false,
888
+ onRemove
889
+ }) {
890
+ const typeColors = {
891
+ [TagType.DEMOGRAPHIC]: '#3B82F6',
892
+ [TagType.INTEREST]: '#10B981',
893
+ [TagType.BEHAVIOR]: '#F59E0B',
894
+ [TagType.LIFECYCLE]: '#8B5CF6',
895
+ [TagType.CUSTOM]: '#6B7280'
896
+ };
897
+ const color = typeColors[tag.type] || '#6B7280';
898
+ return /*#__PURE__*/React.createElement("span", {
899
+ className: "eco-profile-tag",
900
+ style: {
901
+ backgroundColor: `${color}20`,
902
+ color
903
+ }
904
+ }, tag.name || tag.value, removable && onRemove && /*#__PURE__*/React.createElement("button", {
905
+ className: "eco-profile-tag-remove",
906
+ onClick: () => onRemove(tag.id)
907
+ }, "\xD7"));
908
+ }
909
+
910
+ /**
911
+ * TagCloud 组件
912
+ */
913
+ function TagCloud({
914
+ tags = [],
915
+ type,
916
+ removable = false,
917
+ onRemove
918
+ }) {
919
+ const filteredTags = type ? tags.filter(t => t.type === type) : tags;
920
+ if (filteredTags.length === 0) {
921
+ return /*#__PURE__*/React.createElement("div", {
922
+ className: "eco-profile-empty"
923
+ }, "\u6682\u65E0\u6807\u7B7E");
924
+ }
925
+ return /*#__PURE__*/React.createElement("div", {
926
+ className: "eco-profile-tag-cloud"
927
+ }, filteredTags.map(tag => /*#__PURE__*/React.createElement(UserTag, {
928
+ key: tag.id,
929
+ tag: tag,
930
+ removable: removable,
931
+ onRemove: onRemove
932
+ })));
933
+ }
934
+
935
+ /**
936
+ * LifecycleIndicator 组件
937
+ */
938
+ function LifecycleIndicator({
939
+ stage
940
+ }) {
941
+ const color = lifecycleColors[stage] || '#6B7280';
942
+ return /*#__PURE__*/React.createElement("div", {
943
+ className: "eco-profile-lifecycle",
944
+ style: {
945
+ borderColor: color
946
+ }
947
+ }, /*#__PURE__*/React.createElement("span", {
948
+ className: "eco-profile-lifecycle-dot",
949
+ style: {
950
+ backgroundColor: color
951
+ }
952
+ }), /*#__PURE__*/React.createElement("span", {
953
+ className: "eco-profile-lifecycle-text"
954
+ }, t(stage)));
955
+ }
956
+
957
+ /**
958
+ * ValueTierBadge 组件
959
+ */
960
+ function ValueTierBadge({
961
+ tier
962
+ }) {
963
+ const color = valueColors[tier] || '#6B7280';
964
+ return /*#__PURE__*/React.createElement("span", {
965
+ className: "eco-profile-value-badge",
966
+ style: {
967
+ backgroundColor: color
968
+ }
969
+ }, t(tier));
970
+ }
971
+
972
+ /**
973
+ * BehaviorStats 组件
974
+ */
975
+ function BehaviorStats({
976
+ behavior
977
+ }) {
978
+ if (!behavior) return null;
979
+ const stats = [{
980
+ label: t('lastActive'),
981
+ value: behavior.last_active ? new Date(behavior.last_active).toLocaleDateString() : '-'
982
+ }, {
983
+ label: t('visitCount'),
984
+ value: behavior.visit_count || 0
985
+ }, {
986
+ label: t('orderCount'),
987
+ value: behavior.order_count || 0
988
+ }, {
989
+ label: t('totalSpent'),
990
+ value: `¥${(behavior.total_spent || 0).toLocaleString()}`
991
+ }, {
992
+ label: t('avgOrderValue'),
993
+ value: `¥${(behavior.avg_order_value || 0).toFixed(2)}`
994
+ }];
995
+ return /*#__PURE__*/React.createElement("div", {
996
+ className: "eco-profile-behavior-stats"
997
+ }, stats.map((stat, i) => /*#__PURE__*/React.createElement("div", {
998
+ key: i,
999
+ className: "eco-profile-stat"
1000
+ }, /*#__PURE__*/React.createElement("span", {
1001
+ className: "eco-profile-stat-value"
1002
+ }, stat.value), /*#__PURE__*/React.createElement("span", {
1003
+ className: "eco-profile-stat-label"
1004
+ }, stat.label))));
1005
+ }
1006
+
1007
+ /**
1008
+ * PreferenceList 组件
1009
+ */
1010
+ function PreferenceList({
1011
+ preferences,
1012
+ type
1013
+ }) {
1014
+ const items = preferences?.[type] || [];
1015
+ if (items.length === 0) {
1016
+ return /*#__PURE__*/React.createElement("div", {
1017
+ className: "eco-profile-empty"
1018
+ }, "\u6682\u65E0\u6570\u636E");
1019
+ }
1020
+ return /*#__PURE__*/React.createElement("div", {
1021
+ className: "eco-profile-preference-list"
1022
+ }, items.map((item, i) => /*#__PURE__*/React.createElement("div", {
1023
+ key: i,
1024
+ className: "eco-profile-preference-item"
1025
+ }, /*#__PURE__*/React.createElement("span", {
1026
+ className: "eco-profile-preference-name"
1027
+ }, item.name), /*#__PURE__*/React.createElement("div", {
1028
+ className: "eco-profile-preference-bar"
1029
+ }, /*#__PURE__*/React.createElement("div", {
1030
+ className: "eco-profile-preference-fill",
1031
+ style: {
1032
+ width: `${item.score * 100}%`
1033
+ }
1034
+ })), /*#__PURE__*/React.createElement("span", {
1035
+ className: "eco-profile-preference-score"
1036
+ }, Math.round(item.score * 100), "%"))));
1037
+ }
1038
+
1039
+ /**
1040
+ * UserProfileCard 组件
1041
+ */
1042
+ function UserProfileCard({
1043
+ profile,
1044
+ behavior,
1045
+ tags = [],
1046
+ compact = false
1047
+ }) {
1048
+ if (!profile) return null;
1049
+ const {
1050
+ lifecycle_stage,
1051
+ value_tier
1052
+ } = profile;
1053
+ return /*#__PURE__*/React.createElement("div", {
1054
+ className: `eco-profile-card ${compact ? 'compact' : ''}`
1055
+ }, /*#__PURE__*/React.createElement("div", {
1056
+ className: "eco-profile-card-header"
1057
+ }, /*#__PURE__*/React.createElement("div", {
1058
+ className: "eco-profile-badges"
1059
+ }, lifecycle_stage && /*#__PURE__*/React.createElement(LifecycleIndicator, {
1060
+ stage: lifecycle_stage
1061
+ }), value_tier && /*#__PURE__*/React.createElement(ValueTierBadge, {
1062
+ tier: value_tier
1063
+ }))), !compact && behavior && /*#__PURE__*/React.createElement(BehaviorStats, {
1064
+ behavior: behavior
1065
+ }), tags.length > 0 && /*#__PURE__*/React.createElement("div", {
1066
+ className: "eco-profile-card-tags"
1067
+ }, /*#__PURE__*/React.createElement("h4", null, t('tags')), /*#__PURE__*/React.createElement(TagCloud, {
1068
+ tags: tags.slice(0, compact ? 5 : 10)
1069
+ })));
1070
+ }
1071
+
1072
+ /**
1073
+ * PrivacyToggle 组件
1074
+ */
1075
+ function PrivacyToggle({
1076
+ label,
1077
+ value,
1078
+ onChange,
1079
+ loading = false
1080
+ }) {
1081
+ return /*#__PURE__*/React.createElement("div", {
1082
+ className: "eco-profile-privacy-toggle"
1083
+ }, /*#__PURE__*/React.createElement("span", {
1084
+ className: "eco-profile-privacy-label"
1085
+ }, label), /*#__PURE__*/React.createElement("button", {
1086
+ className: `eco-profile-toggle ${value ? 'active' : ''}`,
1087
+ onClick: () => onChange(!value),
1088
+ disabled: loading
1089
+ }, /*#__PURE__*/React.createElement("span", {
1090
+ className: "eco-profile-toggle-slider"
1091
+ })), /*#__PURE__*/React.createElement("span", {
1092
+ className: "eco-profile-privacy-status"
1093
+ }, value ? t('enabled') : t('disabled')));
1094
+ }
1095
+
1096
+ /**
1097
+ * @quantabit/profile-sdk
1098
+ * User Profile SDK - Full Version
1099
+ */
1100
+
1101
+ const getMyProfile = () => profileApi.getMyProfile();
1102
+ const getUserProfile = userId => profileApi.getUserProfile(userId);
1103
+ const getMyTags = () => profileApi.getMyTags();
1104
+ const getUserTags = userId => profileApi.getUserTags(userId);
1105
+ const addTag = tag => profileApi.addTag(tag);
1106
+ const removeTag = tagId => profileApi.removeTag(tagId);
1107
+ const getMyBehavior = () => profileApi.getMyBehavior();
1108
+ const trackBehavior = (eventType, data) => profileApi.trackBehavior(eventType, data);
1109
+ const getPreferences = () => profileApi.getPreferences();
1110
+ const updatePreferences = updates => profileApi.updatePreferences(updates);
1111
+ const getPrivacySettings = () => profileApi.getPrivacySettings();
1112
+ const updatePrivacySettings = settings => profileApi.updatePrivacySettings(settings);
1113
+
1114
+ exports.BehaviorStats = BehaviorStats;
1115
+ exports.LifecycleIndicator = LifecycleIndicator;
1116
+ exports.LifecycleStage = LifecycleStage;
1117
+ exports.PreferenceList = PreferenceList;
1118
+ exports.PrivacyToggle = PrivacyToggle;
1119
+ exports.ProfileApiClient = ProfileApiClient;
1120
+ exports.SUPPORTED_LANGUAGES = SUPPORTED_LANGUAGES;
1121
+ exports.TagCloud = TagCloud;
1122
+ exports.TagSource = TagSource;
1123
+ exports.TagType = TagType;
1124
+ exports.UserProfileCard = UserProfileCard;
1125
+ exports.UserTag = UserTag;
1126
+ exports.ValueTier = ValueTier;
1127
+ exports.ValueTierBadge = ValueTierBadge;
1128
+ exports.addTag = addTag;
1129
+ exports.getLanguage = getLanguage;
1130
+ exports.getMyBehavior = getMyBehavior;
1131
+ exports.getMyProfile = getMyProfile;
1132
+ exports.getMyTags = getMyTags;
1133
+ exports.getPreferences = getPreferences;
1134
+ exports.getPrivacySettings = getPrivacySettings;
1135
+ exports.getUserProfile = getUserProfile;
1136
+ exports.getUserTags = getUserTags;
1137
+ exports.messages = messages;
1138
+ exports.profileApi = profileApi;
1139
+ exports.removeTag = removeTag;
1140
+ exports.setLanguage = setLanguage;
1141
+ exports.t = t;
1142
+ exports.trackBehavior = trackBehavior;
1143
+ exports.updatePreferences = updatePreferences;
1144
+ exports.updatePrivacySettings = updatePrivacySettings;
1145
+ exports.usePrivacySettings = usePrivacySettings;
1146
+ exports.useUserBehavior = useUserBehavior;
1147
+ exports.useUserPreferences = useUserPreferences;
1148
+ exports.useUserProfile = useUserProfile;
1149
+ exports.useUserSegments = useUserSegments;
1150
+ exports.useUserTags = useUserTags;
1151
+ //# sourceMappingURL=index.cjs.map