文档版本:2026-05-29
适用范围:backend/backendParse 数据库 + 客户群管理页筛选功能
GroupChat 表最初只为企微 Webhook 同步设计,仅保存:
客户群管理页需要按门店、活跃度、健康状态、文档状态筛选,但这些字段在数据库中不存在,导致:
在不破坏企微同步逻辑的前提下:
GroupChat 业务字段,支持筛选与展示Store 表,门店下拉从数据库读取Community 表,支持后续「小区—群—门店」关系维护| 原则 | 说明 |
|---|---|
| 分层存储 | 企微原始字段与业务扩展字段共存于 GroupChat,同步时只更新企微字段,不覆盖门店等业务字段 |
| 默认归属 | 新同步的群默认归属第一家门店(上海总部),后续由运营手工调整 |
| 指标可计算 | 活跃度、健康分根据消息数、成员数实时计算后写回数据库,供筛选使用 |
| 幂等启动 | Schema、种子门店、历史数据补全均在服务启动时自动执行,可重复运行 |
原有字段见 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', ...) 直接过滤,必须持久化。
| 字段 | 类型 | 说明 |
|---|---|---|
code |
String | 门店编码,如 SH001 |
name |
String | 门店名称,如「上海总部」 |
region |
String | 所属区域,如「华东」 |
status |
String | active / inactive |
种子数据(启动时自动写入):
| code | name | region |
|---|---|---|
| SH001 | 上海总部 | 华东 |
| BJ001 | 北京旗舰店 | 华北 |
| SZ001 | 深圳体验店 | 华南 |
用途: 客户群管理页「门店」下拉选项的数据来源。
| 字段 | 类型 | 说明 |
|---|---|---|
name |
String | 小区名称 |
storeId |
String | 归属门店 ID |
storeName |
String | 归属门店名称 |
address |
String | 地址 |
status |
String | active / inactive |
当前状态: 仅建表,暂无种子数据。
后续用途: 维护「小区—群—门店」对应关系(见功能清单模块 2)。
activityLevel| 条件 | 结果 |
|---|---|
| 今日消息 ≥ 20 | high |
| 今日消息 ≥ 5 | medium |
| 今日消息 ≥ 1 或成员数 > 0 | low |
| 其他 | inactive |
healthScore基础分 55
+ 成员数 ≥ 50 → +15;≥ 10 → +8
+ min(今日消息数, 15)
+ 有沟通记录文档 → +10
群已解散 → 0
最终限制在 0–100
注意:门店、小区、负责人姓名等字段不会自动填充占位值。数据库中无真实归属时保持为空,前端显示「—」。
healthStatus| 条件 | 结果 |
|---|---|
| 群已解散 或 健康分 < 50 | critical |
| 健康分 < 70 | warning |
| 其他 | healthy |
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 数组,供前端渲染门店下拉。
GET /api/qiwe/stores
返回所有 status=active 的门店。
服务启动顺序(src/index.ts):
ensureSchemas() — 建表/补字段(幂等)bootstrapAuth() — 用户与角色bootstrapOrganization() — 门店种子 + 历史群业务字段补全日志示例:
[Schema] GroupChat — updated
[Schema] Store — created
[Organization] 门店数据已同步:新增 3,更新 0
[Organization] 已补全 5 个群的业务字段默认值
| 操作 | 是否覆盖业务字段 |
|---|---|
从企微同步(sync-groups) |
否,仅更新群名/群主/成员数/头像/状态;不写入门店 |
| Webhook 群事件 | 否,新建群时不写入门店/小区占位值 |
列表查询(listGroupChats) |
会重算并写回活跃度/健康分/今日消息数 |
| 启动迁移 | 一次性清除历史占位门店/小区数据 |
Community 表录入数据后,提供接口维护 GroupChat.communityIdAppUser 增加 wecomUserId 字段,与 ownerId 关联hasDocument / documentPinnedGroupMember 入退群事件计算 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/ |
前端筛选接入 |