日期: 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 数据保留为「历史资源」,不迁移 |
连接类型分两层语义:
理想社会连接 = 必备社会连接(12 类)+ 理想扩展(13 类)= 共 25 类。
| 类型 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 月) |
| 类型 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 获取枚举(便于未来调整顺序/周期/文案)。
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 新增迁移):
connection_type = ''(空,标记为历史资源,不参与 25 类分组)priority = 2(默认中)value_score = 50(默认中)last_interaction_at = nullinteraction_count = 0type 字段保留不变(INFO/PLACE/SKILL 作为兼容字段)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='珍珠图互动日志';
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='珍珠图互动提醒';
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...
}
新增方法:
/** 计算并更新资源的价值分 */
public void recalculateValueScore(Long userId, Long itemId)
/** 自动计算价值分(纯函数,不写 DB) */
public int calculateValueScore(ResourceItem item, List<InteractionLog> logs)
// 类型基础权重:ESSENTIAL=30, IDEAL=20
// 近90天互动次数 × 5,上限 30
// 最近互动时效性:≤30天=+20, ≤90天=+10, ≤180天=+5
// 互动总量 × 2,上限 20
// 总分为 0-100
// 获取连接类型列表(供前端类型选择器使用)
@PostMapping("/connection-types")
public Result<List<Map<String, Object>>> getConnectionTypes()
// 记录互动
@PostMapping("/interaction/add")
public Result<Void> addInteraction(@RequestBody Map params, @RequestAttribute Long userId)
// params: itemId, interactionType, content
// 获取互动历史
@PostMapping("/interaction/list")
public Result<List<Map<String, Object>>> getInteractionList(
@RequestBody Map params, @RequestAttribute Long userId)
// params: itemId, page, size
// 获取待办提醒列表
@PostMapping("/reminder/list")
public Result<List<Map<String, Object>>> getReminders(@RequestAttribute Long userId)
// 更新资源优先级(手动覆盖)
@PostMapping("/item/update-priority")
public Result<Void> updatePriority(@RequestBody Map params, @RequestAttribute Long userId)
// params: itemId, priority (1/2/3)
@Scheduled(cron = "0 0 8 * * ?") // 每日 08:00
public void generateReminders() {
// 遍历所有用户的 25 类资源(connection_type 非空)
// 对每个资源:
// daysSinceLast = today - lastInteractionAt
// 若 daysSinceLast >= suggestIntervalMonth * 30
// 且不存在 PENDING 提醒 → 创建提醒记录
// 若 status = DONE/DISMISSED → 跳过
}
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// 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))
| 元素 | 样式 |
|---|---|
| 中心节点「我」 | 半径 36px,实心圆,深橙色 #F97316 |
| 内圈连线 | 从中心放射至每组弧心,透明度 30% |
| 外圈连线 | 同上,透明度 20% |
| 资源珍珠 | 实心圆,颜色 = 类型颜色,半径 = 优先级映射 |
| 高价值珍珠(优先级 1) | 加粗边框(2px 白色描边) |
| 分组标签 | 置于弧外侧,字号 20rpx,颜色 = 类型颜色 |
| 互动逾期珍珠 | 边缘显示红色角标「逾期N天」 |
现有流程:选类型(INFO/PLACE/SKILL)→ 填名称/描述 → 关联联系人 → 提交
新流程:
1. 底部弹出选择器:选择连接类型(25 类,分「必备」/「理想」两个分组 Tab)
2. 进入现有表单:名称、描述、关联联系人
3. 提交(connection_type 必填)
点击珍珠后弹窗新增:
/interaction/add/item/update-priority在珍珠图页面顶部新增「提醒」Tab:
{
"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 项
]
}
{
"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='' 的资源)
]
}
}
请求体:
{ "itemId": 1, "interactionType": "WECHAT", "content": "咨询孩子过敏问题" }
成功:{ "code": 200 }
返回当前用户所有 PENDING 状态的提醒。
打开珍珠图页
↓
调用 /api/pearl/resources
↓
渲染两层同心圆(内圈必备 12 组 / 外圈理想 13 组)
↓
点击珍珠
↓
弹窗:名称 / 类型 / 优先级 / 互动记录 / 按钮[联系][编辑][删除]
↓
点击「联系」
↓
底部弹出 ActionSheet(电话/微信/见面/其他)
↓
填写互动内容 → 提交 /api/pearl/interaction/add
↓
更新珍珠 radius(实时重绘)+ 刷新互动记录
↓
(定时任务)每日 08:00 检查互动频率 → 生成提醒
↓
珍珠边缘显示「逾期N天」角标 → 点击跳转提醒列表
DatabaseInitializer 新增迁移:
migration_XXX: 给 resource_items 添加新字段,存量数据填充默认值migration_XXX+1: 创建 pearl_interaction_log 表migration_XXX+2: 创建 pearl_interaction_reminder 表后端:
ConnectionType.java 枚举(25 类)ResourceService.java(新增价值分计算、互动、提醒方法)PearlController.java(新增 5 个接口)PearlReminderScheduler.java(定时任务)PearlInteractionLog.java、PearlInteractionReminder.java 实体 + Mapper前端:
PearlDiagram.vue(同心圆布局算法 + 半径计算)pearl-add-resource.vue(25 类类型选择器)api.js(新增 5 个 API 方法)测试验证:
mvn clean compile 无编译错误| 项目 | 要求 |
|---|---|
| 性能 | Canvas 单帧 < 16ms(25 组 × 平均 3-5 珍珠 ≈ 100 节点) |
| 兼容性 | iOS/Android 微信基础库 2.25.0+ |
| 空状态 | 无任何新类型资源时显示引导按钮「登记你的第一个社会连接」 |
| 异常处理 | 连接类型不在枚举 → 拦截报错;互动日志写入失败 → 回滚 + 提示 |
| 权限 | 复用现有 JWT 认证,仅本人可见 |
| # | 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 月 |