group-organization-schema.md 7.5 KB

客户群组织资产 — 数据库扩展设计说明

文档版本:2026-05-29
适用范围:backend/backend Parse 数据库 + 客户群管理页筛选功能


一、设计背景

1.1 原有问题

GroupChat 表最初只为企微 Webhook 同步设计,仅保存:

  • 群 ID、群名、群主、成员数、状态、设备 guid

客户群管理页需要按门店、活跃度、健康状态、文档状态筛选,但这些字段在数据库中不存在,导致:

  1. 筛选项写死在前端(上海总部 / 北京旗舰店 / 深圳体验店)
  2. 列表中的门店、活跃度、健康分由前端临时计算
  3. 筛选只在浏览器本地执行,无法按真实数据过滤

1.2 设计目标

在不破坏企微同步逻辑的前提下:

  1. 扩展 GroupChat 业务字段,支持筛选与展示
  2. 新增 Store 表,门店下拉从数据库读取
  3. 预留 Community 表,支持后续「小区—群—门店」关系维护
  4. 启动时自动建表、补字段、种子数据迁移

二、设计原则

原则 说明
分层存储 企微原始字段与业务扩展字段共存于 GroupChat,同步时只更新企微字段,不覆盖门店等业务字段
默认归属 新同步的群默认归属第一家门店(上海总部),后续由运营手工调整
指标可计算 活跃度、健康分根据消息数、成员数实时计算后写回数据库,供筛选使用
幂等启动 Schema、种子门店、历史数据补全均在服务启动时自动执行,可重复运行

三、表结构说明

3.1 GroupChat(群聊表 — 扩展字段)

原有字段见 database-design.md,此处仅列新增业务字段。

字段 类型 说明 数据来源
ownerName String 负责人显示名 暂用企微 ownerId,后续可关联用户表
storeId String 归属门店 ID(Parse objectId) 默认第一家门店;运营维护
storeName String 归属门店名称(冗余,便于展示) 同上
communityId String 归属小区 ID 运营维护,暂为空
communityName String 归属小区名称 运营维护,暂为空
activityLevel String 活跃度:high / medium / low / inactive 根据今日消息数计算
healthScore Number 健康分 0–100 根据成员数、消息数、文档状态计算
healthStatus String 健康状态:healthy / warning / critical 由健康分推导
hasDocument Boolean 是否已登记沟通记录文档 默认 false,合规模块后续写入
documentPinned Boolean 文档是否置顶 默认 false
documentInNotice Boolean 公告是否含文档链接 默认 false
messageCountToday Number 今日消息数 从 Message 表统计
memberChange24h Number 24 小时成员变化 默认 0,后续由成员事件计算

为什么门店信息冗余存储 storeName?
列表页高频展示,避免每次查询再关联 Store 表;storeId 仍作为筛选与关联主键。

为什么活跃度/健康分写入数据库而不是只在前端算?
筛选条件需要后端 Parse.Query.equalTo('activityLevel', ...) 直接过滤,必须持久化。


3.2 Store(门店表)— 新建

字段 类型 说明
code String 门店编码,如 SH001
name String 门店名称,如「上海总部」
region String 所属区域,如「华东」
status String active / inactive

种子数据(启动时自动写入):

code name region
SH001 上海总部 华东
BJ001 北京旗舰店 华北
SZ001 深圳体验店 华南

用途: 客户群管理页「门店」下拉选项的数据来源。


3.3 Community(小区表)— 新建(预留)

字段 类型 说明
name String 小区名称
storeId String 归属门店 ID
storeName String 归属门店名称
address String 地址
status String active / inactive

当前状态: 仅建表,暂无种子数据。
后续用途: 维护「小区—群—门店」对应关系(见功能清单模块 2)。


四、指标计算规则

4.1 活跃度 activityLevel

条件 结果
今日消息 ≥ 20 high
今日消息 ≥ 5 medium
今日消息 ≥ 1 或成员数 > 0 low
其他 inactive

4.2 健康分 healthScore

基础分 55
+ 成员数 ≥ 50 → +15;≥ 10 → +8
+ min(今日消息数, 15)
+ 有沟通记录文档 → +10
群已解散 → 0
最终限制在 0–100

注意:门店、小区、负责人姓名等字段不会自动填充占位值。数据库中无真实归属时保持为空,前端显示「—」。

4.3 健康状态 healthStatus

条件 结果
群已解散 或 健康分 < 50 critical
健康分 < 70 warning
其他 healthy

五、API 变更

5.1 群列表(支持筛选)

GET /api/qiwe/groups?storeId=&activityLevel=&healthStatus=&hasDocument=&q=
参数 说明
storeId 门店 Parse objectId
activityLevel high / medium / low / inactive
healthStatus healthy / warning / critical
hasDocument true / false
q 关键词(群名、负责人、小区、门店)

响应新增: stores 数组,供前端渲染门店下拉。

5.2 门店列表

GET /api/qiwe/stores

返回所有 status=active 的门店。


六、启动时自动执行

服务启动顺序(src/index.ts):

  1. ensureSchemas() — 建表/补字段(幂等)
  2. bootstrapAuth() — 用户与角色
  3. bootstrapOrganization() — 门店种子 + 历史群业务字段补全

日志示例:

[Schema] GroupChat — updated
[Schema] Store — created
[Organization] 门店数据已同步:新增 3,更新 0
[Organization] 已补全 5 个群的业务字段默认值

七、与企微同步的兼容性

操作 是否覆盖业务字段
从企微同步(sync-groups) 否,仅更新群名/群主/成员数/头像/状态;不写入门店
Webhook 群事件 否,新建群时不写入门店/小区占位值
列表查询(listGroupChats) 会重算并写回活跃度/健康分/今日消息数
启动迁移 一次性清除历史占位门店/小区数据

八、后续扩展建议

  1. 小区关联:在 Community 表录入数据后,提供接口维护 GroupChat.communityId
  2. 负责人姓名:在 AppUser 增加 wecomUserId 字段,与 ownerId 关联
  3. 文档状态:合规模块识别到沟通记录后,更新 hasDocument / documentPinned
  4. 成员变化:根据 GroupMember 入退群事件计算 memberChange24h

九、相关代码文件

文件 职责
src/shared/db/schema-setup.ts Schema 定义
src/shared/db/organization-bootstrap.ts 组织数据启动入口
src/apps/pc/qiwe/services/organization.service.ts 门店 CRUD
src/apps/pc/qiwe/services/groups.service.ts 群列表、指标计算、字段迁移
src/apps/pc/qiwe/utils/group-metrics.util.ts 活跃度/健康分算法
src/app/features/group-management/group-list/ 前端筛选接入