# 珍珠图重新设计:社会连接盘点系统 **日期**: 2026-09-10 **状态**: 初稿 **关联**: `2026-07-12-dimension-features-energy-map.md` 中「圈子(珍珠图)概念」 --- ## 背景 当前珍珠图是「资源清单」实现:从联系人(PERSON)、帮助记录(SKILL)、手工登记(INFO/PLACE)聚合出 4 组弧形分布的珍珠节点。 书稿定义:「把可调用的外部资源串成珍珠项链,盘点'我有什么'」。 新需求:将珍珠图重构为**社会连接盘点工具**,明确用户有哪些可依赖的外部资源,并按类型分级、定期互动维护。 --- ## 核心设计决策 | 决策点 | 选择 | |--------|------| | 布局 | 保留现有 Canvas 圆形布局,扩展为**两层同心圆**(内圈必备 12 组 + 外圈理想 13 组) | | 分类方式 | 新建固定枚举 `ConnectionType`(25 类),不允许用户自定义 | | 分级逻辑 | 系统自动计算价值分 + 用户手动覆盖优先级 | | 珍珠大小 | 按优先级/价值分映射半径(高价值=大珍珠,低价值=小珍珠) | | 互动记录 | 新增 `pearl_interaction_log` 表;定时任务每日生成提醒 | | 兼容性 | 现有 PERSON/SKILL/INFO/PLACE 数据保留为「历史资源」,不迁移 | --- ## 一、连接类型枚举(ConnectionType) 连接类型分两层语义: - **必备社会连接(ESSENTIAL,12 类)**:用户必须拥有的核心资源,内圈展示 - **理想社会连接(IDEAL,13 类)**:在必备基础上的扩展资源,外圈展示 > 理想社会连接 = 必备社会连接(12 类)+ 理想扩展(13 类)= 共 25 类。 ### 1.1 必备社会连接(12 类,内圈) | 类型 Code | 中文名 | 建议互动周期 | |-----------|--------|-------------| | MEDICAL | 医疗 | 季度(3 月) | | EDUCATION | 教育 | 学期(约 6 月) | | PUBLIC_SECURITY | 公安 | 年度(12 月) | | AUTO | 汽车相关 | 半年(6 月) | | REAL_ESTATE | 房产服务 | 年度(12 月) | | TICKETING | 票务 | 年度(12 月) | | HANDYMAN | 多能工人 | 半年(6 月) | | APPLIANCE | 家电 | 年度(12 月) | | CATERING | 餐饮 | 季度(3 月) | | FOOD | 食品 | 季度(3 月) | | LEGAL | 法律 | 年度(12 月) | | WEEKEND_EXPERT | 周末达人 | 月度(1 月) | ### 1.2 理想社会连接扩展(13 类,外圈) | 类型 Code | 中文名 | 建议互动周期 | |-----------|--------|-------------| | GOVERNMENT | 政府综合 | 年度(12 月) | | TAX | 税务 | 年度(12 月) | | BUSINESS_ADMIN | 工商 | 年度(12 月) | | BANK | 银行 | 半年(6 月) | | MEDIA | 媒体 | 年度(12 月) | | TRAVEL | 旅游 | 年度(12 月) | | LEADING_ENTERPRISE | 领军企业 | 年度(12 月) | | INDUSTRY_BENCHMARK | 行业标杆 | 年度(12 月) | | OVERSEAS | 国外 | 年度(12 月) | | UNIVERSITY | 高校 | 年度(12 月) | | FINANCE | 金融 | 年度(12 月) | | SENIOR_LOCAL | 资深土著 | 半年(6 月) | | KEY_CITY | 北上广等关键城市 | 年度(12 月) | > **说明**:建议互动周期为产品默认值,后续可在管理端调整。前端通过 API 获取枚举(便于未来调整顺序/周期/文案)。 --- ## 二、数据库变更 ### 2.1 resource_items 表新增字段 ```sql ALTER TABLE resource_items ADD COLUMN connection_type VARCHAR(50) NOT NULL DEFAULT '' COMMENT '关联 ConnectionType 枚举名', ADD COLUMN priority TINYINT DEFAULT 2 COMMENT '优先级:1=高 2=中 3=低(手动覆盖用)', ADD COLUMN value_score INT DEFAULT 0 COMMENT '价值分 0-100(自动计算,用户可手动覆盖)', ADD COLUMN last_interaction_at DATETIME DEFAULT NULL COMMENT '最近互动时间', ADD COLUMN interaction_count INT DEFAULT 0 COMMENT '互动总次数', ADD INDEX idx_ri_connection_type (connection_type); ``` **迁移逻辑**(DatabaseInitializer 新增迁移): - 现有 INFO/PLACE/SKILL 类型资源: - `connection_type = ''`(空,标记为历史资源,不参与 25 类分组) - `priority = 2`(默认中) - `value_score = 50`(默认中) - `last_interaction_at = null` - `interaction_count = 0` - 原有 `type` 字段保留不变(INFO/PLACE/SKILL 作为兼容字段) ### 2.2 新增 pearl_interaction_log 表 ```sql CREATE TABLE IF NOT EXISTS pearl_interaction_log ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL COMMENT '操作人', resource_item_id BIGINT NOT NULL COMMENT '关联资源', interaction_type VARCHAR(20) NOT NULL COMMENT 'PHONE/WECHAT/MEETING/OTHER', content VARCHAR(500) DEFAULT NULL COMMENT '互动内容简记', happened_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '互动发生时间', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX idx_pil_user (user_id), INDEX idx_pil_resource (resource_item_id), INDEX idx_pil_happened (happened_at) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='珍珠图互动日志'; ``` ### 2.3 新增 pearl_interaction_reminder 表 ```sql CREATE TABLE IF NOT EXISTS pearl_interaction_reminder ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT NOT NULL, resource_item_id BIGINT NOT NULL, connection_type VARCHAR(50) NOT NULL, suggest_interval_month INT NOT NULL COMMENT '建议互动周期(月)', last_interaction_at DATETIME DEFAULT NULL, days_since_last INT NOT NULL COMMENT '距上次互动天数', status VARCHAR(20) DEFAULT 'PENDING' COMMENT 'PENDING/DONE/DISMISSED', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_pir_user_status (user_id, status), INDEX idx_pir_resource (resource_item_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='珍珠图互动提醒'; ``` --- ## 三、后端设计 ### 3.1 新增枚举类 ConnectionType.java ```java public enum ConnectionType { // 必备社会连接(ESSENTIAL)— 内圈,12 类 MEDICAL("医疗", "ESSENTIAL", 3), EDUCATION("教育", "ESSENTIAL", 6), PUBLIC_SECURITY("公安", "ESSENTIAL", 12), AUTO("汽车相关", "ESSENTIAL", 6), REAL_ESTATE("房产服务", "ESSENTIAL", 12), TICKETING("票务", "ESSENTIAL", 12), HANDYMAN("多能工人", "ESSENTIAL", 6), APPLIANCE("家电", "ESSENTIAL", 12), CATERING("餐饮", "ESSENTIAL", 3), FOOD("食品", "ESSENTIAL", 3), LEGAL("法律", "ESSENTIAL", 12), WEEKEND_EXPERT("周末达人", "ESSENTIAL", 1), // 理想社会连接扩展(IDEAL)— 外圈,13 类 GOVERNMENT("政府综合", "IDEAL", 12), TAX("税务", "IDEAL", 12), BUSINESS_ADMIN("工商", "IDEAL", 12), BANK("银行", "IDEAL", 6), MEDIA("媒体", "IDEAL", 12), TRAVEL("旅游", "IDEAL", 12), LEADING_ENTERPRISE("领军企业", "IDEAL", 12), INDUSTRY_BENCHMARK("行业标杆", "IDEAL", 12), OVERSEAS("国外", "IDEAL", 12), UNIVERSITY("高校", "IDEAL", 12), FINANCE("金融", "IDEAL", 12), SENIOR_LOCAL("资深土著", "IDEAL", 6), KEY_CITY("北上广等关键城市", "IDEAL", 12); private final String label; private final String category; // ESSENTIAL / IDEAL private final int suggestIntervalMonth; ConnectionType(String label, String category, int suggestIntervalMonth) { this.label = label; this.category = category; this.suggestIntervalMonth = suggestIntervalMonth; } // getters... } ``` ### 3.2 ResourceService 扩展 新增方法: ```java /** 计算并更新资源的价值分 */ public void recalculateValueScore(Long userId, Long itemId) /** 自动计算价值分(纯函数,不写 DB) */ public int calculateValueScore(ResourceItem item, List logs) // 类型基础权重:ESSENTIAL=30, IDEAL=20 // 近90天互动次数 × 5,上限 30 // 最近互动时效性:≤30天=+20, ≤90天=+10, ≤180天=+5 // 互动总量 × 2,上限 20 // 总分为 0-100 ``` ### 3.3 PearlController 新增接口 ```java // 获取连接类型列表(供前端类型选择器使用) @PostMapping("/connection-types") public Result>> getConnectionTypes() // 记录互动 @PostMapping("/interaction/add") public Result addInteraction(@RequestBody Map params, @RequestAttribute Long userId) // params: itemId, interactionType, content // 获取互动历史 @PostMapping("/interaction/list") public Result>> getInteractionList( @RequestBody Map params, @RequestAttribute Long userId) // params: itemId, page, size // 获取待办提醒列表 @PostMapping("/reminder/list") public Result>> getReminders(@RequestAttribute Long userId) // 更新资源优先级(手动覆盖) @PostMapping("/item/update-priority") public Result updatePriority(@RequestBody Map params, @RequestAttribute Long userId) // params: itemId, priority (1/2/3) ``` ### 3.4 定时任务 PearlReminderScheduler ```java @Scheduled(cron = "0 0 8 * * ?") // 每日 08:00 public void generateReminders() { // 遍历所有用户的 25 类资源(connection_type 非空) // 对每个资源: // daysSinceLast = today - lastInteractionAt // 若 daysSinceLast >= suggestIntervalMonth * 30 // 且不存在 PENDING 提醒 → 创建提醒记录 // 若 status = DONE/DISMISSED → 跳过 } ``` --- ## 四、前端设计 ### 4.1 PearlDiagram.vue 核心变更 #### 4.1.1 groupConfigs 扩展为两层同心圆 ```javascript groupConfigs: [ // 内圈(必备 12 组) { type: 'MEDICAL', typeName: '医疗', color: '#EF4444', radiusPct: 0.28, startAngle: -Math.PI * 0.85, endAngle: -Math.PI * 0.15, ring: 'inner' }, { type: 'EDUCATION', typeName: '教育', color: '#3B82F6', radiusPct: 0.28, startAngle: -Math.PI * 0.85, endAngle: -Math.PI * 0.15, ring: 'inner' }, // ... 其余 10 个内圈类型(每组弧度均分,避免重叠) // 外圈(理想 13 组) { type: 'GOVERNMENT', typeName: '政府综合', color: '#8B5CF6', radiusPct: 0.55, startAngle: -Math.PI * 0.7, endAngle: Math.PI * 0.2, ring: 'outer' }, // ... 其余 12 个外圈类型 ] ``` **布局说明**: - 内圈半径比例 `radiusPct = 0.28`,外圈 `radiusPct = 0.55` - 每组分配一个角度区间,组内珍珠沿弧线等间距分布 - 25 组平均分配圆周:内圈 12 组 × 约 26° 弧 + 间隙,外圈 13 组 × 约 24° 弧 + 间隙 - 由于内圈/外圈半径不同、颜色不同,即使角度重叠也能区分 #### 4.1.2 珍珠半径动态计算(按价值分映射) ```javascript // buildLayout 中计算每个珍珠的 radius var valueScore = item.valueScore || 0 var priority = item.priority || 2 // 手动优先级优先于自动 var priorityMultiplier = priority === 1 ? 1.4 : priority === 3 ? 0.7 : 1.0 var itemRadius = minDim * config.radiusPct * 0.09 * priorityMultiplier // 内圈珍珠半径范围约 8-20px,外圈 8-20px,最小 8,最大 26 itemRadius = Math.max(8, Math.min(26, itemRadius)) ``` #### 4.1.3 视觉规范 | 元素 | 样式 | |------|------| | 中心节点「我」 | 半径 36px,实心圆,深橙色 #F97316 | | 内圈连线 | 从中心放射至每组弧心,透明度 30% | | 外圈连线 | 同上,透明度 20% | | 资源珍珠 | 实心圆,颜色 = 类型颜色,半径 = 优先级映射 | | 高价值珍珠(优先级 1) | 加粗边框(2px 白色描边) | | 分组标签 | 置于弧外侧,字号 20rpx,颜色 = 类型颜色 | | 互动逾期珍珠 | 边缘显示红色角标「逾期N天」 | ### 4.2 pearl-add-resource.vue 扩展 **现有流程**:选类型(INFO/PLACE/SKILL)→ 填名称/描述 → 关联联系人 → 提交 **新流程**: ``` 1. 底部弹出选择器:选择连接类型(25 类,分「必备」/「理想」两个分组 Tab) 2. 进入现有表单:名称、描述、关联联系人 3. 提交(connection_type 必填) ``` ### 4.3 珍珠弹窗扩展 点击珍珠后弹窗新增: - **互动记录**折叠面板(最近 5 条,显示时间/方式/内容) - **联系按钮**:底部 ActionSheet 选方式 → 填简记 → 确认 → 调 `/interaction/add` - **优先级调整**:三档选择(高/中/低)→ 调 `/item/update-priority` ### 4.4 提醒入口 在珍珠图页面顶部新增「提醒」Tab: - 红点计数(PENDING 提醒数) - 点击进入提醒列表 - 每条提醒:资源名称、连接类型、距上次互动天数、操作「去联系」「稍后提醒」 --- ## 五、API 响应格式 ### 5.1 POST /api/pearl/connection-types ```json { "code": 200, "data": [ { "code": "MEDICAL", "label": "医疗", "category": "ESSENTIAL", "suggestIntervalMonth": 3 }, { "code": "EDUCATION", "label": "教育", "category": "ESSENTIAL", "suggestIntervalMonth": 6 }, { "code": "GOVERNMENT", "label": "政府综合", "category": "IDEAL", "suggestIntervalMonth": 12 } // ... 共 25 项 ] } ``` ### 5.2 POST /api/pearl/resources(响应格式变更) ```json { "code": 200, "data": { "groups": [ { "type": "MEDICAL", "typeName": "医疗", "category": "ESSENTIAL", "color": "#EF4444", "ring": "inner", "items": [ { "id": 1, "name": "社区王医生", "description": "儿科,社区医院主治医", "avatar": null, "valueScore": 72, "priority": 1, "contactId": 5, "lastInteractionAt": "2026-08-15T10:30:00", "interactionCount": 8, "daysSinceLast": 26 } ] } // ... 共 25 组(为空时 items=[]) ], "legacyGroups": [ // 原有 PERSON/SKILL/INFO/PLACE,作为历史资源(connection_type='' 的资源) ] } } ``` ### 5.3 POST /api/pearl/interaction/add 请求体: ```json { "itemId": 1, "interactionType": "WECHAT", "content": "咨询孩子过敏问题" } ``` 成功:`{ "code": 200 }` ### 5.4 POST /api/pearl/reminder/list 返回当前用户所有 PENDING 状态的提醒。 --- ## 六、交互流程图 ``` 打开珍珠图页 ↓ 调用 /api/pearl/resources ↓ 渲染两层同心圆(内圈必备 12 组 / 外圈理想 13 组) ↓ 点击珍珠 ↓ 弹窗:名称 / 类型 / 优先级 / 互动记录 / 按钮[联系][编辑][删除] ↓ 点击「联系」 ↓ 底部弹出 ActionSheet(电话/微信/见面/其他) ↓ 填写互动内容 → 提交 /api/pearl/interaction/add ↓ 更新珍珠 radius(实时重绘)+ 刷新互动记录 ↓ (定时任务)每日 08:00 检查互动频率 → 生成提醒 ↓ 珍珠边缘显示「逾期N天」角标 → 点击跳转提醒列表 ``` --- ## 七、迁移计划 ### Migration 步骤 1. **DatabaseInitializer 新增迁移**: - `migration_XXX`: 给 `resource_items` 添加新字段,存量数据填充默认值 - `migration_XXX+1`: 创建 `pearl_interaction_log` 表 - `migration_XXX+2`: 创建 `pearl_interaction_reminder` 表 2. **后端**: - 新增 `ConnectionType.java` 枚举(25 类) - 扩展 `ResourceService.java`(新增价值分计算、互动、提醒方法) - 扩展 `PearlController.java`(新增 5 个接口) - 新增 `PearlReminderScheduler.java`(定时任务) - 新增 `PearlInteractionLog.java`、`PearlInteractionReminder.java` 实体 + Mapper 3. **前端**: - 扩展 `PearlDiagram.vue`(同心圆布局算法 + 半径计算) - 扩展 `pearl-add-resource.vue`(25 类类型选择器) - 扩展弹窗交互(联系按钮 + 互动记录面板) - 新增提醒列表页 - 更新 `api.js`(新增 5 个 API 方法) 4. **测试验证**: - `mvn clean compile` 无编译错误 - 小程序端:25 组分类渲染正常、点击交互正常 - 互动记录写入/读取正常 - 定时任务生成提醒正常 --- ## 八、非功能需求 | 项目 | 要求 | |------|------| | **性能** | Canvas 单帧 < 16ms(25 组 × 平均 3-5 珍珠 ≈ 100 节点) | | **兼容性** | iOS/Android 微信基础库 2.25.0+ | | **空状态** | 无任何新类型资源时显示引导按钮「登记你的第一个社会连接」 | | **异常处理** | 连接类型不在枚举 → 拦截报错;互动日志写入失败 → 回滚 + 提示 | | **权限** | 复用现有 JWT 认证,仅本人可见 | --- ## 附录:25 类 ConnectionType 完整列表 | # | Code | 中文名 | Category | 建议周期 | |---|------|--------|----------|---------| | 1 | MEDICAL | 医疗 | ESSENTIAL | 3 月 | | 2 | EDUCATION | 教育 | ESSENTIAL | 6 月 | | 3 | PUBLIC_SECURITY | 公安 | ESSENTIAL | 12 月 | | 4 | AUTO | 汽车相关 | ESSENTIAL | 6 月 | | 5 | REAL_ESTATE | 房产服务 | ESSENTIAL | 12 月 | | 6 | TICKETING | 票务 | ESSENTIAL | 12 月 | | 7 | HANDYMAN | 多能工人 | ESSENTIAL | 6 月 | | 8 | APPLIANCE | 家电 | ESSENTIAL | 12 月 | | 9 | CATERING | 餐饮 | ESSENTIAL | 3 月 | | 10 | FOOD | 食品 | ESSENTIAL | 3 月 | | 11 | LEGAL | 法律 | ESSENTIAL | 12 月 | | 12 | WEEKEND_EXPERT | 周末达人 | ESSENTIAL | 1 月 | | 13 | GOVERNMENT | 政府综合 | IDEAL | 12 月 | | 14 | TAX | 税务 | IDEAL | 12 月 | | 15 | BUSINESS_ADMIN | 工商 | IDEAL | 12 月 | | 16 | BANK | 银行 | IDEAL | 6 月 | | 17 | MEDIA | 媒体 | IDEAL | 12 月 | | 18 | TRAVEL | 旅游 | IDEAL | 12 月 | | 19 | LEADING_ENTERPRISE | 领军企业 | IDEAL | 12 月 | | 20 | INDUSTRY_BENCHMARK | 行业标杆 | IDEAL | 12 月 | | 21 | OVERSEAS | 国外 | IDEAL | 12 月 | | 22 | UNIVERSITY | 高校 | IDEAL | 12 月 | | 23 | FINANCE | 金融 | IDEAL | 12 月 | | 24 | SENIOR_LOCAL | 资深土著 | IDEAL | 6 月 | | 25 | KEY_CITY | 北上广等关键城市 | IDEAL | 12 月 |