Merge branch 'main' into dev

# Conflicts:
#	docs/backstage/IM-Kafka接入方案.md
#	docs/backstage/IM-Kafka接入范围识别.md
#	server/api/admin/imconfiginternal/im_config.go
#	server/api/admin/imuser/im_user.go
#	server/internal/consts/im_kafka.go
#	server/internal/controller/admin/sys/im_user.go
#	server/internal/controller/internalapi/imconfig/im_config.go
#	server/internal/library/imadmin/client.go
#	server/internal/library/imadmin/client_test.go
#	server/internal/library/imadmin/internal.go
#	server/internal/library/imkafka/command.go
#	server/internal/library/imtenant/db.go
#	server/internal/library/imtenant/db_test.go
#	server/internal/library/queue/kafkamq.go
#	server/internal/library/queue/queue.go
#	server/internal/logic/admin/member.go
#	server/internal/logic/sys/im_dead_letter.go
#	server/internal/logic/sys/im_group.go
#	server/internal/logic/sys/im_invite.go
#	server/internal/logic/sys/im_kafka_audit.go
#	server/internal/logic/sys/im_kafka_command.go
#	server/internal/logic/sys/im_tenant.go
#	server/internal/logic/sys/im_user.go
#	server/internal/model/input/sysin/im_group.go
#	server/internal/model/input/sysin/im_invite.go
#	server/internal/model/input/sysin/im_user.go
#	server/internal/queues/im_admin_command_result.go
#	server/internal/queues/im_audit_event.go
#	server/internal/queues/im_dead_letter.go
#	server/internal/service/sys.go
#	server/manifest/config/config.example.yaml
#	server/storage/data/generate/im_group_menu.sql
#	server/storage/data/generate/im_user_menu.sql
#	server/storage/data/im_registration_invite_hotgo.sql
#	web/src/views/im/registrationInvite/BatchViewModal.vue
#	web/src/views/im/registrationInvite/index.vue
#	web/src/views/im/registrationInvite/model.ts
#	web/src/views/imDeadLetter/index.vue
#	web/src/views/imDeadLetter/model.ts
#	web/src/views/imGroup/governance.vue
#	web/src/views/imGroup/members.vue
#	web/src/views/imKafkaAudit/model.ts
#	web/src/views/imKafkaAudit/view.vue
#	web/src/views/imKafkaCommand/index.vue
#	web/src/views/imKafkaCommand/model.ts
#	web/src/views/imKafkaCommand/view.vue
#	web/src/views/imUser/ResetPasswordModal.vue
#	web/src/views/imUser/governance.vue
#	web/src/views/imUser/index.vue
#	web/src/views/imUser/model.ts
#	web/src/views/imUser/view.vue
This commit is contained in:
richjohnrambo
2026-09-13 23:43:51 +08:00
444 changed files with 46212 additions and 2158 deletions
@@ -0,0 +1,62 @@
{
"label": "数据概览",
"slug": "01-dashboard",
"route": "/dashboard",
"url": "https://admin.adfahi.cn/dashboard",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/01-dashboard.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n总用户数\n16\n活跃用户\n0\n总群组数\n6\n总会话数\n11\n总消息数\n220\n今日消息\n0\n用户增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0\n消息增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0",
"tables": [
{
"headers": [
"日期",
"数量"
],
"idx": 0,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
]
]
},
{
"headers": [
"日期",
"数量"
],
"idx": 1,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,78 @@
{
"label": "数据概览",
"slug": "01-dashboard",
"route": "/dashboard",
"url": "https://admin.adfahi.cn/dashboard",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/01-dashboard.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n总用户数\n16\n活跃用户\n0\n总群组数\n6\n总会话数\n11\n总消息数\n220\n今日消息\n0\n用户增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0\n消息增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0",
"tables": [
{
"headers": [
"日期",
"数量"
],
"idx": 0,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
],
[
"2026-08-31",
"0"
],
[
"2026-09-01",
"0"
]
]
},
{
"headers": [
"日期",
"数量"
],
"idx": 1,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
],
[
"2026-08-31",
"0"
],
[
"2026-09-01",
"0"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,866 @@
{
"label": "用户管理",
"slug": "02-user-management",
"route": "/users",
"url": "https://admin.adfahi.cn/users",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/02-user-management.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户管理\n编号\n用户分组\n全部\n状态\n全部\n关键词\n查 询\n重 置\n创建用户\n批量创建\n导入Excel\n下载模板\n编号\t头像\t用户名\t昵称\t手机号\t注册方式\t状态\t用户分组\t最近登录\t创建时间\t操作\n\n编号\n\t\n头像\n\t\n用户名\n\t\n昵称\n\t\n手机号\n\t\n注册方式\n\t\n状态\n\t\n用户分组\n\t\n最近登录\n\t\n创建时间\n\t\n操作\n\n16\t\n点击查看\n\thy73074162\tpoter001\t\t后台创建\t正常\t默认用户组\t2026-08-01 00:05:30\t2026-08-01 00:04:25\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n15\t\n点击查看\n\thy17716319\t往事随风\t\t后台创建\t正常\t默认用户组\t2026-08-01 21:16:15\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n14\t\n点击查看\n\thy34899108\t坚守的山羊\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n13\t\n点击查看\n\thy45257043\t沉稳的松鼠\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n12\t\n点击查看\n\thy69598195\t专注的白兔\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n11\t\n点击查看\n\thy20144467\t沉稳的黑猫\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n10\t\n点击查看\n\thy70299474\t坚韧的河马\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n9\t\n点击查看\n\thy23799134\t求真的猫头\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n8\t\n点击查看\n\thy18959373\t进取的北狐\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n7\t\n点击查看\n\thy80424632\t温润的乌鸦\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n6\t\n点击查看\n\thy98400466\t乐观的野马\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n5\t\n点击查看\n\thy99111080\tF\t13800001115\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:43:53\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n4\t\n点击查看\n\thy92120233\tF\t13800001114\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:43:37\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n3\t\n点击查看\n\thy69089908\tF\t13800001113\t后台创建\t正常\t默认用户组\t2026-07-31 23:26:17\t2026-07-31 22:43:12\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n2\t\n点击查看\n\thy91144536\t冯仁飞\t13800001112\t后台创建\t正常\t默认用户组\t2026-08-02 22:27:59\t2026-07-31 22:43:02\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n1\t\n点击查看\n\thy50703413\t冯宇昂\t13800001111\t后台创建\t正常\t默认用户组\t2026-07-31 23:37:42\t2026-07-31 22:42:45\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n1\n20 条/页",
"tables": [
{
"headers": [
"编号",
"头像",
"用户名",
"昵称",
"手机号",
"注册方式",
"状态",
"用户分组",
"最近登录",
"创建时间",
"操作",
"编号",
"头像",
"用户名",
"昵称",
"手机号",
"注册方式",
"状态",
"用户分组",
"最近登录",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"16",
"点击查看",
"hy73074162",
"poter001",
"后台创建",
"正常",
"默认用户组",
"2026-08-01 00:05:30",
"2026-08-01 00:04:25",
"详 情重置密码IP白名单调整分组封 禁强制下线强制注销"
],
[
"15",
"点击查看",
"hy17716319",
"往事随风",
"后台创建",
"正常",
"默认用户组",
"2026-08-01 21:16:15",
"2026-07-31 22:44:06",
"详 情重置密码IP白名单调整分组封 禁强制下线强制注销"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户编号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名/昵称/手机号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "创建用户"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量创建"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导入Excel"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载模板"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
{
"label": "用户分组",
"slug": "03-user-groups",
"route": "/user-policy-groups",
"url": "https://admin.adfahi.cn/user-policy-groups",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/03-user-groups.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户分组\n添加分组\nIP 白名单\n租户渠道 IP 白名单\nAPP:未配置\nWeb:未配置\n分组名称\t用户数\t允许加好友\t允许建群\t直接加好友\t好友上限\tAPP 登录\tWeb 登录\tIP 白名单\t状态\t权重\t操作\n\n分组名称\n\t\n用户数\n\t\n允许加好友\n\t\n允许建群\n\t\n直接加好友\n\t\n好友上限\n\t\nAPP 登录\n\t\nWeb 登录\n\t\nIP 白名单\n\t\n状态\n\t\n权重\n\t\n操作\n\n\n默认用户组\n默认\n\t16\t允许\t允许\t--\t不限\t允许\t允许\t未启用\t启用\t0\t\n编辑\n删除",
"tables": [
{
"headers": [
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作",
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作"
],
"idx": 0,
"rows": [
[],
[
"默认用户组默认",
"16",
"允许",
"允许",
"--",
"不限",
"允许",
"允许",
"未启用",
"启用",
"0",
"编辑删除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "添加分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP 白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "编辑"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "删除"
}
],
"resources": []
}
@@ -0,0 +1,108 @@
{
"label": "用户分组",
"slug": "03-user-groups",
"route": "/user-policy-groups",
"url": "https://admin.adfahi.cn/user-policy-groups",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/03-user-groups.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户分组\n添加分组\nIP 白名单\n租户渠道 IP 白名单\nAPP:未配置\nWeb:未配置\n分组名称\t用户数\t允许加好友\t允许建群\t直接加好友\t好友上限\tAPP 登录\tWeb 登录\tIP 白名单\t状态\t权重\t操作\n\n分组名称\n\t\n用户数\n\t\n允许加好友\n\t\n允许建群\n\t\n直接加好友\n\t\n好友上限\n\t\nAPP 登录\n\t\nWeb 登录\n\t\nIP 白名单\n\t\n状态\n\t\n权重\n\t\n操作\n\n\n默认用户组\n默认\n\t16\t允许\t允许\t--\t不限\t允许\t允许\t未启用\t启用\t0\t\n编辑\n删除",
"tables": [
{
"headers": [
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作",
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作"
],
"idx": 0,
"rows": [
[],
[
"默认用户组默认",
"16",
"允许",
"允许",
"--",
"不限",
"允许",
"允许",
"未启用",
"启用",
"0",
"编辑删除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "添加分组",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP 白名单",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "编辑",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "删除",
"value": ""
}
]
}
@@ -0,0 +1,296 @@
{
"label": "群组管理",
"slug": "04-group-management",
"route": "/groups",
"url": "https://admin.adfahi.cn/groups",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/04-group-management.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群组管理\n群号\n关键词\n状态\n全部\n查 询\n重 置\n群组编号\t群号\t群组名称\t成员数\t群主昵称\t群主用户名\t状态\t创建时间\t操作\n\n群组编号\n\t\n群号\n\t\n群组名称\n\t\n成员数\n\t\n群主昵称\n\t\n群主用户名\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n6\t68127154\t\n冯\n\t1\tF\thy69089908\t正常\t2026-07-31 22:50:52\t\n详 情\n群文件\n封 禁\n解 散\n\n5\t62262588\t\n冯\n\t3\tF\thy69089908\t正常\t2026-07-31 22:50:38\t\n详 情\n群文件\n封 禁\n解 散\n\n4\t75874312\t\n346亿916\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:57\t\n详 情\n群文件\n封 禁\n解 散\n\n3\t92851806\t\n64919\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:49\t\n详 情\n群文件\n封 禁\n解 散\n\n2\t89233971\t\n冯氏家族\n\t3\t冯仁飞\thy91144536\t正常\t2026-07-31 22:46:59\t\n详 情\n群文件\n封 禁\n解 散\n\n1\t17776111\t\n冯氏家族\n\t3\t冯宇昂\thy50703413\t正常\t2026-07-31 22:45:28\t\n详 情\n群文件\n封 禁\n解 散\n1\n20 条/页",
"tables": [
{
"headers": [
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作",
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"6",
"68127154",
"冯",
"1",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:52",
"详 情群文件封 禁解 散"
],
[
"5",
"62262588",
"冯",
"3",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:38",
"详 情群文件封 禁解 散"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "群名称",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,356 @@
{
"label": "群组管理",
"slug": "04-group-management",
"route": "/groups",
"url": "https://admin.adfahi.cn/groups",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/04-group-management.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群组管理\n群号\n关键词\n状态\n全部\n查 询\n重 置\n群组编号\t群号\t群组名称\t成员数\t群主昵称\t群主用户名\t状态\t创建时间\t操作\n\n群组编号\n\t\n群号\n\t\n群组名称\n\t\n成员数\n\t\n群主昵称\n\t\n群主用户名\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n6\t68127154\t\n冯\n\t1\tF\thy69089908\t正常\t2026-07-31 22:50:52\t\n详 情\n群文件\n封 禁\n解 散\n\n5\t62262588\t\n冯\n\t3\tF\thy69089908\t正常\t2026-07-31 22:50:38\t\n详 情\n群文件\n封 禁\n解 散\n\n4\t75874312\t\n346亿916\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:57\t\n详 情\n群文件\n封 禁\n解 散\n\n3\t92851806\t\n64919\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:49\t\n详 情\n群文件\n封 禁\n解 散\n\n2\t89233971\t\n冯氏家族\n\t3\t冯仁飞\thy91144536\t正常\t2026-07-31 22:46:59\t\n详 情\n群文件\n封 禁\n解 散\n\n1\t17776111\t\n冯氏家族\n\t3\t冯宇昂\thy50703413\t正常\t2026-07-31 22:45:28\t\n详 情\n群文件\n封 禁\n解 散\n1\n20 条/页",
"tables": [
{
"headers": [
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作",
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"6",
"68127154",
"冯",
"1",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:52",
"详 情群文件封 禁解 散"
],
[
"5",
"62262588",
"冯",
"3",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:38",
"详 情群文件封 禁解 散"
],
[
"4",
"75874312",
"346亿916",
"1",
"冯宇昂",
"hy50703413",
"正常",
"2026-07-31 22:47:57",
"详 情群文件封 禁解 散"
],
[
"3",
"92851806",
"64919",
"1",
"冯宇昂",
"hy50703413",
"正常",
"2026-07-31 22:47:49",
"详 情群文件封 禁解 散"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "群名称",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,113 @@
{
"label": "群成员管理",
"slug": "05-group-members",
"route": "/group-members",
"url": "https://admin.adfahi.cn/group-members",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/05-group-members.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群成员管理\n群号\n角色\n全部\n关键词\n查 询\n重 置\n刷 新\n编号\t用户名\t昵称\t群昵称\t角色\t禁言状态\t加入时间\t操作\n\n编号\n\t\n用户名\n\t\n昵称\n\t\n群昵称\n\t\n角色\n\t\n禁言状态\n\t\n加入时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作",
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "必填",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名/昵称",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
}
],
"resources": []
}
@@ -0,0 +1,173 @@
{
"label": "群成员管理",
"slug": "05-group-members",
"route": "/group-members",
"url": "https://admin.adfahi.cn/group-members",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/05-group-members.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群成员管理\n群号\n角色\n全部\n关键词\n查 询\n重 置\n刷 新\n编号\t用户名\t昵称\t群昵称\t角色\t禁言状态\t加入时间\t操作\n\n编号\n\t\n用户名\n\t\n昵称\n\t\n群昵称\n\t\n角色\n\t\n禁言状态\n\t\n加入时间\n\t\n操作\n\n3\thy69089908\tF\t--\t群主\t正常\t2026-07-31 22:50:52\t\n禁 言\n踢 出\n1\n20 条/页",
"tables": [
{
"headers": [
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作",
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"3",
"hy69089908",
"F",
"--",
"群主",
"正常",
"2026-07-31 22:50:52",
"禁 言踢 出"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "必填",
"tag": "INPUT",
"text": "",
"value": "68127154"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名/昵称",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "禁 言",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "踢 出",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,103 @@
{
"label": "邀请码管理",
"slug": "06-invite-codes",
"route": "/invite-codes",
"url": "https://admin.adfahi.cn/invite-codes",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/06-invite-codes.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n邀请码管理\n状态\n全部\n邀请码\n查 询\n重 置\n生成邀请码\n每次生成 1 个邀请码,可多次使用。可指定企业与用户;勾选「不限制时间」则永久有效;开启「自动加好友」后,使用该码注册的用户将直接成为指定用户的好友。点击「查看」可查看使用该邀请码注册的用户列表。\n邀请码\t指定用户\t自动加好友\t使用人数\t创建人\t状态\t过期时间\t创建时间\t操作\n\n邀请码\n\t\n指定用户\n\t\n自动加好友\n\t\n使用人数\n\t\n创建人\n\t\n状态\n\t\n过期时间\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作",
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "6 位数字",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "生成邀请码"
}
],
"resources": []
}
@@ -0,0 +1,162 @@
{
"label": "邀请码管理",
"slug": "06-invite-codes",
"route": "/invite-codes",
"url": "https://admin.adfahi.cn/invite-codes",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/06-invite-codes.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n邀请码管理\n状态\n全部\n邀请码\n查 询\n重 置\n生成邀请码\n每次生成 1 个邀请码,可多次使用。可指定企业与用户;勾选「不限制时间」则永久有效;开启「自动加好友」后,使用该码注册的用户将直接成为指定用户的好友。点击「查看」可查看使用该邀请码注册的用户列表。\n邀请码\t指定用户\t自动加好友\t使用人数\t创建人\t状态\t过期时间\t创建时间\t操作\n\n邀请码\n\t\n指定用户\n\t\n自动加好友\n\t\n使用人数\n\t\n创建人\n\t\n状态\n\t\n过期时间\n\t\n创建时间\n\t\n操作\n\n323441\t往事随风 (#2271)\t否\t0\t16\t启用\t2026-09-05 00:34:55\t2026-09-04 00:34:55\t\n查 看\n删 除\n1\n20 条/页",
"tables": [
{
"headers": [
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作",
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"323441",
"往事随风 (#2271)",
"否",
"0",
"16",
"启用",
"2026-09-05 00:34:55",
"2026-09-04 00:34:55",
"查 看删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "6 位数字",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "生成邀请码",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 看",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,171 @@
{
"label": "单聊消息",
"slug": "07-direct-messages",
"route": "/private-messages",
"url": "https://admin.adfahi.cn/private-messages",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/07-direct-messages.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n单聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n接收人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收人\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n接收人\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
}
],
"resources": []
}
@@ -0,0 +1,393 @@
{
"label": "单聊消息",
"slug": "07-direct-messages",
"route": "/private-messages",
"url": "https://admin.adfahi.cn/private-messages",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/07-direct-messages.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n单聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n接收人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收人\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n接收人\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t系统\t\n语音通话 00:13\n\t正常\t2026-07-31 23:02:22\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t系统\t\n语音通话 00:01\n\t正常\t2026-07-31 23:01:45\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t系统\t\n语音通话 已取消\n\t正常\t2026-07-31 23:01:36\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t系统\t\n语音通话 已拒绝\n\t正常\t2026-07-31 23:01:19\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 已取消\n\t正常\t2026-07-31 23:00:20\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 00:19\n\t正常\t2026-07-31 22:59:59\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n语音通话 00:07\n\t正常\t2026-07-31 22:59:32\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t系统\t\n语音通话 已取消\n\t正常\t2026-07-31 22:59:05\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n语音通话 00:15\n\t正常\t2026-07-31 22:58:42\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 00:07\n\t正常\t2026-07-31 22:58:20\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 已取消\n\t正常\t2026-07-31 22:58:07\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t文本\t\n红心\n\t正常\t2026-07-31 22:51:35\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t文本\t\n1\n\t正常\t2026-07-31 22:51:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t文本\t\n你想让我\n\t正常\t2026-07-31 22:51:32\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t文本\t\n1\n\t正常\t2026-07-31 22:51:32\t\n删 除\n1\n2\n3\n4\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"Fhy69089908",
"冯仁飞hy91144536",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯宇昂hy50703413",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯宇昂hy50703413",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:05:19",
"删 除"
],
[
"Fhy69089908",
"冯仁飞hy91144536",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:04:12",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,159 @@
{
"label": "群聊消息",
"slug": "08-group-messages",
"route": "/group-messages",
"url": "https://admin.adfahi.cn/group-messages",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/08-group-messages.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t群名称\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n群名称\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
}
],
"resources": []
}
@@ -0,0 +1,379 @@
{
"label": "群聊消息",
"slug": "08-group-messages",
"route": "/group-messages",
"url": "https://admin.adfahi.cn/group-messages",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/08-group-messages.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t群名称\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n群名称\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\nF\nhy69089908\n\t\n冯\n68127154\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n89233971\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n62262588\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n89233971\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n68127154\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n68127154\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n89233971\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n62262588\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:03:50\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n😊😊😊😊\n\t正常\t2026-07-31 22:53:42\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nkddkkffjc\n\t正常\t2026-07-31 22:53:18\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nkdododdk\n\t正常\t2026-07-31 22:53:17\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkdkkddoso\n\t正常\t2026-07-31 22:53:16\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkkfkfkd\n\t正常\t2026-07-31 22:53:15\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkfkkdod\n\t正常\t2026-07-31 22:53:14\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t文本\t\n敏敏\n\t正常\t2026-07-31 22:53:13\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkfkfkdo\n\t正常\t2026-07-31 22:53:13\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t文本\t\n您提着\n\t正常\t2026-07-31 22:53:12\t\n删 除\n1\n2\n3\n4\n5\n•••\n8\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"Fhy69089908",
"冯68127154",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯氏家族89233971",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯氏家族17776111",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯62262588",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:05:19",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,135 @@
{
"label": "敏感词库",
"slug": "09-sensitive-words",
"route": "/sensitive-words",
"url": "https://admin.adfahi.cn/sensitive-words",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/09-sensitive-words.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n敏感词库\n状态\n全部\n等级\n全部\n关键词\n查 询\n重 置\n新增敏感词\n批量导入\n文件导入\n批量导出\nID\t敏感词\t等级\t替换词\t状态\t创建时间\t操作\n\nID\n\t\n敏感词\n\t\n等级\n\t\n替换词\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作",
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "敏感词",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "新增敏感词"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导入"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "文件导入"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导出"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,150 @@
{
"label": "敏感词库",
"slug": "09-sensitive-words",
"route": "/sensitive-words",
"url": "https://admin.adfahi.cn/sensitive-words",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/09-sensitive-words.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n敏感词库\n状态\n全部\n等级\n全部\n关键词\n查 询\n重 置\n新增敏感词\n批量导入\n文件导入\n批量导出\nID\t敏感词\t等级\t替换词\t状态\t创建时间\t操作\n\nID\n\t\n敏感词\n\t\n等级\n\t\n替换词\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作",
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "敏感词",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "新增敏感词",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导入",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "文件导入",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导出",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
+288
View File
@@ -0,0 +1,288 @@
{
"label": "文件管理",
"slug": "10-files",
"route": "/file-records",
"url": "https://admin.adfahi.cn/file-records",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/10-files.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n文件管理\n全部\n图片\n视频\n音频\n文档\n其他\n发送人用户名\n文件名\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收方\t类型\t内容\t文件名\t文件大小\tURL\t状态\t发送时间\t操作\n\n发送人\n\t\n接收方\n\t\n类型\n\t\n内容\n\t\n文件名\n\t\n文件大小\n\t\nURL\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n64919\n92851806\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n346亿916\n75874312\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:46:46\t\n删 除\n1\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作",
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"冯宇昂hy50703413",
"Fhy69089908",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"冯氏家族17776111",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "搜索文件名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,348 @@
{
"label": "文件管理",
"slug": "10-files",
"route": "/file-records",
"url": "https://admin.adfahi.cn/file-records",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/10-files.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n文件管理\n全部\n图片\n视频\n音频\n文档\n其他\n发送人用户名\n文件名\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收方\t类型\t内容\t文件名\t文件大小\tURL\t状态\t发送时间\t操作\n\n发送人\n\t\n接收方\n\t\n类型\n\t\n内容\n\t\n文件名\n\t\n文件大小\n\t\nURL\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n64919\n92851806\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n346亿916\n75874312\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:46:46\t\n删 除\n1\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作",
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"冯宇昂hy50703413",
"Fhy69089908",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"冯氏家族17776111",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"6491992851806",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"346亿91675874312",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "搜索文件名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
+141
View File
@@ -0,0 +1,141 @@
{
"label": "用户反馈",
"slug": "11-feedback",
"route": "/feedbacks",
"url": "https://admin.adfahi.cn/feedbacks",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/11-feedback.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户反馈\n用户ID\n状态\n全部\n优先级\n全部\n关键词\n查 询\n重 置\n批量处理\n导出 CSV\n\t用户\t优先级\t内容\t状态\t处理人\t创建时间\t操作\n\n\t\n用户\n\t\n优先级\n\t\n内容\n\t\n状态\n\t\n处理人\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作",
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户ID",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "内容/联系方式",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "批量处理"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV"
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,157 @@
{
"label": "用户反馈",
"slug": "11-feedback",
"route": "/feedbacks",
"url": "https://admin.adfahi.cn/feedbacks",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/11-feedback.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户反馈\n用户ID\n状态\n全部\n优先级\n全部\n关键词\n查 询\n重 置\n批量处理\n导出 CSV\n\t用户\t优先级\t内容\t状态\t处理人\t创建时间\t操作\n\n\t\n用户\n\t\n优先级\n\t\n内容\n\t\n状态\n\t\n处理人\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作",
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户ID",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "内容/联系方式",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "批量处理",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
}
]
}
@@ -0,0 +1,180 @@
{
"label": "操作日志",
"slug": "12-operation-logs",
"route": "/audit-logs",
"url": "https://admin.adfahi.cn/audit-logs",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/12-operation-logs.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n操作日志\n动作\n全部\n目标类型\n全部\n目标用户\n目标群\n开始时间\n结束时间\n查询\n重 置\n导出 CSV\n管理员\t动作\t目标类型\t目标\t查询条件\tIP\t时间\n\n管理员\n\t\n动作\n\t\n目标类型\n\t\n目标\n\t\n查询条件\n\t\nIP\n\t\n时间\n\nheiyue\t创建用户\t用户\t\npoter001\nhy73074162\n\t\n--\n\t104.30.173.163\t2026-08-01 00:04:25\nheiyue\t重置密码\t用户\t\n往事随风\nhy17716319\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:23\nheiyue\t重置密码\t用户\t\n坚守的山羊\nhy34899108\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:19\nheiyue\t重置密码\t用户\t\n沉稳的松鼠\nhy45257043\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:16\nheiyue\t重置密码\t用户\t\n专注的白兔\nhy69598195\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:12\nheiyue\t重置密码\t用户\t\n沉稳的黑猫\nhy20144467\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:09\nheiyue\t重置密码\t用户\t\n坚韧的河马\nhy70299474\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:05\nheiyue\t重置密码\t用户\t\n求真的猫头\nhy23799134\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:00\nheiyue\t重置密码\t用户\t\n进取的北狐\nhy18959373\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:55\nheiyue\t重置密码\t用户\t\n温润的乌鸦\nhy80424632\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:51\nheiyue\t重置密码\t用户\t\n乐观的野马\nhy98400466\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:48\nheiyue\t批量创建用户\t用户\t--\t\n详情\n\t141.164.107.33\t2026-07-31 22:44:06\nheiyue\t创建用户\t用户\t\nF\nhy99111080\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:53\nheiyue\t创建用户\t用户\t\nF\nhy92120233\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:37\nheiyue\t创建用户\t用户\t\nF\nhy69089908\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:12\nheiyue\t创建用户\t用户\t\n冯仁飞\nhy91144536\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:02\nheiyue\t创建用户\t用户\t\n冯宇昂\nhy50703413\n\t\n--\n\t141.164.107.33\t2026-07-31 22:42:45\n1\n20 条/页",
"tables": [
{
"headers": [
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间",
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"创建用户",
"用户",
"poter001hy73074162",
"--",
"104.30.173.163",
"2026-08-01 00:04:25"
],
[
"heiyue",
"重置密码",
"用户",
"往事随风hy17716319",
"--",
"141.164.107.33",
"2026-07-31 22:45:23"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详情"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,218 @@
{
"label": "操作日志",
"slug": "12-operation-logs",
"route": "/audit-logs",
"url": "https://admin.adfahi.cn/audit-logs",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/12-operation-logs.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n操作日志\n动作\n全部\n目标类型\n全部\n目标用户\n目标群\n开始时间\n结束时间\n查询\n重 置\n导出 CSV\n管理员\t动作\t目标类型\t目标\t查询条件\tIP\t时间\n\n管理员\n\t\n动作\n\t\n目标类型\n\t\n目标\n\t\n查询条件\n\t\nIP\n\t\n时间\n\nheiyue\t创建用户\t用户\t\npoter001\nhy73074162\n\t\n--\n\t104.30.173.163\t2026-08-01 00:04:25\nheiyue\t重置密码\t用户\t\n往事随风\nhy17716319\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:23\nheiyue\t重置密码\t用户\t\n坚守的山羊\nhy34899108\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:19\nheiyue\t重置密码\t用户\t\n沉稳的松鼠\nhy45257043\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:16\nheiyue\t重置密码\t用户\t\n专注的白兔\nhy69598195\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:12\nheiyue\t重置密码\t用户\t\n沉稳的黑猫\nhy20144467\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:09\nheiyue\t重置密码\t用户\t\n坚韧的河马\nhy70299474\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:05\nheiyue\t重置密码\t用户\t\n求真的猫头\nhy23799134\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:00\nheiyue\t重置密码\t用户\t\n进取的北狐\nhy18959373\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:55\nheiyue\t重置密码\t用户\t\n温润的乌鸦\nhy80424632\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:51\nheiyue\t重置密码\t用户\t\n乐观的野马\nhy98400466\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:48\nheiyue\t批量创建用户\t用户\t--\t\n详情\n\t141.164.107.33\t2026-07-31 22:44:06\nheiyue\t创建用户\t用户\t\nF\nhy99111080\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:53\nheiyue\t创建用户\t用户\t\nF\nhy92120233\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:37\nheiyue\t创建用户\t用户\t\nF\nhy69089908\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:12\nheiyue\t创建用户\t用户\t\n冯仁飞\nhy91144536\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:02\nheiyue\t创建用户\t用户\t\n冯宇昂\nhy50703413\n\t\n--\n\t141.164.107.33\t2026-07-31 22:42:45\n1\n20 条/页",
"tables": [
{
"headers": [
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间",
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"创建用户",
"用户",
"poter001hy73074162",
"--",
"104.30.173.163",
"2026-08-01 00:04:25"
],
[
"heiyue",
"重置密码",
"用户",
"往事随风hy17716319",
"--",
"141.164.107.33",
"2026-07-31 22:45:23"
],
[
"heiyue",
"重置密码",
"用户",
"坚守的山羊hy34899108",
"--",
"141.164.107.33",
"2026-07-31 22:45:19"
],
[
"heiyue",
"重置密码",
"用户",
"沉稳的松鼠hy45257043",
"--",
"141.164.107.33",
"2026-07-31 22:45:16"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详情",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,172 @@
{
"label": "群事件审计",
"slug": "14-group-event-audit",
"route": "/group-events",
"url": "https://admin.adfahi.cn/group-events",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/14-group-event-audit.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群事件审计\n群号\n操作人\n目标用户\n事件类型\n全部\n开始时间\n结束时间\n查询\n重 置\n群名称\t事件类型\t操作人\t目标用户\t可见范围\t旧值\t新值\t时间\n\n群名称\n\t\n事件类型\n\t\n操作人\n\t\n目标用户\n\t\n可见范围\n\t\n旧值\n\t\n新值\n\t\n时间\n\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n68127154\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:52\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯\n62262588\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:38\n\n冯氏家族\n17776111\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:34\n\n346亿916\n75874312\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:57\n\n64919\n92851806\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:50\n\n冯氏家族\n17776111\n\t成员加入\t\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 22:47:19\n\n冯氏家族\n89233971\n\t群创建\t\n冯仁飞\nhy91144536\n\t--\t全员可见\t--\t--\t2026-07-31 22:46:59\n\n冯氏家族\n17776111\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:45:28\n1\n20 条/页",
"tables": [
{
"headers": [
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间",
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间"
],
"idx": 0,
"rows": [
[],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯宇昂hy50703413",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯仁飞hy91144536",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,210 @@
{
"label": "群事件审计",
"slug": "14-group-event-audit",
"route": "/group-events",
"url": "https://admin.adfahi.cn/group-events",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/14-group-event-audit.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群事件审计\n群号\n操作人\n目标用户\n事件类型\n全部\n开始时间\n结束时间\n查询\n重 置\n群名称\t事件类型\t操作人\t目标用户\t可见范围\t旧值\t新值\t时间\n\n群名称\n\t\n事件类型\n\t\n操作人\n\t\n目标用户\n\t\n可见范围\n\t\n旧值\n\t\n新值\n\t\n时间\n\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n68127154\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:52\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯\n62262588\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:38\n\n冯氏家族\n17776111\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:34\n\n346亿916\n75874312\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:57\n\n64919\n92851806\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:50\n\n冯氏家族\n17776111\n\t成员加入\t\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 22:47:19\n\n冯氏家族\n89233971\n\t群创建\t\n冯仁飞\nhy91144536\n\t--\t全员可见\t--\t--\t2026-07-31 22:46:59\n\n冯氏家族\n17776111\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:45:28\n1\n20 条/页",
"tables": [
{
"headers": [
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间",
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间"
],
"idx": 0,
"rows": [
[],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯宇昂hy50703413",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯仁飞hy91144536",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
],
[
"冯68127154",
"群创建",
"Fhy69089908",
"--",
"全员可见",
"--",
"--",
"2026-07-31 22:50:52"
],
[
"冯氏家族89233971",
"成员加入",
"冯仁飞hy91144536",
"冯宇昂hy50703413",
"全员可见",
"--",
"--",
"2026-07-31 22:50:43"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,102 @@
{
"label": "管理员登录",
"slug": "15-admin-login",
"route": "/admin-login-history",
"url": "https://admin.adfahi.cn/admin-login-history",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/15-admin-login.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n管理员登录历史\n开始时间\n结束时间\n查询\n用户名\t登录时间\tUser-Agent\tIP 地址\n\n用户名\n\t\n登录时间\n\t\nUser-Agent\n\t\nIP 地址\n\nheiyue\t2026-09-04 00:10:57\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36\t103.136.251.99\nheiyue\t2026-08-01 00:03:52\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 22:47:31\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t141.164.107.235\nheiyue\t2026-07-31 22:42:09\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t141.164.107.33\nheiyue\t2026-07-31 21:32:28\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 00:45:59\tMozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1\t203.160.80.152\n1\n20 条/页",
"tables": [
{
"headers": [
"用户名",
"登录时间",
"User-Agent",
"IP 地址",
"用户名",
"登录时间",
"User-Agent",
"IP 地址"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"2026-09-04 00:10:57",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
"103.136.251.99"
],
[
"heiyue",
"2026-08-01 00:03:52",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
"104.30.173.163"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,123 @@
{
"label": "管理员登录",
"slug": "15-admin-login",
"route": "/admin-login-history",
"url": "https://admin.adfahi.cn/admin-login-history",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/15-admin-login.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n管理员登录历史\n开始时间\n结束时间\n查询\n用户名\t登录时间\tUser-Agent\tIP 地址\n\n用户名\n\t\n登录时间\n\t\nUser-Agent\n\t\nIP 地址\n\nheiyue\t2026-09-04 00:10:57\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36\t103.136.251.99\nheiyue\t2026-08-01 00:03:52\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 22:47:31\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t141.164.107.235\nheiyue\t2026-07-31 22:42:09\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t141.164.107.33\nheiyue\t2026-07-31 21:32:28\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 00:45:59\tMozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1\t203.160.80.152\n1\n20 条/页",
"tables": [
{
"headers": [
"用户名",
"登录时间",
"User-Agent",
"IP 地址",
"用户名",
"登录时间",
"User-Agent",
"IP 地址"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"2026-09-04 00:10:57",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
"103.136.251.99"
],
[
"heiyue",
"2026-08-01 00:03:52",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
"104.30.173.163"
],
[
"heiyue",
"2026-07-31 22:47:31",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
"141.164.107.235"
],
[
"heiyue",
"2026-07-31 22:42:09",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0",
"141.164.107.33"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,202 @@
{
"label": "用户登录",
"slug": "16-user-login",
"route": "/user-login-history",
"url": "https://admin.adfahi.cn/user-login-history",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/16-user-login.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户登录历史\n用户编号\n用户名\n开始时间\n结束时间\nIP\n查询\n重 置\n用户编号\t用户名\t当前状态\t登录时间\t设备类型\t设备型号\t系统版本\tApp 版本\tIP 地址\t归属地\t操作\n\n用户编号\n\t\n用户名\n\t\n当前状态\n\t\n登录时间\n\t\n设备类型\n\t\n设备型号\n\t\n系统版本\n\t\nApp 版本\n\t\nIP 地址\n\t\n归属地\n\t\n操作\n\n16\thy73074162\t正常\t2026-08-01 00:04:47\tweb\tChrome Browser\tMac OS X 10.15.7\t-\t104.30.173.163\t美国 加利福尼亚州 罗斯维尔\t\n快速封禁\n\n15\thy17716319\t正常\t2026-07-31 22:50:19\tandroid\tOPPO PCHM10\tAndroid 11\t1.1.2\t141.164.107.33\t老挝 万象市 永珍\t\n快速封禁\n\n3\thy69089908\t正常\t2026-07-31 22:47:10\tandroid\tOPPO PKW110\tAndroid 16\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n2\thy91144536\t正常\t2026-07-31 22:45:28\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:45:10\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:44:44\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:43:38\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n1\n20 条/页",
"tables": [
{
"headers": [
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作",
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作"
],
"idx": 0,
"rows": [
[],
[
"16",
"hy73074162",
"正常",
"2026-08-01 00:04:47",
"web",
"Chrome Browser",
"Mac OS X 10.15.7",
"-",
"104.30.173.163",
"美国 加利福尼亚州 罗斯维尔",
"快速封禁"
],
[
"15",
"hy17716319",
"正常",
"2026-07-31 22:50:19",
"android",
"OPPO PCHM10",
"Android 11",
"1.1.2",
"141.164.107.33",
"老挝 万象市 永珍",
"快速封禁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "登录IP",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,249 @@
{
"label": "用户登录",
"slug": "16-user-login",
"route": "/user-login-history",
"url": "https://admin.adfahi.cn/user-login-history",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/16-user-login.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户登录历史\n用户编号\n用户名\n开始时间\n结束时间\nIP\n查询\n重 置\n用户编号\t用户名\t当前状态\t登录时间\t设备类型\t设备型号\t系统版本\tApp 版本\tIP 地址\t归属地\t操作\n\n用户编号\n\t\n用户名\n\t\n当前状态\n\t\n登录时间\n\t\n设备类型\n\t\n设备型号\n\t\n系统版本\n\t\nApp 版本\n\t\nIP 地址\n\t\n归属地\n\t\n操作\n\n16\thy73074162\t正常\t2026-08-01 00:04:47\tweb\tChrome Browser\tMac OS X 10.15.7\t-\t104.30.173.163\t美国 加利福尼亚州 罗斯维尔\t\n快速封禁\n\n15\thy17716319\t正常\t2026-07-31 22:50:19\tandroid\tOPPO PCHM10\tAndroid 11\t1.1.2\t141.164.107.33\t老挝 万象市 永珍\t\n快速封禁\n\n3\thy69089908\t正常\t2026-07-31 22:47:10\tandroid\tOPPO PKW110\tAndroid 16\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n2\thy91144536\t正常\t2026-07-31 22:45:28\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:45:10\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:44:44\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:43:38\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n1\n20 条/页",
"tables": [
{
"headers": [
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作",
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作"
],
"idx": 0,
"rows": [
[],
[
"16",
"hy73074162",
"正常",
"2026-08-01 00:04:47",
"web",
"Chrome Browser",
"Mac OS X 10.15.7",
"-",
"104.30.173.163",
"美国 加利福尼亚州 罗斯维尔",
"快速封禁"
],
[
"15",
"hy17716319",
"正常",
"2026-07-31 22:50:19",
"android",
"OPPO PCHM10",
"Android 11",
"1.1.2",
"141.164.107.33",
"老挝 万象市 永珍",
"快速封禁"
],
[
"3",
"hy69089908",
"正常",
"2026-07-31 22:47:10",
"android",
"OPPO PKW110",
"Android 16",
"1.1.2",
"61.153.184.95",
"浙江 绍兴",
"快速封禁"
],
[
"2",
"hy91144536",
"正常",
"2026-07-31 22:45:28",
"android",
"HONOR LOG-AN00",
"Android 15",
"1.1.2",
"61.153.184.95",
"浙江 绍兴",
"快速封禁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "登录IP",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,123 @@
{
"label": "IP 账号统计",
"slug": "17-ip-account-stats",
"route": "/ip-user-stats",
"url": "https://admin.adfahi.cn/ip-user-stats",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/17-ip-account-stats.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\nIP 账号统计\nIP\n查询\n重 置\nIP 地址\t归属地\t关联用户\t注册用户\t登录用户\t正常\t已封禁\t最近活动\t操作\n\nIP 地址\n\t\n归属地\n\t\n关联用户\n\t\n注册用户\n\t\n登录用户\n\t\n正常\n\t\n已封禁\n\t\n最近活动\n\t\n操作\n\n61.153.184.95\t浙江 绍兴\t\n3\n多账号\n\t0\t3\t3\t0\t2026-08-02 22:27:59\t\n用户列表\n共 1 个 IP\n1\n20 条/页",
"tables": [
{
"headers": [
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作",
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作"
],
"idx": 0,
"rows": [
[],
[
"61.153.184.95",
"浙江 绍兴",
"3多账号",
"0",
"3",
"3",
"0",
"2026-08-02 22:27:59",
"用户列表"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "IPv4 或 IPv6",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "用户列表"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,134 @@
{
"label": "IP 账号统计",
"slug": "17-ip-account-stats",
"route": "/ip-user-stats",
"url": "https://admin.adfahi.cn/ip-user-stats",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/17-ip-account-stats.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\nIP 账号统计\nIP\n查询\n重 置\nIP 地址\t归属地\t关联用户\t注册用户\t登录用户\t正常\t已封禁\t最近活动\t操作\n\nIP 地址\n\t\n归属地\n\t\n关联用户\n\t\n注册用户\n\t\n登录用户\n\t\n正常\n\t\n已封禁\n\t\n最近活动\n\t\n操作\n\n61.153.184.95\t浙江 绍兴\t\n3\n多账号\n\t0\t3\t3\t0\t2026-08-02 22:27:59\t\n用户列表\n共 1 个 IP\n1\n20 条/页",
"tables": [
{
"headers": [
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作",
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作"
],
"idx": 0,
"rows": [
[],
[
"61.153.184.95",
"浙江 绍兴",
"3多账号",
"0",
"3",
"3",
"0",
"2026-08-02 22:27:59",
"用户列表"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "IPv4 或 IPv6",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "用户列表",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,156 @@
{
"label": "登录失败日志",
"slug": "18-login-failures",
"route": "/login-failure-logs",
"url": "https://admin.adfahi.cn/login-failure-logs",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/18-login-failures.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n登录失败日志\n类型\n全部\n登录账号\nIP\n开始时间\n结束时间\n查询\n重 置\n类型\t登录账号\t失败原因\tIP 地址\tUser-Agent\t时间\t操作\n\n类型\n\t\n登录账号\n\t\n失败原因\n\t\nIP 地址\n\t\nUser-Agent\n\t\n时间\n\t\n操作\n\n用户\t13800001111\t密码错误\t106.119.66.106\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t2026-08-01 01:31:20\t\n解锁\n\n用户\t13800001111\t密码错误\t61.153.184.95\tokhttp/4.12.0\t2026-07-31 22:44:36\t\n解锁\n1\n20 条/页",
"tables": [
{
"headers": [
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作",
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"用户",
"13800001111",
"密码错误",
"106.119.66.106",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0",
"2026-08-01 01:31:20",
"解锁"
],
[
"用户",
"13800001111",
"密码错误",
"61.153.184.95",
"okhttp/4.12.0",
"2026-07-31 22:44:36",
"解锁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,172 @@
{
"label": "登录失败日志",
"slug": "18-login-failures",
"route": "/login-failure-logs",
"url": "https://admin.adfahi.cn/login-failure-logs",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/18-login-failures.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n登录失败日志\n类型\n全部\n登录账号\nIP\n开始时间\n结束时间\n查询\n重 置\n类型\t登录账号\t失败原因\tIP 地址\tUser-Agent\t时间\t操作\n\n类型\n\t\n登录账号\n\t\n失败原因\n\t\nIP 地址\n\t\nUser-Agent\n\t\n时间\n\t\n操作\n\n用户\t13800001111\t密码错误\t106.119.66.106\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t2026-08-01 01:31:20\t\n解锁\n\n用户\t13800001111\t密码错误\t61.153.184.95\tokhttp/4.12.0\t2026-07-31 22:44:36\t\n解锁\n1\n20 条/页",
"tables": [
{
"headers": [
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作",
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"用户",
"13800001111",
"密码错误",
"106.119.66.106",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0",
"2026-08-01 01:31:20",
"解锁"
],
[
"用户",
"13800001111",
"密码错误",
"61.153.184.95",
"okhttp/4.12.0",
"2026-07-31 22:44:36",
"解锁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,67 @@
{
"label": "私域设置",
"slug": "19-private-domain-settings",
"route": "/tenant-settings",
"url": "https://admin.adfahi.cn/tenant-settings",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/19-private-domain-settings.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n私域设置\n私域设置 - 黑月测试\n消息字符上限\n范围 1~5000,仅允许调低\n图片大小上限(MB)\n范围 1~1000 MB\n文件大小上限(MB)\n范围 1~1000 MB,默认 50 MB\n最大撤回消息时间(分钟)\n群主/管理员撤回自己或他人消息的最长时限,范围 1~1440 分钟;普通用户撤回自己的消息固定为 2 分钟\n是否开启手机验证码\n开启\n关闭\n关闭后,注册仅支持邀请码,短信登录与忘记密码不可用\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "开启关闭"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置"
}
],
"resources": []
}
@@ -0,0 +1,75 @@
{
"label": "私域设置",
"slug": "19-private-domain-settings",
"route": "/tenant-settings",
"url": "https://admin.adfahi.cn/tenant-settings",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/19-private-domain-settings.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n私域设置\n私域设置 - 黑月测试\n消息字符上限\n范围 1~5000,仅允许调低\n图片大小上限(MB)\n范围 1~1000 MB\n文件大小上限(MB)\n范围 1~1000 MB,默认 50 MB\n最大撤回消息时间(分钟)\n群主/管理员撤回自己或他人消息的最长时限,范围 1~1440 分钟;普通用户撤回自己的消息固定为 2 分钟\n是否开启手机验证码\n开启\n关闭\n关闭后,注册仅支持邀请码,短信登录与忘记密码不可用\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "5000"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "10"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "50"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "2"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "开启关闭",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置",
"value": ""
}
]
}
@@ -0,0 +1,157 @@
{
"label": "安全策略",
"slug": "20-security-policy",
"route": "/security-settings",
"url": "https://admin.adfahi.cn/security-settings",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/20-security-policy.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n安全策略\n用户安全\n验证码触发次数\n连续密码错误该次数后要求验证码(1~10)\n密码最小位数\n用户密码最少字符数(6~32)\n强制包含字母\n开启后密码必须包含英文字母\n注册登录管控\n用户注册功能\n关闭后用户端将隐藏注册入口\n修改密码功能\n关闭后用户无法修改密码,不影响后台重置\n用户登录功能\n关闭后用户无法登录,不影响已登录用户\nWeb 端多端登录\n开启后同一账号可在多个浏览器标签页同时登录\n聊天消息管控\n全局好友功能\n关闭后无法搜索和添加好友\n全局群消息\n关闭后所有群无法发送消息\n全局私聊消息\n关闭后所有私聊无法发送消息\n群聊发言间隔(秒)\n0 表示不限制;1~1000 表示同一群内两次发言的最小间隔\n群组功能管控\n群号搜索功能\n关闭后用户端隐藏群号和群搜索入口\n群创建功能\n关闭后任何人无法新建群聊\n群解散功能\n关闭后任何人无法解散群聊,不影响后台管理员\n群管理员功能\n关闭后任何人无法设置群管理员\n是否显示群人数\n关闭后用户端查看群资料时不显示群总人数\n其他功能开关\n允许用户注销账号\n关闭后用户端将隐藏注销账号按钮\nIP 访问控制\n注册 IP 黑名单\n留空表示不限制;每行一个 IPv4、IPv6 或 CIDR。命中后无法注册及获取注册短信验证码。\n登录 IP 黑名单\n留空表示不限制;命中后无法密码登录、短信登录及刷新 Token。与用户分组登录白名单独立,黑名单优先。\n高级功能\n消息已读状态\n开启后显示消息已读/未读标记\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "203.0.113.10\n10.0.0.0/8",
"tag": "TEXTAREA",
"text": ""
},
{
"disabled": null,
"placeholder": "198.51.100.0/24",
"tag": "TEXTAREA",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置"
}
],
"resources": []
}
@@ -0,0 +1,180 @@
{
"label": "安全策略",
"slug": "20-security-policy",
"route": "/security-settings",
"url": "https://admin.adfahi.cn/security-settings",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/20-security-policy.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n安全策略\n用户安全\n验证码触发次数\n连续密码错误该次数后要求验证码(1~10)\n密码最小位数\n用户密码最少字符数(6~32)\n强制包含字母\n开启后密码必须包含英文字母\n注册登录管控\n用户注册功能\n关闭后用户端将隐藏注册入口\n修改密码功能\n关闭后用户无法修改密码,不影响后台重置\n用户登录功能\n关闭后用户无法登录,不影响已登录用户\nWeb 端多端登录\n开启后同一账号可在多个浏览器标签页同时登录\n聊天消息管控\n全局好友功能\n关闭后无法搜索和添加好友\n全局群消息\n关闭后所有群无法发送消息\n全局私聊消息\n关闭后所有私聊无法发送消息\n群聊发言间隔(秒)\n0 表示不限制;1~1000 表示同一群内两次发言的最小间隔\n群组功能管控\n群号搜索功能\n关闭后用户端隐藏群号和群搜索入口\n群创建功能\n关闭后任何人无法新建群聊\n群解散功能\n关闭后任何人无法解散群聊,不影响后台管理员\n群管理员功能\n关闭后任何人无法设置群管理员\n是否显示群人数\n关闭后用户端查看群资料时不显示群总人数\n其他功能开关\n允许用户注销账号\n关闭后用户端将隐藏注销账号按钮\nIP 访问控制\n注册 IP 黑名单\n留空表示不限制;每行一个 IPv4、IPv6 或 CIDR。命中后无法注册及获取注册短信验证码。\n登录 IP 黑名单\n留空表示不限制;命中后无法密码登录、短信登录及刷新 Token。与用户分组登录白名单独立,黑名单优先。\n高级功能\n消息已读状态\n开启后显示消息已读/未读标记\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "3"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "0"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "203.0.113.10\n10.0.0.0/8",
"tag": "TEXTAREA",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "198.51.100.0/24",
"tag": "TEXTAREA",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置",
"value": ""
}
]
}
@@ -0,0 +1,409 @@
# Admin Server 改造文档:对齐鸽达达管理后台
本文基于 `docs/admin-adfahi-research/需求文档.md`、当前 `im_manager` 代码、`im_server` 文档与本地 MySQL 数据核对结果编写。目标是在 HotGo Admin Server 中复刻鸽达达后台的 20 个页面,并保持当前系统“Admin 只直连 hotgo 控制库,IM Server 只直连租户业务库”的边界。
> 实施状态(2026-09-07):Admin 第二批已完成 `imDashboard`、`imPrivateMessage`、`imGroupMessage`、`imUserLoginHistory`、`imLoginFailureLog` 的 API、service、logic、controller、路由、Vue 页面、字典、导出和菜单权限。消息隐藏/删除、失败日志封禁用户使用 Kafka Admin Command;失败 IP 加黑复用安全策略。对应菜单 SQL 为 `server/storage/data/generate/im_adfahi_batch2_menu.sql`,已在本地 `hotgo` 执行。上述查询仍依赖 IM Server 本文约定的 internal-query;本地 `tinode` 尚无 `im_user_login_logs`、`im_login_failure_logs`,需在 IM Server 批次建表并接入日志采集后才会产生真实数据。
## 1. 改造边界
Admin Server 负责后台菜单、权限、页面、查询聚合、运营配置、Kafka 管理命令投递、命令结果展示与后台审计落库。业务 IM 数据不直接跨库读写,用户、群、消息、文件、反馈、登录等租户数据通过 IM Server 内部只读接口 `/internal/admin-query/tenants/{tenant_code}/...` 查询;用户封禁、群治理、消息处置、文件处置等写操作通过 Kafka Admin Command 下发。
当前本地库观察:
| 数据库 | 现状 |
| --- | --- |
| `hotgo` | 当前有 66 张表,已存在 `im_admin_commands`、`im_admin_operation_audit`、`im_admin_outbox`、`im_registration_invite_codes`、`im_external_config`、`im_file_policy`,并已新增策略分组、敏感词、安全策略和宵禁控制表。 |
| `tinode` | 当前有 73 张表,已有 `im_users`、`im_topics`、`im_subscriptions`、`im_messages`、`im_group_messages`、`im_group_settings`、`im_fileuploads`、`im_feedback_submissions` 等 IM 业务表。 |
| 联调状态 | `hotgo.im_tenant` 已补默认租户映射(`id=1`、`code=default`);`tinode` 尚无用户登录历史和登录失败日志表,对应页面待 IM Server 批次补齐数据源。 |
敏感配置说明:`hg_sys_config` 中已有声网、阿里云、OSS、短信等配置项。文档与页面只能展示脱敏值,后续应补充加密存储、轮换、审计与权限控制。
## 2. 当前 Admin 能力盘点
已有后台 API 模块:
| 模块 | 主要能力 | 复用结论 |
| --- | --- | --- |
| `imUser` | 用户列表、详情、设备、重置密码、状态、封禁/解封、冻结/解冻、踢设备、全端登出、注销、删除 | 可作为“用户管理”的主体,补鸽达达字段、筛选项、批量操作与审计页联动。 |
| `imGroup` | 群列表、详情、成员、群治理、资料更新、解散、禁言、成员禁言/移除、入群申请、邀请、公告、文件、管理日志 | 可覆盖“群组管理”和“群成员管理”主体,需补独立菜单页与鸽达达状态/字段映射。 |
| `imInvite` | 邀请码列表、新增、批量新增、批量查看、撤销、批量撤销、导出 | 可覆盖“邀请码管理”,但邀请码格式与鸽达达需求存在差异。 |
| `imFile` | 文件列表、详情、隔离、删除、清理孤儿文件 | 可覆盖“文件管理”,需补预览/下载链接、审核枚举、内容风险字段。 |
| `imAuditLog` | 操作日志列表、详情、导出 | 可覆盖后台操作日志的一部分,需扩展为用户账号审计、群事件审计等专题页。 |
| `imKafkaCommand`、`imDeadLetter`、`imOutbox`、`imKafkaAudit` | Kafka 命令、死信、出站事件、审计事件 | 保留为运维治理菜单,不直接出现在鸽达达 20 个业务页中。 |
| `imRegistration`、`imExternalConfig`、`imFilePolicy`、`imFeedbackConfig`、`imMailboxConfig`、`imNotification`、`imAppRelease` | 注册、第三方、文件策略、反馈、邮箱、通知、版本配置 | 可组合成“私域设置”和“安全策略”,需要按鸽达达页面重组展示。 |
已有菜单已包含 `IM管理`、`注册邀请码`、`用户列表`、`群组列表`、`文件治理`、`Kafka命令`、`Kafka死信`、`Kafka审计事件`、`租户管理` 等。缺少与鸽达达完全一致的“单聊消息、群聊消息、敏感词库、用户反馈、用户登录、IP账号统计、登录失败日志”等前台页面。
## 3. 页面级改造
### 3.1 数据概览
新增或改造 `imDashboard` 页面。顶部统计按需求文档展示总用户数、活跃用户、总群组数、总会话数、总消息数、今日消息;趋势表展示最近 7 天用户增长、消息增长。现有 IM Server 内部接口已有 `/internal/admin-query/tenants/{tenant}/dashboard/summary`,但仅返回 `dead_kafka`、`pending_business`、`pending_group`、`pending_realname`、`received_abuse`、`received_feedback`、`active_groups`、`suspended_users`,不足以还原鸽达达数据概览。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| API | 新增 `/imDashboard/overview`、`/imDashboard/trends`。 |
| 数据源 | 控制库租户信息来自 `im_tenant`;统计与趋势来自 IM internal query。 |
| 字段 | `totalUsers`、`activeUsers`、`totalGroups`、`totalConversations`、`totalMessages`、`todayMessages`、`date`、`userCount`、`messageCount`。 |
| 按钮 | 页面无写操作;支持租户切换、日期范围查询、刷新。 |
| 缺口 | IM Server 需补 dashboard 聚合项与趋势接口;Admin 需补租户为空时的默认租户兜底提示。 |
### 3.2 用户管理
复用 `imUser`,页面按鸽达达字段重排。列表数据通过 IM internal `users` 查询,详情通过 `/users/{id}`,设备通过 `/users/{id}/devices`。当前 Admin 已有 `/imUser/list`、`/imUser/view`、`/imUser/devices`、`/imUser/ban`、`/imUser/unban`、`/imUser/freeze`、`/imUser/unfreeze`、`/imUser/deviceKick`、`/imUser/logoutAll`、`/imUser/accountCancel`、`/imUser/accountDelete`、`/imUser/resetPassword`。
字段说明:
| 字段 | 来源 | 说明 |
| --- | --- | --- |
| `user_id` | `tinode.im_users.id` 编码后 | 用户唯一 ID,列表、详情、审计联动主键。 |
| `username` | `im_users.username` | 登录账号。 |
| `phone` | `im_users.phone` | 手机号,页面默认脱敏,导出需权限。 |
| `nickname` | `im_user_profiles.nickname` | 昵称,支持模糊查询。 |
| `public_account` | `im_user_profiles.public_account` | 对外账号/靓号。 |
| `state` | `im_users.state` | Tinode 用户状态。建议映射为正常、封禁、冻结、注销、删除。 |
| `last_seen` | `im_users.last_seen` | 最后在线/活跃时间。 |
| `created_at` | `im_users.created_at` | 注册时间。 |
| `credentials` | IM internal 聚合 | 认证方式摘要,不展示密钥。 |
按钮功能:
| 按钮 | 已有接口 | 功能 |
| --- | --- | --- |
| 查询 | `/imUser/list` | 按用户 ID、昵称、账号、手机号、状态、注册时间、最后活跃时间筛选。 |
| 重置 | 前端表单重置 | 清空筛选项并重新查询。 |
| 查看 | `/imUser/view` | 展示基础信息、资料、设备、最近审计事件。 |
| 封禁/解封 | `/imUser/ban`、`/imUser/unban` | Kafka 命令 `user.ban` / `user.unban`,需填写原因和过期时间。 |
| 冻结/解冻 | `/imUser/freeze`、`/imUser/unfreeze` | Kafka 命令 `user.freeze` / `user.unfreeze`。 |
| 踢设备 | `/imUser/deviceKick` | 指定设备下线。 |
| 全端登出 | `/imUser/logoutAll` | 下发 `user.logout_all`。 |
| 注销/删除 | `/imUser/accountCancel`、`/imUser/accountDelete` | 高危操作,二次确认并写审计。 |
枚举说明:`state` 当前来自 Tinode 状态值,Admin 应新增统一字典 `im_user_state`,至少包含正常、封禁、冻结、注销、删除。封禁/冻结原因不应写死在前端,可复用“安全策略”中的处置原因配置。
### 3.3 用户分组
当前缺少等价模块,需要新增 `imUserPolicyGroup`。该页面不是组织部门,而是账号策略分组,用于控制注册、登录、私聊、建群、入群、发文件、发消息、IP 限制等策略。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| 后端模块 | `api/admin/imuserpolicygroup`、`controller/admin/sys/im_user_policy_group.go`、`logic/sys/im_user_policy_group.go`、`model/input/sysin/im_user_policy_group.go`。 |
| 控制表 | 新增 `im_user_policy_groups`、`im_user_policy_group_members`。 |
| API | `/imUserPolicyGroup/list`、`/view`、`/edit`、`/delete`、`/status`、`/memberList`、`/memberAdd`、`/memberRemove`。 |
| 同步 | 策略变更写 `im_admin_outbox`,发布 Kafka config event;IM Server 缓存策略并执行。 |
| 页面按钮 | 查询、新增、编辑、删除、启用/禁用、成员管理、导出。 |
字段说明:
| 字段 | 说明 |
| --- | --- |
| `group_code` | 分组编码,唯一,不可编辑。 |
| `group_name` | 分组名称。 |
| `priority` | 命中多个分组时的优先级,数字越大优先级越高。 |
| `status` | 启用、禁用。 |
| `policy_json` | 策略 JSON,包含消息、好友、群、文件、登录等开关与限额。 |
| `ip_whitelist`、`ip_blacklist` | IP 白名单/黑名单,支持 CIDR。 |
| `remark` | 备注。 |
好友策略必须覆盖移动端所有加好友入口,不能只依赖前端隐藏。分组策略中至少包含 `allow_add_friend`、`direct_add_friend`、`max_friend_count`、`allow_view_profile_when_friend_disabled`、`allow_qrcode_add_friend`、`allow_group_member_profile`。当全局或分组禁止加好友时,从搜索、通讯录推荐、群成员列表、个人名片、二维码解析、邀请注册自动加好友、转发目标联系人等入口都必须由后端返回禁止或脱敏数据。
枚举说明:`status` 使用 `sys_normal_disable`;策略开关使用布尔值;策略动作建议统一枚举 `allow`、`deny`、`challenge`、`audit_only`。好友入口枚举建议 `search`、`profile_card`、`group_member`、`qrcode`、`invite_auto_add`、`recommendation`。
### 3.4 群组管理
复用 `imGroup`。列表通过 IM internal `groups` 查询,详情通过 `/groups/{topic}`,现有 Admin 已有群资料、治理、成员、申请、邀请、公告、文件、日志等接口。
字段说明:
| 字段 | 来源 | 说明 |
| --- | --- | --- |
| `topic` | `im_topics.name` | 群 Topic,等价群号/群唯一标识。 |
| `group_name` | `im_group_settings.group_name` | 群名称。 |
| `owner_id` | `im_topics.owner` | 群主。 |
| `group_status` | `im_group_settings.group_status` | 群业务状态。 |
| `state` | `im_topics.state` | Tinode topic 状态。 |
| `business_member_count` | `im_group_settings.business_member_count` | 成员数。 |
| `join_approval` | `im_group_settings.join_approval` | 入群审批开关。 |
| `mute_all` | `im_group_settings.mute_all` | 全员禁言。 |
| `created_at`、`updated_at` | topic/settings | 创建与更新时间。 |
按钮功能:
| 按钮 | 已有接口 | 功能 |
| --- | --- | --- |
| 查询 | `/imGroup/list` | 按群号、群名、群主、状态、创建时间查询。 |
| 查看 | `/imGroup/view` | 群资料、成员、公告、邀请、文件、治理配置汇总。 |
| 编辑资料 | `/imGroup/profileUpdate` | Kafka `group.profile.update`。 |
| 治理设置 | `/imGroup/governanceUpdate` | Kafka `group.governance.update`,控制入群审批、全员禁言、成员上传文件、成员私聊、成员邀请。 |
| 解散 | `/imGroup/dissolve` | 高危操作,二次确认。 |
| 禁言/解禁言 | `/imGroup/mute`、`/imGroup/unmute` | 群级禁言。 |
| 成员/申请/邀请/公告/文件/日志 | 已有子接口 | 打开子面板或跳转专题页。 |
枚举说明:新增 `im_group_status`,建议值 `active` 正常、`muted` 禁言、`suspended` 封停、`dissolved` 已解散。当前 IM 命令未看到明确 `group.ban`/`group.unban`,如鸽达达要求群封停,需要新增 Kafka 命令或通过治理状态统一处理。
### 3.5 群成员管理
复用 `/imGroup/memberList` 与成员治理命令,新增独立菜单页 `imGroupMember`,默认要求输入群号或从群详情跳转携带 `topic`。
字段说明:
| 字段 | 说明 |
| --- | --- |
| `topic` | 群 Topic。 |
| `user_id` | 成员用户 ID。 |
| `display_name`/`nickname` | 群内展示名或用户昵称。 |
| `role` | 群主、管理员、普通成员。 |
| `status` | 正常、禁言、已移除。 |
| `muted_until` | 禁言截止时间。 |
| `joined_at` | 入群时间。 |
按钮功能:查询、查看用户、设为管理员、取消管理员、转让群主、成员禁言、解除禁言、移除成员。现有接口分别为 `/imGroup/adminSet`、`/adminRemove`、`/ownerTransfer`、`/memberMute`、`/memberUnmute`、`/memberRemove`。
枚举说明:`role` 新增字典 `im_group_member_role`:`owner` 群主、`admin` 管理员、`member` 成员;成员状态新增 `im_group_member_status`。
### 3.6 邀请码管理
复用 `imInvite`。本地 `hotgo.im_registration_invite_codes` 有样例数据,批次表为空。当前需求文档按鸽达达页面描述 6 位数字邀请码;历史 IM 文档曾描述 5 位 ASCII 字母邀请码,二者冲突。若以复刻鸽达达为目标,应统一改为 6 位数字,Admin 生成、IM 注册校验、数据库唯一索引与错误文案同步调整。
字段说明:`code` 邀请码、`batch_id` 批次、`status` 状态、`max_uses` 最大使用次数、`used_count` 已使用次数、`expires_at` 过期时间、`created_by` 创建管理员、`created_at` 创建时间、`revoked_at` 撤销时间。
按钮功能:查询、新增、批量生成、批量查看、撤销、批量撤销、导出。接口已有 `/imInvite/list`、`/create`、`/batchCreate`、`/batchView`、`/revoke`、`/batchRevoke`、`/export`。
枚举说明:新增或确认 `im_invite_status`:`unused` 未使用、`used` 已用完、`expired` 已过期、`revoked` 已撤销。
### 3.7 单聊消息
当前 Admin 缺少独立消息管理模块,需要新增 `imPrivateMessage`。IM Server 已有消息表 `im_messages`,但 internal admin query 目前只支持 `/messages/{topic}/{seq}` 单条查询,缺少按会话分页检索单聊消息的后台列表接口。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| API | `/imPrivateMessage/list`、`/view`、`/hide`、`/delete`、`/export`。 |
| 数据源 | 新增 IM internal query:`/tenants/{tenant}/private-messages` 或 `/messages?kind=p2p`。 |
| 写操作 | 通过 Kafka 命令新增 `message.hide`、`message.delete` 或复用 IM 已有 recall/delete 能力封装为后台命令。 |
| 页面按钮 | 查询、查看上下文、隐藏、删除、导出。 |
字段说明:`topic` 会话 Topic、`seq_id` 消息序号、`from_user_id` 发送者、`to_user_id` 接收者、`content_type` 内容类型、`content` 内容摘要、`status` 状态、`created_at` 发送时间、`updated_at` 更新时间。
枚举说明:`content_type` 建议 `text`、`image`、`file`、`audio`、`video`、`card`、`system`;`status` 建议 `normal`、`recalled`、`hidden`、`deleted`。
### 3.8 群聊消息
新增 `imGroupMessage` 页面。IM Server 本地有 `im_group_messages` 表,需求需要按群号、发送者、时间、内容关键字筛选。
Admin 改造:新增 `/imGroupMessage/list`、`/view`、`/hide`、`/delete`、`/export`。IM Server 补 internal query `group-messages`,支持 `topic`、`sender_user_id`、`content_type`、`status`、`created_at_start/end`、`keyword`。
字段、按钮、枚举与单聊消息一致,额外展示 `group_name`、`member_role`、`at_users`、`reply_to_seq`。
### 3.9 敏感词库
当前 Admin 与 IM Server 未发现完整敏感词库模块,需要新增。Admin 维护词库,IM Server 在消息发送、昵称/群名修改、反馈/举报提交时执行匹配。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| 表 | `im_sensitive_words`、`im_sensitive_word_groups`、`im_sensitive_word_hits` 可按需拆分。 |
| API | `/imSensitiveWord/list`、`/view`、`/edit`、`/delete`、`/status`、`/import`、`/export`。 |
| 同步 | 词库变更发布 config event 或 cache invalidate,IM Server 热加载。 |
| 按钮 | 查询、新增、编辑、删除、启用/禁用、导入、导出。 |
字段说明:`word` 敏感词、`match_type` 匹配类型、`scene` 生效场景、`action` 命中动作、`replacement` 替换文本、`level` 风险等级、`status` 状态。
枚举说明:`match_type` 为 `exact` 精确、`contains` 包含、`regex` 正则;`scene` 为 `message` 消息、`profile` 资料、`group` 群资料、`feedback` 反馈;`action` 为 `block` 拦截、`replace` 替换、`review` 人审、`audit_only` 仅记录。
### 3.10 文件管理
复用 `imFile`,补齐鸽达达文件字段和预览能力。IM internal 已支持 `files`、`files/{id}`、`files/{id}/links`。本地 `tinode.im_fileuploads` 有数据,`im_group_files` 暂无数据。
字段说明:`file_id` 文件 ID、`user_id` 上传者、`topic` 所属会话、`mime_type` MIME、`size` 文件大小、`location` 存储位置、`etag` 校验、`status` 状态、`review_status` 审核状态、`risk_level` 风险等级、`created_at` 上传时间。
按钮功能:查询、查看、预览/下载、隔离、删除、清理孤儿文件。现有接口 `/imFile/quarantine`、`/imFile/delete`、`/imFile/cleanupOrphan` 可复用;预览/下载建议由 Admin 获取短期签名 URL,不直接暴露 OSS 原始地址。
枚举说明:现有字典包括 `im_storage_provider=oss`、`im_file_review_provider`、`im_file_review_status(pending/running/clean/risky/failed)`、`im_file_review_task_type(content/malware)`;还需补 `im_file_status`:`normal` 正常、`quarantined` 已隔离、`deleted` 已删除。
### 3.11 用户反馈
IM Server 已有 `im_feedback_submissions` 和 internal resource `feedback-submissions`,Admin 目前有反馈配置和工单模块,但缺少完全按鸽达达展示的反馈列表页。建议新增 `imFeedback` 或改造 `imWorkOrder` 作为反馈处理页。
字段说明:`feedback_id` 反馈 ID、`user_id` 用户、`category` 分类、`content` 内容、`contact` 联系方式、`file_ids` 附件、`client_context` 客户端环境、`include_diagnostics` 是否含诊断、`status` 状态、`created_at`、`updated_at`。
按钮功能:查询、查看、标记处理中、标记已处理、关闭、导出。写操作建议先落 Admin 工单状态,再通过 Kafka 或 IM internal 写接口回写业务状态。
枚举说明:`status` 建议 `received` 已收到、`processing` 处理中、`resolved` 已解决、`closed` 已关闭;`category` 来自 `im_feedback_config.categories`,禁止前端硬编码。
### 3.12 操作日志
复用 `imAuditLog` 与 `im_admin_operation_audit`。本地该表已有 45 条记录。页面应覆盖后台管理员对 IM 资源的所有操作,包括用户、群、消息、文件、配置、邀请码、敏感词。
字段说明:`id`、`request_id`、`admin_id`、`admin_name`、`action`、`target_type`、`target_id`、`before_data`、`after_data`、`ip`、`user_agent`、`result`、`created_at`。
按钮功能:查询、查看详情、导出。详情页需展示变更前后 JSON,敏感字段脱敏。
枚举说明:`target_type` 建议 `user`、`group`、`message`、`file`、`invite`、`config`、`sensitive_word`;`result` 为 `success`、`failed`、`pending`。
### 3.13 用户账号审计
新增专题页 `imUserAccountAudit`,从 Admin 操作审计、IM `audit-logs`、Kafka 命令结果中聚合用户生命周期事件。
字段说明:`event_id`、`user_id`、`action`、`operator_type`、`operator_id`、`reason`、`ip`、`device_id`、`before_state`、`after_state`、`created_at`。
按钮功能:查询、查看、跳转用户详情、导出。动作枚举建议包含注册、登录、登出、登录失败、改密、重置密码、封禁、解封、冻结、解冻、注销、删除、踢设备。
缺口:IM Server 当前 `im_audit_logs` 本地为空,需要补登录、失败、状态变更等事件写入。
### 3.14 群事件审计
可复用 `imGroup/adminLogList`、`outbox/group-events` 和 Admin audit,新增独立页面 `imGroupEventAudit`。
字段说明:`event_id`、`topic`、`group_name`、`operator_id`、`target_user_id`、`action`、`object_type`、`object_id`、`before_data`、`after_data`、`created_at`。
按钮功能:查询、查看、跳转群详情、导出。动作枚举建议包含建群、解散、修改资料、修改治理设置、入群申请、审批通过、审批拒绝、成员禁言、解除禁言、移除成员、设管理员、取消管理员、转让群主、公告发布/删除、文件删除。
### 3.15 管理员登录历史
复用 HotGo 系统登录日志能力,不需要 IM Server 改造。若现有登录日志字段不足,补 Admin 聚合接口 `/imAdminLoginHistory/list` 或直接配置菜单指向系统日志页面。
字段说明:管理员 ID、账号、昵称、登录 IP、地区、浏览器、系统、登录结果、失败原因、登录时间。
按钮功能:查询、查看、导出。枚举 `result` 为成功、失败。
### 3.16 用户登录历史
当前缺少完整用户登录历史。IM Server 路由中未看到客户端 `/v1/im/auth/logout` 或独立 session 管理路由;本地 `im_devices` 为空,不能满足鸽达达登录历史。
Admin 改造:新增 `imUserLoginHistory` 页面,查询 IM internal `user-login-logs`。字段包括 `user_id`、`phone`、`login_type`、`device_id`、`platform`、`ip`、`region`、`user_agent`、`result`、`login_at`、`logout_at`。
枚举说明:`login_type` 为 `password`、`sms`、`token_refresh`、`third_party`;`platform` 为 `ios`、`android`、`web`、`desktop`;`result` 为 `success`、`failed`。
### 3.17 IP 账号统计
新增 `imIpAccountStats` 页面。数据由 IM Server 基于注册 IP、登录 IP、失败登录 IP 聚合生成,Admin 只查询展示。
字段说明:`ip`、`region`、`user_count`、`register_count`、`login_count`、`failure_count`、`first_seen_at`、`last_seen_at`、`risk_level`。
按钮功能:查询、查看关联账号、导出、加入黑名单。加入黑名单写入“安全策略”或“用户分组”策略并同步 IM Server。
枚举说明:`risk_level` 为 `low`、`medium`、`high`;黑名单动作 `block_login`、`challenge`、`audit_only`。
### 3.18 登录失败日志
新增 `imLoginFailureLog` 页面。IM Server 需要记录认证失败事件,包含错误原因、IP、设备、账号、风控命中信息。
字段说明:`id`、`account`、`user_id`、`failure_type`、`reason`、`ip`、`region`、`device_id`、`platform`、`user_agent`、`attempt_at`。
按钮功能:查询、查看、导出、加入 IP 黑名单、封禁账号。封禁账号复用 `/imUser/ban`。
枚举说明:`failure_type` 为 `password_error`、`code_error`、`account_not_found`、`account_disabled`、`rate_limited`、`risk_blocked`。
### 3.19 私域设置
以现有 `imRegistration`、`imExternalConfig`、`imFeedbackConfig`、`imMailboxConfig`、`imNotification`、`imAppRelease` 为基础,新增汇总页面 `imTenantSettings`,按鸽达达页面展示私域基础配置。
字段说明:租户标识、注册开关、邀请码开关、短信开关、默认策略分组、客服/反馈配置、上传配置、客户端功能开关、版本策略、更新时间。客户端功能开关中新增 `global_friend_enabled` 和 `message_curfew_enabled` 的只读摘要,便于运营在私域设置页看到当前私域是否允许加好友、是否存在宵禁;实际编辑入口放在“安全策略”。
按钮功能:查看、编辑、保存、测试第三方配置、发布配置。保存后写 `hg_sys_config` 或 `im_tenant_config`,并通过 Kafka config event 通知 IM Server 刷新缓存。
枚举说明:开关统一使用布尔;配置发布状态 `draft`、`published`、`failed`。
### 3.20 安全策略
新增或重组 `imSecurityPolicy` 页面,统一管理登录安全、密码策略、IP 风控、敏感词动作、文件审核、用户处置原因、消息处置策略、全局好友功能与消息宵禁。
字段说明:`password_policy`、`login_rate_limit`、`ip_blacklist`、`ip_whitelist`、`sensitive_word_policy`、`file_review_policy`、`realname_policy`、`risk_challenge_policy`、`global_friend_enabled`、`friend_disabled_policy`、`message_curfew_enabled`、`message_curfew_rules`。
全局好友功能说明:
| 字段 | 说明 |
| --- | --- |
| `global_friend_enabled` | 全局是否允许加好友。关闭后所有移动端加好友入口都禁止。 |
| `friend_disabled_policy.hide_profile` | 禁止加好友时,群成员列表、个人名片、二维码解析是否隐藏个人资料。 |
| `friend_disabled_policy.block_qrcode` | 是否禁止通过二维码添加好友。 |
| `friend_disabled_policy.block_group_member_entry` | 是否禁止从群成员入口查看个人信息或发起添加。 |
| `friend_disabled_policy.blocked_entries` | 被禁用的移动端入口数组:`search`、`profile_card`、`group_member`、`qrcode`、`invite_auto_add`、`recommendation`。关闭全局好友功能时默认全部禁用。 |
| `friend_disabled_policy.message` | 移动端提示文案,例如“当前时段暂不允许添加好友”。 |
宵禁功能说明:
| 字段 | 说明 |
| --- | --- |
| `message_curfew_enabled` | 是否启用消息宵禁。 |
| `message_curfew_rules` | 宵禁规则数组,支持每天、工作日、周末、自定义日期。 |
| `start_time`、`end_time` | 宵禁开始和结束时间,支持跨天,例如 `18:00` 到次日 `09:00`。 |
| `scope` | 管控范围,默认至少包含 `private_message`,即单聊/私聊不能发消息;如产品确认,可扩展 `group_message`。 |
| `whitelist_user_ids`、`whitelist_group_ids` | 豁免用户或群,可选。 |
| `message` | 宵禁期间发送失败提示。 |
按钮功能:查询当前策略、编辑、保存、发布、恢复默认、查看发布历史。保存必须写操作审计。
枚举说明:风控动作统一 `allow`、`challenge`、`block`、`audit_only`;文件审核动作 `allow`、`quarantine`、`delete`、`manual_review`;宵禁范围 `private_message` 单聊/私聊、`group_message` 群聊;日期类型 `daily` 每天、`weekday` 工作日、`weekend` 周末、`custom` 自定义。
## 4. 后端实现方案
按 HotGo 规范新增模块时,必须完成 API、controller、service、logic、model/input、router/genrouter、前端 API、views/model、index、edit。接口命名沿用当前项目风格,例如 `/imPrivateMessage/list`,不直接照搬鸽达达 `/admin/private-messages` 路径;如后续需要兼容外部路径,可单独加网关映射。
新增模块建议:
| 优先级 | 模块 | 说明 |
| --- | --- | --- |
| P0 | `imDashboard` | 首页统计与趋势。 |
| P0 | `imPrivateMessage`、`imGroupMessage` | 单聊/群聊消息查询与处置。 |
| P0 | `imSensitiveWord` | 敏感词库与热更新。 |
| P0 | `imUserLoginHistory`、`imLoginFailureLog` | 用户登录与失败日志。 |
| P1 | `imUserPolicyGroup` | 用户策略分组。 |
| P1 | `imFeedback` | 用户反馈处理页。 |
| P1 | `imIpAccountStats` | IP 聚合与风控联动。 |
| P1 | `imUserAccountAudit`、`imGroupEventAudit` | 专题审计页。 |
| P2 | `imTenantSettings`、`imSecurityPolicy` | 运营配置整合页。 |
Admin 查询 IM 数据统一走 `server/internal/library/iminternal` 或现有 `imadmin` 封装,不在 logic 层直接拼接外部 HTTP 细节。写操作统一创建 `im_admin_commands` 与 `im_admin_outbox`,等待 Kafka result 更新状态;页面展示 `pending/success/failed/dead`。
## 5. 数据库与字典
配套建表与兼容迁移脚本见 [`sql/Admin-Server改造.sql`](sql/Admin-Server改造.sql)。脚本面向 `hotgo` 控制库,只保存管理配置和发布版本,不保存租户业务消息。
控制库新增表优先放在 `hotgo`:
| 表 | 用途 |
| --- | --- |
| `im_user_policy_groups` | 用户策略分组主表。 |
| `im_user_policy_group_members` | 用户与策略分组关系。 |
| `im_sensitive_word_groups` | 敏感词分组。 |
| `im_sensitive_words` | 敏感词主表。 |
| `im_security_policy` | 安全策略草稿/发布版本。 |
| `im_admin_query_snapshots` | 可选,用于缓存重型统计查询快照。 |
字典由后端 `internal/consts` 注册,前端通过 `dict.loadOptions()` 加载。禁止在 `model.ts` 中硬编码枚举中文。
必补字典:`im_user_state`、`im_group_status`、`im_group_member_role`、`im_group_member_status`、`im_invite_status`、`im_message_content_type`、`im_message_status`、`im_sensitive_match_type`、`im_sensitive_scene`、`im_sensitive_action`、`im_feedback_status`、`im_login_type`、`im_login_failure_type`、`im_risk_level`、`im_policy_action`、`im_friend_entry`、`im_curfew_scope`、`im_curfew_date_type`。
## 6. 权限与审计
每个按钮权限与后端路由保持一致,例如 `/imPrivateMessage/delete`、`/imSensitiveWord/import`。高危操作包括用户删除、账号注销、群解散、消息删除、文件删除、IP 拉黑、安全策略发布,必须二次确认、记录原因、写 `im_admin_operation_audit`,并展示 Kafka 命令执行结果。
审计脱敏规则:
| 类型 | 展示规则 |
| --- | --- |
| 手机号 | 中间 4 位脱敏。 |
| 身份证/实名 | 默认只展示脱敏字段。 |
| 配置密钥 | 只展示前后 3-4 位或完全隐藏。 |
| 消息内容 | 列表展示摘要,详情按权限展示全文。 |
| 文件地址 | 不展示原始存储地址,使用短期签名 URL。 |
## 7. 联调与验收
验收顺序建议:
1. 补齐 `hotgo.im_tenant` 与 `tinode` 默认租户映射,确保 Admin 能通过 tenant code 查询 IM internal。
2. 完成数据概览、用户、群、邀请码、文件这些已有基础模块的字段对齐。
3. 新增消息、敏感词、反馈、登录日志、IP 统计等缺口模块。
4. 为 Kafka 命令增加幂等键、结果回写、失败重试、死信展示。
5. 使用本地 `hotgo` 与 `tinode` 数据验证每个页面的查询、按钮、详情和导出。
最低验收标准:20 个菜单均可打开;查询按钮能返回真实或空态数据;表格字段与需求文档一致;所有枚举来自字典;高危按钮有二次确认、原因、审计与结果状态;敏感数据脱敏。
@@ -0,0 +1,390 @@
# IM Server 改造文档:支撑鸽达达管理后台复刻
本文面向 `/Users/youshunlai/go_workspace/im_server` 的现状改造,目标是为 Admin Server 复刻鸽达达后台提供准确、稳定、可审计的数据与治理能力。当前 IM Server 已有 Tinode 风格实时链路、产品 HTTP API、独立租户库、Kafka 命令消费、配置事件消费、内部 Admin Query,但鸽达达 20 个后台页面仍需要补充若干查询资源、审计日志、风控策略与消息处置能力。
## 1. 当前实现现状
核心架构:
| 能力 | 现状 |
| --- | --- |
| 实时链路 | `Session -> Hub -> Topic -> Store`。 |
| 客户端入口 | WebSocket `/v0/channels`,产品 HTTP `/v1/im/...`。 |
| 后台只读入口 | `/internal/admin-query/tenants/{tenant_code}/...`,要求内部签名与 capability。 |
| 后台写操作 | Kafka Admin Command 消费器 `startKafkaAdminCommandConsumer`。 |
| 配置同步 | Kafka config event、cache invalidate consumer 已存在。 |
| 消息转发 | `/v1/im/messages/forward` 已存在。 |
| 音视频入会 | `/v1/im/calls/rtc-join` 已存在。 |
本地 `tinode` 库观察:`im_users` 有数据、`im_topics` 有群/会话、`im_subscriptions` 有订阅、`im_messages` 有大量单聊或通用消息、`im_group_messages` 有群消息、`im_feedback_submissions`、`im_abuse_reports`、`im_kafka_dead_letters` 等表已存在;`im_audit_logs` 本地为空,`im_devices` 和 `im_group_files` 数据不足,不能直接满足后台登录历史、设备历史和群文件验收。
已有内部查询资源:
| 资源 | 已支持字段/场景 |
| --- | --- |
| `users` | 用户 ID、账号、手机号、状态、资料、凭证摘要、最后在线。 |
| `user_devices` | 用户设备列表。 |
| `groups` | Topic、群名、群主、群状态、治理开关、成员数。 |
| `groups/{topic}/members` | 群成员。 |
| `group_join-requests`、`group_invitations`、`group_announcements`、`group_mutes`、`group_admin-logs`、`group_files` | 群治理专题资源。 |
| `messages/{topic}/{seq}` | 单条消息详情。 |
| `files`、`files/{id}`、`files/{id}/links` | 文件列表、详情、链接关系。 |
| `feedback-submissions`、`abuse-reports`、`realname-verifications`、`audit-logs` | 用户反馈、举报、实名、审计。 |
| `dashboard/summary` | 当前只返回待办类指标,不足以覆盖鸽达达首页统计。 |
已有 Kafka Admin Command:
| 命令 | 用途 |
| --- | --- |
| `user.ban`、`user.unban` | 用户封禁/解封。 |
| `user.freeze`、`user.unfreeze` | 用户冻结/解冻。 |
| `user.device.kick`、`user.logout_all` | 设备踢下线、全端登出。 |
| `user.account.cancel`、`user.account.delete`、`user.password.reset` | 注销、删除、重置密码。 |
| `group.profile.update` | 群资料更新。 |
| `group.governance.update` | 群治理开关更新。 |
| `group.admin.set`、`group.admin.remove`、`group.owner.transfer` | 群角色管理。 |
| `group.member.mute`、`group.member.unmute`、`group.member.remove` | 群成员治理。 |
| `kafka.dead_letter.replay`、`ignore`、`resolve` | 死信处理。 |
## 2. 改造原则
IM Server 只负责租户业务库的数据查询、业务状态变更、实时生效与审计事件产生,不直接依赖 HotGo 后台表。Admin Server 下发的配置、策略和治理命令必须具备幂等性,并通过事件结果回传 Admin。所有 internal query 必须保持只读、分页、最大 `per_page=200`、内部签名鉴权、字段脱敏。
响应协议继续沿用现有 `imHTTPResponse`:`code`、`data`、`reason`、`message`、`details`、`request_id`、`server_time`。写请求继续要求 `Idempotency-Key`,避免后台重复点击导致重复处置。
## 3. 支撑 20 个后台页面的 IM 改造
### 3.1 数据概览
现有 `dashboard/summary` 只覆盖死信、待处理 outbox、待实名、待举报、待反馈、活跃群、封禁用户等运营待办。需要新增统计字段以匹配鸽达达首页。
新增 internal query:
| 路径 | 方法 | 说明 |
| --- | --- | --- |
| `/internal/admin-query/tenants/{tenant}/dashboard/overview` | GET | 返回总用户、活跃用户、总群组、总会话、总消息、今日消息。 |
| `/internal/admin-query/tenants/{tenant}/dashboard/trends` | GET | 返回最近 N 天用户增长与消息增长。 |
字段口径:
| 字段 | 计算建议 |
| --- | --- |
| `total_users` | `im_users` 有效用户数。 |
| `active_users` | `last_seen` 在指定窗口内或当天有消息/登录行为的用户数。 |
| `total_groups` | `im_group_settings.group_status='active'` 或有效群 topic 数。 |
| `total_conversations` | 有效 P2P topic + group topic 数。 |
| `total_messages` | `im_messages` + `im_group_messages`,按最终存储口径去重。 |
| `today_messages` | 当天创建的消息数,按租户时区对齐。 |
| `date`、`user_count`、`message_count` | 趋势表行字段。 |
### 3.2 用户管理
现有 `users`、`users/{id}`、`users/{id}/devices` 基本可支撑。需要补足鸽达达页面要求的筛选和字段脱敏。
改造点:
| 项 | 内容 |
| --- | --- |
| 查询筛选 | 增加 `phone`、`public_account`、`state`、`created_at_start/end`、`last_seen_start/end`。 |
| 字段补充 | 返回 `nickname`、头像文件 ID、性别、地区、签名、认证方式摘要、设备数、最近登录 IP。 |
| 状态映射 | 将 Tinode `state` 转换为 Admin 字典所需状态;保留原始值 `raw_state` 便于排错。 |
| 治理命令 | 继续使用已有 `user.ban/unban/freeze/unfreeze/device.kick/logout_all/account.cancel/account.delete/password.reset`。 |
| 审计 | 每次状态变更、重置密码、踢设备必须写 `im_audit_logs` 并产出 Kafka command result。 |
枚举建议:`user_state` 包含正常、封禁、冻结、注销、删除;`device_platform` 包含 `ios`、`android`、`web`、`desktop`、`unknown`。
### 3.3 用户分组
当前 IM Server 没有看到独立的后台用户策略分组执行层。Admin 将新增策略分组后,IM Server 需要消费配置事件并在运行时执行。
新增能力:
| 能力 | 说明 |
| --- | --- |
| 策略缓存 | 按租户缓存 `policy_group`、成员关系、优先级与规则。 |
| 匹配入口 | 登录、注册、加好友、建群、入群、发消息、上传文件、发起音视频前匹配策略。 |
| 配置事件 | 消费 `user_policy_group.updated`、`user_policy_group.deleted`,刷新缓存。 |
| 命中审计 | 命中 `deny`、`challenge` 或高风险规则时写审计与风控日志。 |
策略动作枚举:`allow` 放行、`deny` 拦截、`challenge` 二次验证、`audit_only` 仅记录。
好友策略需要在后端覆盖所有移动端入口。禁止加好友时,IM Server 不能只让客户端隐藏按钮,而要在搜索用户、查看个人名片、群成员资料、二维码解析、好友申请、邀请注册自动加好友、推荐联系人等入口统一拦截或返回脱敏数据。策略字段建议包含 `allow_add_friend`、`direct_add_friend`、`max_friend_count`、`allow_qrcode_add_friend`、`allow_group_member_profile`、`hide_profile_when_friend_disabled`。
### 3.4 群组管理
已有 `groups` 查询与群治理命令可复用。需要补鸽达达群状态语义和可能的群封停动作。
改造点:
| 项 | 内容 |
| --- | --- |
| 字段 | 确保 `topic`、`group_name`、`owner_id`、`group_status`、`business_member_count`、`join_approval`、`mute_all`、`created_at` 可稳定返回。 |
| 状态 | 明确 `group_status` 值:`active`、`muted`、`suspended`、`dissolved`。 |
| 命令 | 已有资料更新、治理设置、角色与成员治理;若需求要求群封停/解封,新增 `group.suspend`、`group.unsuspend`。 |
| 审计 | 群资料、治理开关、解散、禁言、成员操作均写 `im_group_admin_logs` 与 `im_audit_logs`。 |
### 3.5 群成员管理
现有 `groups/{topic}/members` 与成员命令可支撑。需要确保成员列表返回群角色、禁言状态与入群时间。
字段建议:`topic`、`user_id`、`nickname`、`display_name`、`role`、`status`、`muted_until`、`joined_at`、`last_seen`。`role` 枚举为 `owner`、`admin`、`member`;`status` 枚举为 `active`、`muted`、`removed`。
按钮对应命令:设管理员 `group.admin.set`、取消管理员 `group.admin.remove`、转让群主 `group.owner.transfer`、成员禁言 `group.member.mute`、解除禁言 `group.member.unmute`、移除成员 `group.member.remove`。
### 3.6 邀请码管理
IM Server 注册链路已在 `user.go` 中记录邀请码使用,`RecordUse` 已存在。需要解决邀请码格式冲突:鸽达达需求为 6 位数字,既有 IM 文档曾要求 5 位 ASCII 字母。若严格复刻鸽达达,应将 IM 注册校验、错误提示、唯一性检查、大小写处理统一改为 6 位数字。
改造点:
| 项 | 内容 |
| --- | --- |
| 校验 | 仅允许 `^[0-9]{6}$`,不做大小写转换。 |
| 使用记录 | 注册成功后写 `im_registration_invite_uses`,包含 `code`、`user_id`、`request_id`、`used_at`。 |
| 并发 | 使用事务或唯一约束防止超发。 |
| 状态 | `unused`、`used`、`expired`、`revoked`。 |
### 3.7 单聊消息
当前只有单条消息 detail 查询,不足以支撑后台列表。需要新增 internal message list resource。
新增 internal query:
| 路径 | 方法 | 查询参数 |
| --- | --- | --- |
| `/internal/admin-query/tenants/{tenant}/private-messages` | GET | `topic`、`from_user_id`、`to_user_id`、`content_type`、`status`、`keyword`、`created_at_start/end`、`page`、`per_page`。 |
| `/internal/admin-query/tenants/{tenant}/messages/{topic}/{seq}` | GET | 已有,继续作为详情。 |
返回字段:`topic`、`seq_id`、`from_user_id`、`to_user_id`、`head`、`content_type`、`content_excerpt`、`status`、`created_at`、`updated_at`。全文内容只在详情返回,并根据 Admin 权限决定是否脱敏。
新增 Kafka 命令建议:`message.hide`、`message.unhide`、`message.delete`、`message.restore`。如果只允许软删除,命令语义必须明确为后台不可见还是客户端不可见。
### 3.8 群聊消息
新增 internal group message list resource。可读取 `im_group_messages` 或统一消息表,但需要确保与客户端可见状态一致。
路径:`/internal/admin-query/tenants/{tenant}/group-messages`。
筛选:`topic`、`sender_user_id`、`content_type`、`status`、`keyword`、`created_at_start/end`。返回字段在单聊基础上增加 `group_name`、`reply_to_seq`、`at_users`。
处置命令复用消息命令,审计动作记录为 `message.hidden`、`message.deleted`、`message.restored`。
### 3.9 敏感词库
IM Server 需要新增敏感词运行时,而词库主数据由 Admin 控制库维护。IM Server 只消费配置事件和执行匹配。
改造点:
| 项 | 内容 |
| --- | --- |
| 缓存 | 按租户加载敏感词,支持分组、场景、优先级、状态。 |
| 匹配场景 | 消息发送、编辑消息、用户昵称/签名、群名/公告、反馈/举报内容。 |
| 动作 | `block` 拦截、`replace` 替换、`review` 进入审核、`audit_only` 仅审计。 |
| 审计 | 命中记录写 `im_sensitive_word_hits` 或统一 `im_audit_logs`。 |
| 性能 | 正常词库用 AC 自动机或等价高效结构;正则词单独限量执行。 |
### 3.10 文件管理
已有 internal `files`、`files/{id}`、`files/{id}/links` 与文件生命周期 Kafka 基础。需要补后台预览、下载和处置一致性。
改造点:
| 项 | 内容 |
| --- | --- |
| 文件状态 | `normal`、`quarantined`、`deleted`、`orphan`。 |
| 审核状态 | 复用 `pending`、`running`、`clean`、`risky`、`failed`。 |
| 预览下载 | 提供短期签名 URL 或代理下载,禁止返回永久存储地址。 |
| 隔离删除 | 后台命令应更新文件状态、断开消息/群文件链接、写审计。 |
| 安全审核 | 接入内容安全/恶意文件检测结果,并可重试审核。 |
### 3.11 用户反馈
已有 `feedback-submissions` 资源可查询。需要补处理状态回写能力和状态审计。
新增能力:
| 能力 | 说明 |
| --- | --- |
| 状态更新 | 提供 Kafka 命令或内部写接口:`feedback.status.update`。 |
| 附件 | 通过文件链接查询反馈附件,并返回签名预览 URL。 |
| 分类 | 分类来自 `im_feedback_config.categories`,Admin 配置后 IM bootstrap 同步。 |
| 审计 | 处理人、处理备注、前后状态写入审计。 |
状态枚举:`received`、`processing`、`resolved`、`closed`。
### 3.12 操作日志
IM Server 需要补充业务侧审计完整性。当前本地 `im_audit_logs` 为空,不能支撑后台多页面审计。
写入场景:用户状态变更、群治理、消息处置、文件处置、反馈处理、敏感词命中、登录成功/失败、配置热更新、Kafka 命令执行结果。
字段:`request_id`、`actor_user_id`、`action`、`target_type`、`target_id`、`ip`、`user_agent`、`detail`、`created_at`。`detail` 中敏感值必须脱敏或存 hash。
### 3.13 用户账号审计
新增或扩展 internal resource `user-account-audit`,按用户聚合账号生命周期事件。
路径:`/internal/admin-query/tenants/{tenant}/user-account-audit`。
筛选:`user_id`、`action`、`operator_id`、`ip`、`created_at_start/end`。返回:`event_id`、`user_id`、`action`、`operator_type`、`operator_id`、`before_state`、`after_state`、`reason`、`ip`、`device_id`、`created_at`。
动作枚举:`registered`、`login_success`、`logout`、`login_failed`、`password_changed`、`password_reset`、`banned`、`unbanned`、`frozen`、`unfrozen`、`device_kicked`、`cancelled`、`deleted`。
### 3.14 群事件审计
已有 `im_group_admin_logs` 与 group outbox,可新增 internal resource `group-event-audit` 或复用 `group_admin-logs` 扩展为全局查询。
路径:`/internal/admin-query/tenants/{tenant}/group-event-audit`。
筛选:`topic`、`operator_id`、`target_user_id`、`action`、`created_at_start/end`。返回:`event_id`、`topic`、`operator_id`、`target_user_id`、`action`、`object_type`、`object_id`、`before_data`、`after_data`、`created_at`。
### 3.15 管理员登录历史
该页面主要由 Admin Server 的 HotGo 登录日志提供,IM Server 无需改造。若以后要展示 Admin 调用 IM internal API 的访问审计,可在 internal API 层增加 `internal_api_access_logs`,但不作为复刻 P0。
### 3.16 用户登录历史
当前缺少完整登录历史表与查询。需要在认证成功、token 刷新、主动/被动登出时记录日志。
新增表建议:`im_user_login_logs`。
字段:`id`、`user_id`、`login_type`、`device_id`、`platform`、`ip`、`region`、`user_agent`、`request_id`、`result`、`login_at`、`logout_at`、`created_at`。
新增 internal query:`/internal/admin-query/tenants/{tenant}/user-login-logs`,支持按 `user_id`、`phone/account`、`ip`、`platform`、`login_at_start/end` 查询。
### 3.17 IP 账号统计
新增聚合查询,来源为注册、登录成功、登录失败、风控命中日志。
路径:`/internal/admin-query/tenants/{tenant}/ip-account-stats`。
字段:`ip`、`region`、`user_count`、`register_count`、`login_count`、`failure_count`、`first_seen_at`、`last_seen_at`、`risk_level`。该接口可以实时聚合,也可以由定时任务写入快照表以降低查询成本。
### 3.18 登录失败日志
新增表建议:`im_login_failure_logs`。
字段:`id`、`account`、`user_id`、`failure_type`、`reason`、`ip`、`region`、`device_id`、`platform`、`user_agent`、`request_id`、`attempt_at`。
新增 internal query:`/internal/admin-query/tenants/{tenant}/login-failure-logs`。失败类型枚举:`password_error`、`code_error`、`account_not_found`、`account_disabled`、`rate_limited`、`risk_blocked`。
### 3.19 私域设置
IM Server 需要确保客户端 `/v1/im/bootstrap` 使用 Admin 发布后的配置,并支持热更新。
配置来源:
| 配置 | 使用位置 |
| --- | --- |
| 注册/邀请码/短信 | 注册、验证码、密码找回。 |
| 上传/文件审核 | 上传、预览、消息附件。 |
| 反馈/邮箱/通知 | 客户端入口与提交表单。 |
| 版本/工作台 | bootstrap、app release、workbench。 |
| 声网/音视频 | `/v1/im/calls/rtc-join`。 |
| 好友功能 | `/v1/im/bootstrap` 返回 `global_friend_enabled`、入口隐藏建议和提示文案;实际拦截由后端完成。 |
| 消息宵禁 | `/v1/im/bootstrap` 返回当前宵禁摘要和提示文案;发送消息时仍以后端实时校验为准。 |
改造点:配置事件必须带版本号;IM 缓存应支持按租户回滚到上一发布版本;bootstrap 返回的配置只包含客户端需要的非敏感字段。
### 3.20 安全策略
IM Server 是安全策略的执行层。需要将 Admin 发布的策略接入登录、验证码、消息、文件、群、好友、实名、音视频等关键入口。全局禁止加好友和宵禁必须后端强制执行,客户端展示仅作为体验优化。
策略项:
| 策略 | 执行点 |
| --- | --- |
| 密码强度 | 注册、改密、重置密码。 |
| 登录限频 | 登录、验证码校验、密码找回。 |
| IP 黑白名单 | 登录、注册、发消息、上传文件。 |
| 敏感词 | 消息、资料、群资料、反馈。 |
| 文件审核 | 上传完成、消息发送前后、后台重审。 |
| 实名要求 | 加好友、建群、入群、发起音视频等敏感能力。 |
| 风控挑战 | 登录异常、IP 异常、设备异常。 |
| 全局好友功能 | 用户搜索、好友申请、二维码解析、群成员资料、个人名片、推荐联系人、邀请注册自动加好友。 |
| 消息宵禁 | 单聊/私聊发送消息入口,默认拦截 P2P topic 的 `{pub}`;如产品确认群聊也管控,可扩展到 group topic。 |
动作枚举统一为 `allow`、`challenge`、`block`、`audit_only`。
全局禁止加好友执行规则:
| 入口 | 后端行为 |
| --- | --- |
| 用户搜索 `/v1/im/users/search` | 可直接返回空列表,或返回脱敏结果但不允许发起好友申请。 |
| 用户详情 `/v1/im/users/{id}` | 非好友场景隐藏手机号、账号、二维码、个人扩展资料,只保留必要公开字段。 |
| 好友申请 `/v1/im/friend-requests` | 返回业务错误 reason,例如 `FRIEND_DISABLED`。 |
| 二维码解析 `/v1/im/qrcodes/resolve` | 禁止解析出可加好友目标,或返回脱敏提示。 |
| 群成员资料 | 群成员列表可保留昵称/头像用于群聊展示,但点击个人信息时返回脱敏资料并禁止添加。 |
| 邀请注册自动加好友 | 注册成功后跳过自动加好友并写审计,或按策略直接拒绝该邀请码能力。 |
宵禁执行规则:
| 项 | 说明 |
| --- | --- |
| 配置 | `message_curfew_enabled`、`message_curfew_rules`,规则包含时区、日期类型、开始时间、结束时间、scope、白名单、提示文案。 |
| 判断时机 | P2P topic 发布消息前校验;HTTP 消息发送、转发、草稿转发送等入口也要校验。 |
| 拦截结果 | 返回明确 reason,例如 `MESSAGE_CURFEW_ACTIVE`,并记录 `im_audit_logs` 或风控日志。 |
| 白名单 | 支持用户、系统账号、特定会话豁免。 |
| 跨天 | 支持如下班后 `18:00-09:00` 的跨天时间段。 |
## 4. Internal Admin Query 新增清单
建议在 `server/internal_admin_query_resources.go` 增补资源,并在 `server/internal_api.go` 路由分发中注册:
| 资源 | 必要性 | 说明 |
| --- | --- | --- |
| `dashboard/overview`、`dashboard/trends` | P0 | 首页统计。 |
| `private-messages`、`group-messages` | P0 | 消息管理列表。 |
| `user-login-logs`、`login-failure-logs` | P0 | 登录审计。 |
| `ip-account-stats` | P1 | IP 风险聚合。 |
| `user-account-audit`、`group-event-audit` | P1 | 专题审计。 |
| `sensitive-word-hits` | P1 | 敏感词命中查询。 |
所有列表接口返回现有结构:`items`、`total`、`page`、`per_page`。时间筛选继续使用 `created_at_start`、`created_at_end` 等字段,UID 参数继续支持数字 ID 与 `usr...` 编码。
## 5. Kafka 命令新增清单
已有用户与群治理命令保留。为复刻后台补充以下命令:
| 命令 | 说明 |
| --- | --- |
| `message.hide` | 后台隐藏消息,客户端不可见或后台标记隐藏。 |
| `message.unhide` | 恢复隐藏消息。 |
| `message.delete` | 后台删除消息,建议软删除。 |
| `message.restore` | 恢复误删消息。 |
| `file.quarantine` | 文件隔离。 |
| `file.delete` | 文件删除。 |
| `feedback.status.update` | 反馈处理状态更新。 |
| `group.suspend`、`group.unsuspend` | 如产品确认需要群封停/解封,则新增。 |
| `security.policy.reload` | 强制刷新安全策略缓存。 |
| `friend.policy.reload` | 强制刷新好友入口策略缓存,可与 `security.policy.reload` 合并实现。 |
| `curfew.policy.reload` | 强制刷新宵禁策略缓存,可与 `security.policy.reload` 合并实现。 |
命令 payload 必须包含 `command_id`、`actor_user_id`、`reason`、目标 ID、`version` 或 `expected_version`。执行后发布 result,状态为 `success`、`failed`、`dead`。
## 6. 数据库改造建议
配套建表与兼容迁移脚本见 [`sql/IM-Server改造.sql`](sql/IM-Server改造.sql)。脚本默认使用本地 `tinode`,上线时必须对每个独立租户数据库分别执行,租户业务表不增加 `tenant_id`。
租户库新增或补强表:
| 表 | 用途 |
| --- | --- |
| `im_user_login_logs` | 用户登录成功、登出、刷新等历史。 |
| `im_login_failure_logs` | 登录失败与风控拦截历史。 |
| `im_message_admin_actions` | 后台消息处置记录,可与消息表状态解耦。 |
| `im_sensitive_word_hits` | 敏感词命中记录。 |
| `im_ip_account_stats_daily` | IP 账号统计快照,可选。 |
已有表需补数据写入:`im_audit_logs` 当前本地为空,需要在关键业务入口统一写;`im_devices` 本地为空,需要确认设备绑定/解绑路径是否完整;`im_group_files` 本地为空,需要验证群文件写入链路。
索引建议:消息列表按 `(topic, created_at)`、`(from, created_at)`;登录日志按 `(user_id, login_at)`、`(ip, login_at)`;失败日志按 `(account, attempt_at)`、`(ip, attempt_at)`;敏感词命中按 `(scene, created_at)`、`(user_id, created_at)`。
## 7. 验收要求
1. Admin 20 个页面的查询按钮均能通过 internal query 返回真实数据或标准空态。
2. 用户、群、消息、文件、反馈的后台写操作均通过 Kafka 命令,具备幂等、结果回传、死信处理。
3. 登录历史、失败日志、账号审计、群事件审计不再依赖空表,关键入口可产生真实审计数据。
4. 敏感词与安全策略变更可热更新,无需重启 IM Server。
5. 所有 internal query 均保持只读、分页、鉴权、脱敏和错误 reason 规范。
6. 邀请码格式在 Admin 与 IM 注册链路完全一致,避免后台生成后客户端无法使用。
@@ -0,0 +1,70 @@
[
{
"endpoint": "/admin/stats",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/users",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/user-policy-groups",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/groups",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/groups/events",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/invite-codes",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/messages/search",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/sensitive-words",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/feedbacks",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/audit/logs",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/audit/user-account-logs",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/admin-login-history",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/user-login-history",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/audit/ip-user-stats",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/login-failure-logs",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/tenant-settings",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/security-settings",
"error": "TypeError: window.fetch is not a function"
}
]
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,350 @@
-- Admin Server schema changes for the GedaDa management-console adaptation.
-- Target: MySQL 8.0+, control database `hotgo`.
-- Scope: control-plane configuration only. Do not create tenant business data here.
-- Review before execution. DDL statements auto-commit in MySQL.
SET NAMES utf8mb4;
USE `hotgo`;
-- ============================================================
-- 1. User policy groups.
-- status: 1=enabled, 2=disabled.
-- policy_json action values: allow, deny, challenge, audit_only.
-- Friend entry values: search, profile_card, group_member, qrcode,
-- invite_auto_add, recommendation.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_user_policy_groups` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global default',
`group_code` varchar(64) NOT NULL COMMENT 'Stable policy group code',
`group_name` varchar(128) NOT NULL COMMENT 'Policy group name',
`priority` int NOT NULL DEFAULT 0 COMMENT 'Higher value has higher priority',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`policy_json` json NOT NULL COMMENT 'Login, friend, message, group and file policies',
`ip_whitelist` json DEFAULT NULL COMMENT 'IPv4/IPv6 or CIDR list',
`ip_blacklist` json DEFAULT NULL COMMENT 'IPv4/IPv6 or CIDR list',
`version` int unsigned NOT NULL DEFAULT 1 COMMENT 'Optimistic-lock/config version',
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_user_policy_group_code` (`tenant_id`, `group_code`),
KEY `idx_im_user_policy_group_list` (`tenant_id`, `status`, `priority`, `id`),
KEY `idx_im_user_policy_group_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM user policy groups';
CREATE TABLE IF NOT EXISTS `im_user_policy_group_members` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0,
`group_id` bigint NOT NULL,
`user_id` bigint NOT NULL COMMENT 'User ID in the tenant database',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`valid_from` datetime(3) DEFAULT NULL,
`valid_until` datetime(3) DEFAULT NULL,
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_user_policy_group_member` (`group_id`, `user_id`),
KEY `idx_im_user_policy_member_user` (`tenant_id`, `user_id`, `status`),
KEY `idx_im_user_policy_member_validity` (`status`, `valid_from`, `valid_until`),
KEY `idx_im_user_policy_member_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM user-to-policy-group assignments';
-- ============================================================
-- 2. Sensitive word dictionary.
-- match_type: exact, contains, regex, pinyin.
-- action: replace, block, audit_only, manual_review.
-- risk_level: low, medium, high.
-- scene_json values include private_message, group_message, user_profile,
-- group_profile, feedback, report.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_sensitive_word_groups` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global dictionary',
`group_code` varchar(64) NOT NULL,
`group_name` varchar(128) NOT NULL,
`priority` int NOT NULL DEFAULT 0,
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_sensitive_word_group_code` (`tenant_id`, `group_code`),
KEY `idx_im_sensitive_word_group_list` (`tenant_id`, `status`, `priority`, `id`),
KEY `idx_im_sensitive_word_group_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM sensitive word groups';
CREATE TABLE IF NOT EXISTS `im_sensitive_words` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global dictionary',
`group_id` bigint NOT NULL,
`word` varchar(255) NOT NULL,
`word_hash` char(64) NOT NULL COMMENT 'SHA-256 of normalized word',
`match_type` varchar(24) NOT NULL DEFAULT 'contains',
`scene_json` json NOT NULL COMMENT 'Enabled scenes',
`action` varchar(24) NOT NULL DEFAULT 'block',
`replacement` varchar(255) NOT NULL DEFAULT '',
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
`priority` int NOT NULL DEFAULT 0,
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_sensitive_word_normalized` (`tenant_id`, `group_id`, `word_hash`),
KEY `idx_im_sensitive_word_list` (`tenant_id`, `status`, `risk_level`, `id`),
KEY `idx_im_sensitive_word_group` (`group_id`, `status`, `priority`),
KEY `idx_im_sensitive_word_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM sensitive words';
-- ============================================================
-- 3. Versioned security policy.
-- publish_status: draft, published, archived, failed.
-- The latest published row is the source of truth. The summary switches in
-- im_tenant_config are updated in the same application transaction on publish.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_security_policy` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global fallback policy',
`policy_version` int unsigned NOT NULL,
`policy_name` varchar(128) NOT NULL DEFAULT '',
`publish_status` varchar(16) NOT NULL DEFAULT 'draft',
`password_policy` json DEFAULT NULL,
`login_rate_limit` json DEFAULT NULL,
`sensitive_word_policy` json DEFAULT NULL,
`file_review_policy` json DEFAULT NULL,
`realname_policy` json DEFAULT NULL,
`risk_challenge_policy` json DEFAULT NULL,
`global_friend_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Backend master switch for every add-friend entry',
`friend_disabled_policy` json DEFAULT NULL COMMENT 'Profile masking, QR and group-member restrictions',
`message_curfew_enabled` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Backend master switch for message curfew',
`remark` varchar(500) NOT NULL DEFAULT '',
`published_at` datetime(3) DEFAULT NULL,
`published_by` bigint DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_security_policy_version` (`tenant_id`, `policy_version`),
KEY `idx_im_security_policy_publish` (`tenant_id`, `publish_status`, `published_at`),
KEY `idx_im_security_policy_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Versioned IM security policies';
CREATE TABLE IF NOT EXISTS `im_message_curfew_rules` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0,
`policy_id` bigint NOT NULL,
`rule_code` varchar(64) NOT NULL,
`rule_name` varchar(128) NOT NULL,
`date_type` varchar(16) NOT NULL DEFAULT 'daily' COMMENT 'daily, weekday, weekend, custom',
`custom_dates` json DEFAULT NULL COMMENT 'YYYY-MM-DD values for custom date type',
`weekdays` json DEFAULT NULL COMMENT 'ISO weekdays 1 through 7',
`timezone` varchar(64) NOT NULL DEFAULT 'Asia/Shanghai',
`start_time` time NOT NULL,
`end_time` time NOT NULL COMMENT 'Less than start_time means crossing midnight',
`scope` varchar(24) NOT NULL DEFAULT 'private_message' COMMENT 'private_message or group_message',
`whitelist_user_ids` json DEFAULT NULL,
`whitelist_group_ids` json DEFAULT NULL,
`message` varchar(255) NOT NULL DEFAULT '当前时段暂不能发送消息',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`sort` int NOT NULL DEFAULT 0,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_message_curfew_rule` (`policy_id`, `rule_code`),
KEY `idx_im_message_curfew_policy` (`tenant_id`, `policy_id`, `status`, `sort`),
KEY `idx_im_message_curfew_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Message curfew rules';
CREATE TABLE IF NOT EXISTS `im_security_ip_rules` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0,
`rule_type` varchar(16) NOT NULL COMMENT 'whitelist or blacklist',
`ip_cidr` varchar(64) NOT NULL COMMENT 'IPv4, IPv6 or CIDR',
`action` varchar(24) NOT NULL DEFAULT 'block' COMMENT 'allow, challenge, block, audit_only',
`scene_json` json NOT NULL COMMENT 'login, register, message, upload and other scenes',
`risk_level` varchar(16) NOT NULL DEFAULT 'high',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`expires_at` datetime(3) DEFAULT NULL,
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_security_ip_rule` (`tenant_id`, `rule_type`, `ip_cidr`),
KEY `idx_im_security_ip_rule_list` (`tenant_id`, `status`, `rule_type`, `id`),
KEY `idx_im_security_ip_rule_expiry` (`status`, `expires_at`),
KEY `idx_im_security_ip_rule_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM IP whitelist and blacklist rules';
-- ============================================================
-- 4. Lightweight published-policy summary in im_tenant_config.
-- These columns are read optimizations, not the policy source of truth.
-- The conditional ALTER blocks are safe to rerun on MySQL 8.0.
-- ============================================================
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'default_policy_group_id') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `default_policy_group_id` BIGINT DEFAULT NULL COMMENT ''Default user policy group'' AFTER `max_group_members`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'security_policy_version') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `security_policy_version` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''Published security policy version'' AFTER `default_policy_group_id`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'global_friend_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `global_friend_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Published global friend switch'' AFTER `security_policy_version`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'message_curfew_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `message_curfew_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''Published message curfew switch'' AFTER `global_friend_enabled`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- ============================================================
-- 5. Invitation-code compatibility for the 6-digit target format.
-- Existing 5-letter codes remain stored and valid during the application
-- rollout. New code generation must switch to exactly six ASCII digits, while
-- Admin and IM Server temporarily accept both legacy 5-letter and new 6-digit
-- values. max_uses=0 preserves the current unlimited-use behavior.
-- ============================================================
SET @im_sql = IF(
(SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_codes'
AND COLUMN_NAME = 'code') = 5,
'ALTER TABLE `im_registration_invite_codes` MODIFY COLUMN `code` CHAR(6) CHARACTER SET ascii COLLATE ascii_bin NOT NULL',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_codes'
AND COLUMN_NAME = 'max_uses') = 0,
'ALTER TABLE `im_registration_invite_codes` ADD COLUMN `max_uses` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''0 means unlimited'' AFTER `expires_at`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_batches'
AND COLUMN_NAME = 'max_uses_per_code') = 0,
'ALTER TABLE `im_registration_invite_batches` ADD COLUMN `max_uses_per_code` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''0 means unlimited'' AFTER `quantity`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- ============================================================
-- 6. Safe global fallback policy.
-- Tenant-specific published policies override tenant_id=0.
-- This keeps existing behavior permissive until an administrator publishes
-- the first tenant policy.
-- ============================================================
INSERT INTO `im_security_policy` (
`tenant_id`, `policy_version`, `policy_name`, `publish_status`,
`global_friend_enabled`, `friend_disabled_policy`,
`message_curfew_enabled`, `remark`, `published_at`, `published_by`,
`created_by`, `updated_by`
)
SELECT
0, 1, '全局默认安全策略', 'published',
1,
JSON_OBJECT(
'hide_profile', TRUE,
'block_qrcode', TRUE,
'block_group_member_entry', TRUE,
'blocked_entries', JSON_ARRAY(
'search', 'profile_card', 'group_member',
'qrcode', 'invite_auto_add', 'recommendation'
),
'message', '当前时段暂不允许添加好友'
),
0, '系统初始化:保持原有好友和消息行为', CURRENT_TIMESTAMP(3), 0, 0, 0
WHERE NOT EXISTS (
SELECT 1 FROM `im_security_policy`
WHERE `tenant_id` = 0 AND `publish_status` = 'published' AND `deleted_at` IS NULL
);
UPDATE `im_security_policy`
SET `friend_disabled_policy` = JSON_SET(
`friend_disabled_policy`,
'$.blocked_entries',
JSON_ARRAY('search', 'profile_card', 'group_member', 'qrcode', 'invite_auto_add', 'recommendation')
)
WHERE `tenant_id` = 0
AND `publish_status` = 'published'
AND `deleted_at` IS NULL
AND JSON_CONTAINS_PATH(`friend_disabled_policy`, 'one', '$.blocked_entries') = 0;
-- Post-deployment checks (read-only):
-- SELECT tenant_id, group_code, status, priority FROM im_user_policy_groups;
-- SELECT tenant_id, policy_version, publish_status,
-- global_friend_enabled, message_curfew_enabled
-- FROM im_security_policy ORDER BY tenant_id, policy_version DESC;
-- SELECT tenant_id, policy_id, rule_code, date_type, start_time, end_time, scope
-- FROM im_message_curfew_rules WHERE deleted_at IS NULL;
@@ -0,0 +1,464 @@
-- IM Server schema changes for the GedaDa management-console adaptation.
-- Target: MySQL 8.0+, one independent tenant business database.
-- Default local tenant database: `tinode`.
-- IMPORTANT: execute this migration once for every tenant database. Tenant
-- business tables intentionally do not contain tenant_id.
-- Review before execution. DDL statements auto-commit in MySQL.
SET NAMES utf8mb4;
USE `tinode`;
-- ============================================================
-- 0. User account number.
-- account_no is the short user number shown in Admin. The real primary key
-- im_users.id remains the internal Tinode/Snowflake user ID.
-- Existing users are backfilled from 1000 by created time; new users are
-- assigned by IM Server in the user creation transaction.
-- ============================================================
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_users' AND COLUMN_NAME = 'account_no') = 0,
'ALTER TABLE `im_users` ADD COLUMN `account_no` int unsigned NOT NULL DEFAULT 0 COMMENT ''用户编号'' AFTER `id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @im_user_account_no := (
SELECT GREATEST(COALESCE(MAX(`account_no`), 999), 999)
FROM `im_users`
WHERE `account_no` > 0
);
UPDATE `im_users` AS u
JOIN (
SELECT ordered.`id`, (@im_user_account_no := @im_user_account_no + 1) AS next_no
FROM (
SELECT `id`
FROM `im_users`
WHERE `account_no` = 0
ORDER BY `createdat`, `id`
) AS ordered
) AS seq ON seq.`id` = u.`id`
SET u.`account_no` = seq.next_no
WHERE u.`account_no` = 0;
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_users' AND INDEX_NAME = 'uk_im_users_account_no') = 0,
'CREATE UNIQUE INDEX `uk_im_users_account_no` ON `im_users` (`account_no`)',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ============================================================
-- 1. Applied runtime policy snapshots.
-- policy_type: security, user_policy_groups, sensitive_words.
-- The application uses the latest row with apply_status=applied and also keeps
-- an in-memory cache. This table makes restart and Kafka replay deterministic.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_runtime_policy_snapshots` (
`id` bigint NOT NULL AUTO_INCREMENT,
`policy_type` varchar(32) NOT NULL,
`policy_version` bigint unsigned NOT NULL,
`payload` json NOT NULL,
`payload_sha256` char(64) NOT NULL,
`source_event_id` varchar(96) DEFAULT NULL COMMENT 'NULL for a manual bootstrap snapshot',
`apply_status` varchar(16) NOT NULL DEFAULT 'applied' COMMENT 'applied or failed',
`apply_error` varchar(1000) NOT NULL DEFAULT '',
`applied_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_runtime_policy_version` (`policy_type`, `policy_version`),
UNIQUE KEY `uk_im_runtime_policy_event` (`source_event_id`),
KEY `idx_im_runtime_policy_latest` (`policy_type`, `apply_status`, `policy_version` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Last applied Admin policy payloads';
-- ============================================================
-- 2. User login history and login failure history.
-- Never store plaintext passwords, verification codes, access tokens or refresh
-- tokens. account_hash is SHA-256 of the normalized login account; account_mask
-- is the value shown in Admin.
-- login_type: password, sms_code, sso, qr_code, token_refresh.
-- event_type: login, refresh, logout, kicked, session_expired.
-- failure_type: password_error, code_error, account_not_found,
-- account_disabled, rate_limited, risk_blocked.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_user_login_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`username` varchar(64) NOT NULL DEFAULT '',
`account_type` varchar(24) NOT NULL DEFAULT '',
`account_hash` char(64) NOT NULL DEFAULT '',
`account_mask` varchar(128) NOT NULL DEFAULT '',
`login_type` varchar(24) NOT NULL,
`event_type` varchar(24) NOT NULL DEFAULT 'login',
`session_id` varchar(96) NOT NULL DEFAULT '' COMMENT 'Opaque session identifier, never an auth token',
`device_id` varchar(255) NOT NULL DEFAULT '',
`platform` varchar(32) NOT NULL DEFAULT 'unknown',
`app_version` varchar(32) NOT NULL DEFAULT '',
`ip` varchar(64) NOT NULL DEFAULT '',
`region` varchar(128) NOT NULL DEFAULT '',
`user_agent` varchar(512) NOT NULL DEFAULT '',
`risk_level` varchar(16) NOT NULL DEFAULT 'low',
`request_id` varchar(64) NOT NULL DEFAULT '',
`login_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`logout_at` datetime(3) DEFAULT NULL,
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_user_login_user_time` (`user_id`, `login_at` DESC, `id` DESC),
KEY `idx_im_user_login_ip_time` (`ip`, `login_at` DESC, `id` DESC),
KEY `idx_im_user_login_device_time` (`device_id`(64), `login_at` DESC),
KEY `idx_im_user_login_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='User login and session event history';
CREATE TABLE IF NOT EXISTS `im_login_failure_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint DEFAULT NULL COMMENT 'NULL when the account cannot be resolved',
`account_type` varchar(24) NOT NULL DEFAULT '',
`account_hash` char(64) NOT NULL DEFAULT '',
`account_mask` varchar(128) NOT NULL DEFAULT '',
`failure_type` varchar(32) NOT NULL,
`reason` varchar(96) NOT NULL DEFAULT '' COMMENT 'Stable backend reason code',
`detail` varchar(500) NOT NULL DEFAULT '' COMMENT 'Sanitized detail without credentials',
`ip` varchar(64) NOT NULL DEFAULT '',
`region` varchar(128) NOT NULL DEFAULT '',
`device_id` varchar(255) NOT NULL DEFAULT '',
`platform` varchar(32) NOT NULL DEFAULT 'unknown',
`app_version` varchar(32) NOT NULL DEFAULT '',
`user_agent` varchar(512) NOT NULL DEFAULT '',
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
`risk_action` varchar(24) NOT NULL DEFAULT 'audit_only',
`request_id` varchar(64) NOT NULL DEFAULT '',
`attempt_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_login_failure_account_time` (`account_hash`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_user_time` (`user_id`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_ip_time` (`ip`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_type_time` (`failure_type`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Authentication failure and risk-block history';
-- ============================================================
-- 3. Message administration actions.
-- Messages remain in im_messages/im_group_messages. This table is an immutable
-- action overlay so hide/delete can be audited and restored.
-- conversation_type: private, group.
-- action: hide, unhide, delete, restore.
-- action_status: pending, success, failed, dead.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_message_admin_actions` (
`id` bigint NOT NULL AUTO_INCREMENT,
`action_id` varchar(96) NOT NULL,
`command_id` varchar(96) NOT NULL,
`conversation_type` varchar(16) NOT NULL,
`topic` varchar(64) NOT NULL,
`seqid` bigint NOT NULL,
`sender_user_id` bigint DEFAULT NULL,
`action` varchar(16) NOT NULL,
`action_status` varchar(16) NOT NULL DEFAULT 'pending',
`reason` varchar(500) NOT NULL DEFAULT '',
`actor_admin_id` bigint NOT NULL DEFAULT 0,
`request_id` varchar(64) NOT NULL DEFAULT '',
`before_snapshot` json DEFAULT NULL,
`after_snapshot` json DEFAULT NULL,
`failure_reason` varchar(96) NOT NULL DEFAULT '',
`failure_message` varchar(1000) NOT NULL DEFAULT '',
`processed_at` datetime(3) DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_message_admin_action_id` (`action_id`),
UNIQUE KEY `uk_im_message_admin_command` (`command_id`),
KEY `idx_im_message_admin_target` (`conversation_type`, `topic`, `seqid`, `id` DESC),
KEY `idx_im_message_admin_status` (`action_status`, `created_at`, `id`),
KEY `idx_im_message_admin_actor` (`actor_admin_id`, `created_at` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Auditable Admin message actions';
-- ============================================================
-- 4. Sensitive-word hits and policy decisions.
-- Store only a digest and a short masked excerpt. Do not duplicate complete
-- private messages in log tables.
-- policy decision reasons include FRIEND_DISABLED and MESSAGE_CURFEW_ACTIVE.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_sensitive_word_hits` (
`id` bigint NOT NULL AUTO_INCREMENT,
`word_id` bigint NOT NULL DEFAULT 0 COMMENT 'Admin control-database word ID',
`word_group_id` bigint NOT NULL DEFAULT 0,
`word_snapshot` varchar(255) NOT NULL DEFAULT '' COMMENT 'Masked word snapshot',
`scene` varchar(32) NOT NULL,
`user_id` bigint NOT NULL DEFAULT 0,
`target_user_id` bigint DEFAULT NULL,
`topic` varchar(64) NOT NULL DEFAULT '',
`seqid` bigint DEFAULT NULL,
`content_sha256` char(64) NOT NULL DEFAULT '',
`matched_excerpt` varchar(500) NOT NULL DEFAULT '',
`match_type` varchar(24) NOT NULL DEFAULT 'contains',
`action` varchar(24) NOT NULL,
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
`request_id` varchar(64) NOT NULL DEFAULT '',
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_sensitive_hit_scene_time` (`scene`, `created_at` DESC, `id` DESC),
KEY `idx_im_sensitive_hit_user_time` (`user_id`, `created_at` DESC, `id` DESC),
KEY `idx_im_sensitive_hit_target` (`topic`, `seqid`, `id` DESC),
KEY `idx_im_sensitive_hit_word` (`word_id`, `created_at` DESC),
KEY `idx_im_sensitive_hit_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Sensitive-word match events';
CREATE TABLE IF NOT EXISTS `im_policy_decision_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`policy_type` varchar(32) NOT NULL COMMENT 'security or user_policy_group',
`policy_version` bigint unsigned NOT NULL DEFAULT 0,
`policy_group_id` bigint NOT NULL DEFAULT 0,
`scene` varchar(64) NOT NULL COMMENT 'friend_request, qrcode_resolve, private_message and so on',
`action` varchar(24) NOT NULL COMMENT 'deny, challenge or audit_only',
`reason_code` varchar(96) NOT NULL COMMENT 'For example FRIEND_DISABLED or MESSAGE_CURFEW_ACTIVE',
`user_id` bigint NOT NULL DEFAULT 0,
`target_user_id` bigint DEFAULT NULL,
`topic` varchar(64) NOT NULL DEFAULT '',
`request_id` varchar(64) NOT NULL DEFAULT '',
`ip` varchar(64) NOT NULL DEFAULT '',
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_policy_decision_user_time` (`user_id`, `created_at` DESC, `id` DESC),
KEY `idx_im_policy_decision_reason_time` (`reason_code`, `created_at` DESC, `id` DESC),
KEY `idx_im_policy_decision_scene_time` (`scene`, `created_at` DESC, `id` DESC),
KEY `idx_im_policy_decision_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Runtime policy deny/challenge decisions';
-- ============================================================
-- 5. Group event history and daily IP/account statistics.
-- im_group_events_outbox remains the delivery table; this log is the queryable
-- business history after the operation result is known.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_group_event_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`event_id` varchar(96) NOT NULL,
`topic` varchar(64) NOT NULL,
`group_id` bigint DEFAULT NULL,
`event_type` varchar(64) NOT NULL,
`actor_user_id` bigint NOT NULL DEFAULT 0,
`target_user_id` bigint DEFAULT NULL,
`result` varchar(16) NOT NULL DEFAULT 'success',
`reason` varchar(500) NOT NULL DEFAULT '',
`request_id` varchar(64) NOT NULL DEFAULT '',
`before_snapshot` json DEFAULT NULL,
`after_snapshot` json DEFAULT NULL,
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_group_event_log_event` (`event_id`),
KEY `idx_im_group_event_topic_time` (`topic`, `created_at` DESC, `id` DESC),
KEY `idx_im_group_event_type_time` (`event_type`, `created_at` DESC, `id` DESC),
KEY `idx_im_group_event_actor_time` (`actor_user_id`, `created_at` DESC, `id` DESC),
KEY `idx_im_group_event_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Queryable group event audit history';
CREATE TABLE IF NOT EXISTS `im_ip_account_stats_daily` (
`id` bigint NOT NULL AUTO_INCREMENT,
`stat_date` date NOT NULL,
`ip` varchar(64) NOT NULL,
`region` varchar(128) NOT NULL DEFAULT '',
`account_count` int unsigned NOT NULL DEFAULT 0,
`user_count` int unsigned NOT NULL DEFAULT 0,
`login_success_count` int unsigned NOT NULL DEFAULT 0,
`login_failure_count` int unsigned NOT NULL DEFAULT 0,
`first_seen_at` datetime(3) DEFAULT NULL,
`last_seen_at` datetime(3) DEFAULT NULL,
`risk_level` varchar(16) NOT NULL DEFAULT 'low',
`risk_action` varchar(24) NOT NULL DEFAULT 'audit_only',
`sample_user_ids` json DEFAULT NULL COMMENT 'Bounded sample only, not an unlimited array',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_ip_account_stats_day_ip` (`stat_date`, `ip`),
KEY `idx_im_ip_account_stats_ip_day` (`ip`, `stat_date` DESC),
KEY `idx_im_ip_account_stats_risk_day` (`risk_level`, `stat_date` DESC, `id` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Daily IP and account aggregation';
-- ============================================================
-- 6. Compatible enhancements to existing tables.
-- Every ALTER is guarded by information_schema and can be rerun.
-- ============================================================
-- Message management queries need topic/time and sender/time indexes.
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_messages'
AND INDEX_NAME = 'idx_im_messages_topic_created') = 0,
'ALTER TABLE `im_messages` ADD INDEX `idx_im_messages_topic_created` (`topic`, `createdat`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_messages'
AND INDEX_NAME = 'idx_im_messages_from_created') = 0,
'ALTER TABLE `im_messages` ADD INDEX `idx_im_messages_from_created` (`from`, `createdat`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- Extend the existing Tinode device record for device and login-history views.
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'firstseen') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `firstseen` DATETIME(3) DEFAULT NULL AFTER `platform`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'lastip') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `lastip` VARCHAR(64) NOT NULL DEFAULT '''' AFTER `lastseen`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'useragent') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `useragent` VARCHAR(512) NOT NULL DEFAULT '''' AFTER `lastip`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'appversion') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `appversion` VARCHAR(32) NOT NULL DEFAULT '''' AFTER `useragent`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'status') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `status` VARCHAR(16) NOT NULL DEFAULT ''active'' AFTER `appversion`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- Feedback handling fields required by the Admin processing workflow.
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'assigned_to') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `assigned_to` BIGINT DEFAULT NULL AFTER `status`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'handled_by') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `handled_by` BIGINT DEFAULT NULL AFTER `assigned_to`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'handled_at') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `handled_at` DATETIME(3) DEFAULT NULL AFTER `handled_by`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'resolution') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `resolution` VARCHAR(1000) NOT NULL DEFAULT '''' AFTER `handled_at`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'admin_remark') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `admin_remark` VARCHAR(1000) NOT NULL DEFAULT '''' AFTER `resolution`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND INDEX_NAME = 'idx_im_feedback_status_time') = 0,
'ALTER TABLE `im_feedback_submissions` ADD INDEX `idx_im_feedback_status_time` (`status`, `created_at`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_audit_logs'
AND INDEX_NAME = 'idx_im_audit_logs_ip_time') = 0,
'ALTER TABLE `im_audit_logs` ADD INDEX `idx_im_audit_logs_ip_time` (`ip`, `created_at`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- Post-deployment checks (read-only):
-- SELECT policy_type, policy_version, apply_status, applied_at
-- FROM im_runtime_policy_snapshots ORDER BY policy_type, policy_version DESC;
-- SELECT reason_code, scene, COUNT(*) AS deny_count
-- FROM im_policy_decision_logs
-- WHERE created_at >= CURRENT_DATE
-- GROUP BY reason_code, scene;
-- SELECT stat_date, ip, account_count, login_failure_count, risk_level
-- FROM im_ip_account_stats_daily ORDER BY stat_date DESC, login_failure_count DESC;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,570 @@
# 服务端消息加密与内容审核改造文档
> 文档状态:待开发
> 编制日期:2026-09-13
> 适用项目:`im_manager`(Admin Server)、`im_server`(IM Server)
> 本文档是后续数据库设计、后端开发、联调、迁移和验收的共同依据。
## 1. 建设目标
本次改造同时建设两项能力:
1. 内容审核:IM Server 在保存或修改内容前,根据商户策略执行本地敏感词检查和阿里云文本内容审核。
2. 消息加密存储:审核通过的消息在 IM Server 内部使用 AES-256-GCM 加密,国内 IM 业务库只保存密文;经授权的后台查询由 IM Server 解密后返回明文。
本次不做以下内容:
- 不做客户端 AES 二次加密。
- 不做端到端加密(E2EE)。
- 不改变现有 HTTPS/WSS 传输协议。
- 不允许 Admin Server 直接读取 IM 数据库并自行解密。
- 不在日志、Kafka 事件、死信或监控标签中保存消息明文。
## 2. 安全边界和部署前提
### 2.1 部署分工
| 区域 | 系统 | 保存数据 |
| --- | --- | --- |
| 国外 | HotGo / Admin Server | 商户审核策略、阿里云配置、字典根密钥、商户消息密钥 |
| 国内 | IM Server | 用户、会话、消息密文、审核结果和处置记录 |
该方案主要防护目标是:任意一侧数据库单独泄露时,不能直接恢复消息明文。
已知边界:HotGo 数据库、IM 数据库和完整代码同时泄露不在本方案的主要防护范围内。
### 2.2 传输安全
- 客户端调用必须使用 `https://` 和 `wss://`。
- 阿里云审核请求必须使用 HTTPS。
- Admin Server 与 IM Server 的内部配置和查询接口必须使用 HTTPS,并保留现有签名鉴权;跨公网部署时建议叠加 mTLS 或固定出口 IP 白名单。
- TLS 只解决传输保护,AES-256-GCM 解决消息落库后的静态保护。
## 3. 总体架构
```text
客户端
| HTTPS/WSS明文业务数据(TLS通道内不可见)
v
IM Server
|-- 实时权限、宵禁、用户状态等校验
|-- 获取商户审核策略
|-- 本地敏感词补充检查
|-- 阿里云 TextModerationPlus 审核
|-- AES-256-GCM 加密
`-- 密文写入国内IM库
Admin Server(国外HotGo)
|-- 管理阿里云配置
|-- 管理商户审核策略
|-- 管理商户消息密钥
|-- 通过内部接口下发配置/密钥
`-- 通过IM内部查询接口查看解密后消息
```
## 4. 内容审核设计
### 4.1 审核范围
| 场景 | 阿里云 Service | 审核时机 |
| --- | --- | --- |
| 用户昵称 | `nickname_detection_pro` | 更新用户资料前 |
| 群昵称 | `nickname_detection_pro` | 更新群成员资料前 |
| 群名称 | `nickname_detection_pro` | 创建群或修改群资料前 |
| 用户签名 | `nickname_detection_pro` | 更新用户资料前 |
| 单聊文本 | `chat_detection_pro` | 消息入库及广播前 |
| 群聊文本 | `chat_detection_pro` | 消息入库及广播前 |
| 编辑后消息 | `chat_detection_pro` | 新版本入库前 |
| 群公告 | `chat_detection_pro` | 发布或修改前 |
图片、视频和文件继续走文件审核链路,不属于本文本审核接口范围。
### 4.2 商户范围
- 第一阶段只为 `tenant.code=default` 的上架默认商户开启阿里云内容审核。
- 其他团队商户默认关闭,不调用阿里云,不产生审核费用。
- 数据模型保留按商户开启的能力,但只有平台管理员可变更。
- 商户端管理员不得看到阿里云 AK/SK,不得修改平台级审核服务配置。
### 4.3 后台配置项
#### 4.3.1 阿里云服务配置
复用 `im_external_config` 的 `service=moderation`,由平台管理员维护:
| 配置 | 说明 |
| --- | --- |
| `provider` | 固定为 `aliyun_green` |
| `endpoint` | 默认 `green-cip.cn-shanghai.aliyuncs.com` |
| `enabled` | 平台总开关 |
| `config_json.region_id` | 默认 `cn-shanghai` |
| `config_json.nickname_service` | 默认 `nickname_detection_pro` |
| `config_json.chat_service` | 默认 `chat_detection_pro` |
| `config_json.timeout_ms` | 单次请求超时,建议 1000~2000 ms |
| `config_json.failure_mode` | `allow` 或 `block` |
| `secret_json.access_key_id` | 只写不回显 |
| `secret_json.access_key_secret` | 只写不回显 |
“检查配置”必须真实调用阿里云测试文本,不能只检查字段非空。返回仅包含成功与否、请求耗时、RequestID 和脱敏错误。
#### 4.3.2 商户审核策略
可在 `im_security_policy.sensitive_word_policy` 中保存,避免新增重复策略表。JSON 结构固定为:
```json
{
"enabled": true,
"aliyun_enabled": true,
"local_words_enabled": true,
"scenes": {
"user_nickname": true,
"user_signature": true,
"group_name": true,
"group_nickname": true,
"private_message": true,
"group_message": true,
"group_announcement": true
},
"risk_actions": {
"high": "block",
"medium": "block",
"low": "audit_only",
"none": "allow"
},
"failure_mode": "allow",
"block_message": "内容不符合平台规范,请修改后重试"
}
```
策略发布后使用现有 `im.config.event` 发布配置事件,IM Server 按商户清理本地缓存。
### 4.4 文本提取
审核输入不能直接对任意 JSON 执行字符串化,应按 Drafty 或消息类型提取用户可见文本:
- 普通文本:提取完整文本。
- Drafty 富文本:提取 `txt` 及按钮、链接等用户可见标题。
- 图片/文件/语音:审核文件名、说明和附带文本,文件本体走文件审核。
- 系统消息、音视频信令和已签名服务端消息不调用文本审核。
- 空文本直接放行。
### 4.5 阿里云结果处置
IM Server 统一映射为:
| 内部结果 | 动作 |
| --- | --- |
| `allow` | 继续加密、入库和广播 |
| `block` | 不入库、不广播,返回统一业务错误 |
| `audit_only` | 允许发送,但写入审核命中日志 |
| `retryable_error` | 根据 `failure_mode` 放行或拦截 |
不只依赖 `RiskLevel/Label` 一个字段,需保留与验证阿里云原始结果中的扩展判断,针对引流、诈骗等样本建立回归测试集。
### 4.6 超时与降级
- 阿里云请求不得无限等待。
- 建议默认 `failure_mode=allow`,阿里云异常时允许发送并记录告警,防止第三方故障造成 IM 全局不可用。
- 连续失败达到阈值后进入短时熔断;熔断期间按失败策略处理。
- 阿里云调用不得在数据库事务中执行。
## 5. 消息加密设计
### 5.1 算法
- 算法:AES-256-GCM。
- DEK 长度:32 字节。
- Nonce:每次加密使用 `crypto/rand` 生成的独立 12 字节随机值。
- GCM 标签:使用 Go 标准库默认 16 字节认证标签。
- 明文编码:将原 `content` JSON 规范化序列化为 UTF-8 字节。
- 不允许固定 Nonce,不允许 AES-ECB,不使用未认证的 AES-CBC/CTR。
### 5.2 AAD
每条消息加密时绑定以下 AAD:
```text
tenant_id | topic | seqid | from | client_msg_id | key_version | content
```
`content` 是字面标识,用于与未来可能加密的 `head` 区分。AAD 按固定字段顺序和长度前缀编码,禁止使用可产生拼接歧义的简单字符串。
### 5.3 密钥层级
```text
HotGo字典根密钥(KEK)
-> 加密每个商户、每个版本的DEK
-> DEK加密该商户消息
```
根密钥使用 HotGo `hg_sys_dict_data` 保存,但必须满足:
1. 使用独立的内部字典类型,不出现在通用字典管理页。
2. 后端字典列表、选项和导出接口必须对该类型做服务端拒绝,不能仅依赖菜单隐藏。
3. 根密钥为 32 字节强随机值的 Base64 编码,不使用口令、UUID 或可读文本。
4. 根密钥只由 Admin Server 密钥服务读取,不通过通用配置接口返回。
5. 根密钥不写日志,不在后台页面回显,不进入 Kafka 消息。
### 5.4 商户密钥管理表
在 HotGo 库新增 `im_message_encryption_keys`:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | bigint | 主键 |
| `tenant_id` | bigint | 商户 ID |
| `key_version` | int | 密钥版本,从 1 递增 |
| `algorithm` | varchar(32) | 固定 `AES-256-GCM` |
| `encrypted_dek` | blob | KEK 加密后的 DEK |
| `dek_nonce` | varbinary(12) | 加密 DEK 时的随机 Nonce |
| `status` | varchar(16) | `pending` / `active` / `retired` / `disabled` |
| `created_at` | datetime | 创建时间 |
| `activated_at` | datetime | 启用时间 |
| `retired_at` | datetime | 退役时间 |
| `created_by` | bigint | 操作人 |
| `remark` | varchar(255) | 备注 |
约束:
- 唯一键:`tenant_id + key_version`。
- 每个商户同时只能有一把 `active` 密钥。
- `retired` 密钥仍可用于解密历史消息,不得再用于新消息加密。
- `disabled` 只用于安全事故应急,禁用前必须完成历史消息转密钥。
### 5.5 IM 消息表改造
`im_messages` 和 `im_message_revisions` 新增:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `content_ciphertext` | longblob | 消息密文,包含 GCM 认证标签 |
| `content_nonce` | varbinary(12) | 本条消息随机 Nonce |
| `key_version` | int | 商户 DEK 版本 |
| `encryption_alg` | varchar(32) | `AES-256-GCM` |
| `encrypted` | tinyint | `0` 明文兼容,`1` 密文 |
迁移完成前保留现有 `content JSON`,规则如下:
- 新写入:`content=NULL`、`encrypted=1`,只保存密文。
- 旧数据:`content IS NOT NULL`、`encrypted=0`,按原逻辑读取。
- 解密成功后还原为原 `any` JSON 结构,不修改对客户端的协议。
- `head` 第一阶段不加密,但禁止在 `head` 中放置消息正文、联系方式等敏感明文。
`im_group_messages` 是群消息派生查询表,不得另存一份明文。实现时二选一:
1. 推荐:仅保存索引、状态和统计字段,通过 `topic + seqid` 关联 `im_messages` 解密。
2. 兼容:同步增加密文字段,但加密输入和密文必须与主消息表保持一致。
不允许 `im_messages` 已加密而 `im_group_messages.content` 仍保留明文。
### 5.6 加密和解密时机
#### 新消息
```text
校验消息
-> 提取审核文本
-> 内容审核
-> 分配seqid/组装AAD
-> 序列化content
-> 使用当前active DEK加密
-> 事务写入密文和消息元数据
-> 向在线订阅者广播原内存明文
```
#### 历史消息和离线同步
```text
从数据库读取密文
-> 按tenant_id + key_version获取DEK
-> 重建AAD
-> GCM验签并解密
-> JSON反序列化
-> 按原Tinode协议返回
```
任何验签失败均必须当作数据篡改或密钥错误,禁止返回部分内容或尝试忽略错误。
### 5.7 密钥获取和缓存
- IM Server 不得每发送一条消息都跨境请求 HotGo。
- IM Server 按 `tenant_id + key_version` 在内存中缓存已解密 DEK。
- 新消息使用当前 `active` 密钥,历史消息按记录中的 `key_version` 取密钥。
- 密钥变更发布 `message.encryption.key.updated` 配置事件,IM Server 按商户刷新。
- HotGo 短时不可用时继续使用内存密钥。
- IM Server 重启后如无法取得当前密钥,该商户禁止写入新消息,不得降级为明文落库。
### 5.8 密钥轮换
1. Admin Server 生成新 DEK,使用 KEK 加密后以 `pending` 状态入库。
2. 通知 IM Server 拉取并验证新密钥。
3. 验证成功后,在事务中将旧密钥设为 `retired`,新密钥设为 `active`。
4. 新消息立即使用新版本,旧消息仍按原版本解密。
5. 历史转密钥作为单独低速任务执行,不阻塞即时通信。
## 6. Admin Server 改造
### 6.1 内容审核页面
将现有“审核插件”配置补齐为可用功能:
- 阿里云基础配置。
- 真实连通性检查。
- 商户审核开关和场景开关。
- 风险等级与处置动作映射。
- 异常降级策略。
- 配置发布版本和生效时间。
非默认商户显示“未开启内容审核”,不显示阿里云密钥编辑项。
### 6.2 消息加密管理页面
增加平台级“消息加密”页面,只允许最高权限平台管理员使用:
- 查看商户加密状态、当前密钥版本和最后轮换时间。
- 初始化商户密钥。
- 启用/暂停商户消息加密。
- 轮换密钥。
- 查看历史迁移进度。
- 密钥明文、根密钥和 `encrypted_dek` 均不展示。
### 6.3 内部接口
建议增加:
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| GET | `/internal/im-config/tenants/{code}/moderation-policy` | IM Server 拉取商户审核策略 |
| GET | `/internal/im-config/moderation/provider` | IM Server 拉取阿里云配置 |
| GET | `/internal/im-config/tenants/{code}/message-keys/active` | 获取当前消息 DEK |
| GET | `/internal/im-config/tenants/{code}/message-keys/{version}` | 获取指定版本 DEK |
| POST | `/internal/im-config/tenants/{code}/message-keys/ack` | IM Server 确认新密钥可用 |
密钥接口返回的是服务端使用的 DEK,不允许从浏览器或通用 Admin API 调用。接口必须校验服务身份、时间窗、签名、请求 ID 和重放。
### 6.4 后台消息查询
- Admin Server 继续调用 IM Server internal admin-query,不直连 IM 业务库解密。
- IM Server 在商户数据权限通过后批量解密当页消息。
- 每次查看明文记录管理员 ID、商户、IP、查询条件、返回数量、时间和 TraceID。
- 列表只解密当页,禁止一次加载全部历史消息。
## 7. IM Server 改造
### 7.1 新增内部模块
```text
server/moderation/
provider.go 审核Provider接口
policy.go 商户策略和缓存
text.go 文本提取与结果映射
aliyun/text.go TextModerationPlus实现
server/messagecrypto/
cipher.go AES-256-GCM加解密
key_provider.go 密钥获取接口
key_cache.go tenant/version内存缓存
serializer.go content规范序列化
```
不将加密和阿里云调用直接写进 MySQL adapter;adapter 只保存已加密的存储模型和解密所需元数据。
### 7.2 消息发送接入点
在 `Topic.handlePubBroadcast` 中,于 `saveAndBroadcastMessage` 之前执行:
1. 过滤 WebRTC/系统信令等不审核内容。
2. 提取文本。
3. 根据商户策略执行审核。
4. 拦截时通过 Tinode ctrl 回复业务错误,不进入保存和广播。
5. 放行时进入加密保存。
进程内广播使用本次请求已有明文,不为广播执行“加密后立即解密”。
### 7.3 资料修改接入点
用户昵称/签名、群名称/公告、群昵称的 HTTP 和 Tinode meta 修改通道都必须接入同一 moderation service,不能只修改某一个 HTTP API 而留下协议绕过路径。
### 7.4 存储层改造
- `types.Message` 和 `types.MessageRevision` 增加密文及密钥元数据字段。
- MySQL 的普通库和共享库/多商户 adapter 必须同时改造。
- 新建库 schema、启动 ensure 迁移和手工 SQL 脚本必须保持一致。
- Save、GetOne、GetAll、编辑、撤回、删除、历史修订、管理后台查询均需覆盖。
- 解密层应在业务存储服务边界统一执行,避免上层遗漏。
### 7.5 缓存与多 Pod
- 每个 Pod 保持本地密钥和审核策略缓存。
- Kafka 配置事件使所有 Pod 同时失效。
- 事件处理必须幂等,重复消息不得导致重复创建密钥。
- 新旧密钥切换期间,所有 Pod 必须同时具备旧版本解密和新版本加密能力。
## 8. 审核和解密日志
在 IM 业务库增加 `im_content_moderation_logs`:
| 字段 | 说明 |
| --- | --- |
| `id` | 主键 |
| `request_id` | 审核请求 ID |
| `scene` | 昵称、单聊、群聊等 |
| `topic` / `seqid` | 关联消息,拦截时 `seqid` 可为空 |
| `actor_id` | 内容提交人 |
| `provider` | `local` / `aliyun_green` |
| `provider_request_id` | 第三方 RequestID |
| `risk_level` | 统一风险等级 |
| `risk_labels` | 命中标签 JSON |
| `action` | `allow` / `block` / `audit_only` |
| `content_hash` | SHA-256 摘要,不保存明文 |
| `latency_ms` | 审核耗时 |
| `error_code` | 脱敏错误码 |
| `created_at` | 创建时间 |
后台明文查看继续写入统一审计事件,必须区分“列表解密”、“详情解密”和“导出解密”。默认不提供明文批量导出;需要开放时必须增加独立权限和审批。
## 9. 关键词查询限制
消息加密后,MySQL 无法对密文执行原有 `LIKE '%keyword%'`。第一阶段采用:
1. 后台查询必须先提供商户、时间范围,并尽量提供用户或群号。
2. IM Server 先用明文元数据缩小候选集。
3. 对有上限的候选集批量解密,再在内存中匹配关键词。
4. 限制查询最大时间范围和候选消息数,超限返回明确提示。
不建议在第一阶段建设明文分词索引,否则会降低加密保护效果。若后续必须支持全库高性能检索,再单独评审 HMAC 盲索引方案。
## 10. 历史数据迁移
### 10.1 发布顺序
1. 发布 HotGo 表结构、根密钥和密钥管理能力。
2. 为默认商户初始化第 1 版 DEK,但不开启加密。
3. 发布 IM Server 双读兼容版本,验证密钥获取、加解密和内容审核。
4. 开启新消息加密写入,保持旧数据明文可读。
5. 启动历史数据分批迁移。
6. 对比数量、抽样解密和 GCM 验签,完成后清空明文 `content`。
7. 观察期结束后,再决定是否删除原明文字段。
### 10.2 迁移任务要求
- 按主键或 `topic + seqid` 分批,不长时锁表。
- 每批使用独立事务,记录游标、成功数、失败数和最后错误。
- 重复执行必须幂等,`encrypted=1` 的记录直接跳过。
- 先写入密文并验证可解密,再将明文字段置空。
- 失败记录保留明文并进入重试列表,不得丢失原数据。
- 迁移速率可配置,并监控数据库负载和消息延迟。
## 11. 错误码和客户端兼容
客户端不参与加解密,不修改消息协议。新增业务错误建议:
| 错误 | 说明 | 客户端处理 |
| --- | --- | --- |
| `CONTENT_REJECTED` | 内容审核拒绝 | 保留未发送文本并提示修改 |
| `CONTENT_REVIEW_UNAVAILABLE` | 审核不可用且策略要求拦截 | 提示稍后重试 |
| `MESSAGE_ENCRYPTION_UNAVAILABLE` | 未取得加密密钥 | 提示服务暂时不可用 |
| `MESSAGE_DECRYPT_FAILED` | 历史消息验签/解密失败 | 展示“消息暂时无法加载”,不展示乱码 |
不向客户端返回阿里云原始错误、风险标签详情、密钥版本内部信息或解密堆栈。
## 12. 监控指标
### 12.1 内容审核
- 按商户/场景统计请求量、放行数、拦截数、降级数。
- 阿里云耗时 P50/P95/P99。
- 超时率、错误率、熔断状态。
- 各风险等级和标签命中数。
### 12.2 消息加密
- 加密/解密成功数、失败数和耗时。
- 各商户当前密钥版本。
- 密钥获取成功率、缓存命中率和缓存最后刷新时间。
- 明文历史记录剩余数、迁移速率和迁移失败数。
- GCM 验签失败必须产生高优先级告警。
监控标签不得使用消息正文、密文、Nonce、DEK 或 KEK。
## 13. 验收标准
### 13.1 审核验收
- `default` 商户开启后,单聊、群聊、用户昵称、群名称和群昵称均实际调用对应阿里云 Service。
- 其他商户关闭时不调用阿里云。
- 拦截内容不进入 `im_messages` 和 `im_group_messages`,也不向订阅者广播。
- 本地敏感词与阿里云开关可独立启停。
- 阿里云超时严格按 `failure_mode` 处理。
- 配置发布后所有 IM Pod 在规定时间内生效。
### 13.2 加密验收
- 开启商户的新消息在数据库中不存在可读正文。
- 相同内容连续发送两次,Nonce 和密文必须不同。
- 修改密文、Nonce、AAD 关联字段的任意一处后,解密必须失败。
- 在线发送、离线同步、多端漫游、编辑、回复、撤回、删除保持原有业务行为。
- Admin 有权限账号可查看解密明文,无权限账号被拒绝。
- 查看明文必须形成完整审计记录。
- 旧密钥退役后仍可解密旧版本消息,新消息只使用新版本。
- HotGo 暂时断开时,已有内存密钥的 IM Pod 仍可工作;无密钥时绝不允许明文降级落库。
### 13.3 性能验收
- 纯 AES 加密不得成为消息链路主要瓶颈。
- 密钥缓存命中时不发生跨境 HotGo 请求。
- 审核关闭的商户不增加阿里云网络耗时。
- 审核开启时应分别统计本地处理、阿里云调用和消息入库耗时。
- 历史迁移期间即时消息发送和数据库负载不得超过上线阈值。
## 14. 开发分批建议
### 批次一:配置和密钥基础
- Admin 阿里云内容审核配置、商户策略、密钥表和密钥管理页。
- 字典根密钥隔离和专用密钥服务。
- Admin internal config/key API 及 Kafka 失效事件。
### 批次二:IM 文本审核
- 阿里云 `TextModerationPlus` Go 客户端。
- 策略缓存、文本提取、消息和资料修改接入。
- 审核日志、错误码、告警和回归测试。
### 批次三:新消息加密
- AES-256-GCM 模块、密钥缓存和存储模型。
- 新消息加密写入和历史消息解密读取。
- 单聊、群聊、修订、后台查询的一致性验证。
### 批次四:历史迁移和轮换
- 历史明文分批加密。
- 迁移仪表盘、少量失败重试和数据对账。
- 密钥轮换和历史转密钥测试。
- 完成观察后清理明文。
## 15. 开发前必须确认的决策
| 决策 | 文档默认值 |
| --- | --- |
| 阿里云异常时放行还是拦截 | 放行并告警 |
| 是否审核系统消息 | 不审核 |
| 是否加密 `head` | 第一阶段不加密 |
| 第一阶段启用商户 | 仅 `code=default` |
| 是否允许无密钥时明文降级 | 不允许 |
| 后台是否允许明文导出 | 默认不允许 |
| 历史消息是否迁移 | 是,分批迁移 |
| 根密钥位置 | HotGo 内部字典数据,严禁通用接口暴露 |
## 16. 现有实现对照
| 能力 | 现状 | 本次改造 |
| --- | --- | --- |
| 阿里云文本审核 | 文档已人工验证,IM Server 无 Go 调用实现 | 增加 TextModerationPlus 客户端和业务接入 |
| 本地敏感词 | Admin 已有 CRUD,IM Server 未消费 | 作为可选补充审核 |
| 审核配置 | `im_external_config` 已有 `moderation` 占位 | 增加真实检查、商户策略和发布 |
| 配置同步 | 已有 internal config 客户端、Kafka 配置事件和缓存失效 | 复用并扩展新的策略/密钥类型 |
| 消息存储 | `im_messages.content` 和修订表 `content` 为 JSON 明文 | 改为 AES-256-GCM 密文且双读迁移 |
| 群消息派生表 | `im_group_messages.content` 为 JSON 明文 | 去明文副本或同步密文化 |
| 后台消息查询 | IM internal-query 返回库内内容 | 增加权限校验、解密和查看审计 |
@@ -0,0 +1,460 @@
# 鸽达达管理后台改造测试用例
> 依据:`需求文档.md`、`Admin-Server改造文档.md`、`IM-Server改造文档.md`及当前 Admin Server、IM Server 代码。
> 测试环境:`https://admin.118im.com/admin#/dashboard/console`
> 文档日期:2026-09-07
## 1. 测试目标与范围
本测试用于验证鸽达达管理后台改造后的页面操作、Admin Server 接口、IM Server 内部查询与命令执行是否一致,并重点验证以下闭环:
1. 后台查询条件、表格字段、详情、导出及权限控制正确。
2. Admin Server 能正确访问 IM Server,真实展示概览、消息和登录审计数据。
3. 消息隐藏、删除经 Kafka 下发后由 IM Server 幂等执行并记录结果。
4. 全局禁止加好友发布后,移动端所有加好友入口均受后端控制,不能绕过。
5. 宵禁发布后,命中规则的单聊/私聊不能发送消息;配置群聊规则时群聊也必须受限。
6. 登录成功、失败及退出均产生安全审计数据,且账号、口令和验证码等敏感信息不泄露。
### 1.1 本轮交付范围
| 页面/能力 | 本轮状态 | 测试结论口径 |
|---|---|---|
| 3.1 数据概览 | 已改造 | 本轮必测,需联调 Admin 与 IM |
| 3.3 用户分组 | Admin 已改造为用户策略分组 | 后台 CRUD 本轮必测;IM 策略消费待补齐 |
| 3.7 单聊消息 | 已改造 | 本轮必测,需验证 Kafka 处置闭环 |
| 3.8 群聊消息 | 已改造 | 本轮必测,需验证 Kafka 处置闭环 |
| 3.9 敏感词库 | Admin 管理端已改造 | 后台 CRUD 本轮必测;IM 运行时拦截待补齐 |
| 3.16 用户登录历史 | 已改造 | 本轮必测,数据来自 IM Server |
| 3.18 登录失败日志 | 已改造 | 本轮必测,包含加入 IP 黑名单、封禁用户 |
| 3.20 安全策略 | Admin 管理与发布已改造,IM 执行端未完成 | P0 验收目标;当前不得判定端到端通过 |
| 其余 3.2、3.4-3.6、3.10-3.15、3.17、3.19 | 原系统能力/后续改造 | 执行基础回归,不作为本轮新增功能全部交付的判定依据 |
### 1.2 代码核查结论
本节是对当前代码的静态核查结论,不等同于测试环境已经执行通过:
| 服务 | 已实现内容 | 当前缺口/测试关注点 |
|---|---|---|
| Admin Server | 数据概览、单聊/群聊消息查询与处置、用户登录历史、登录失败日志;用户策略分组、敏感词、安全策略和 IP 规则的管理页面、接口、权限与数据表 | 对 IM 的依赖异常、异步命令最终状态、租户隔离仍需按本文实测 |
| IM Server | 概览/趋势、单聊/群聊消息、登录成功/失败日志的 Internal Query;`message.hide`、`message.delete` 消费、幂等处置及动作落库;真实登录成功、失败和退出审计 | 未找到 `security.policy.updated`、用户策略分组、敏感词策略的运行时消费;未找到全局禁加好友、资料隐藏、二维码拦截和消息宵禁在 IM 请求链路的执行代码 |
| 联调结论 | 消息治理、概览和登录审计具备端到端测试基础 | 安全策略页面“发布成功”目前只能证明 Admin 保存并发出事件,不能证明移动端已生效;`FRIEND-*`、`CURFEW-*`、敏感词运行时用例预计失败或阻塞,须补齐 IM 实现后回归 |
因此,测试报告必须分别记录“Admin 配置成功”“事件发布成功”“IM 接收成功”“移动端行为生效”四个结果,禁止合并为一个“发布成功”。
### 1.3 优先级与结果
| 标识 | 说明 |
|---|---|
| P0 | 核心链路或安全控制,失败时禁止上线 |
| P1 | 主要页面功能,失败时原则上阻断上线 |
| P2 | 辅助功能、兼容性或易用性问题 |
| 通过 | 实际结果与预期完全一致,接口、数据库及异步结果无异常 |
| 失败 | 页面、接口、数据库或 IM 最终行为任一不符合预期 |
| 阻塞 | 环境、账号、数据或依赖不可用,无法执行 |
## 2. 测试准备
### 2.1 账号、权限与数据
准备以下数据,所有测试数据使用唯一前缀 `QA_YYYYMMDD_`:
| 数据 | 用途 |
|---|---|
| 超级管理员 A | 验证全部菜单与操作权限 |
| 只读管理员 B | 仅授予列表、详情权限,验证写按钮隐藏和接口拒绝 |
| 租户管理员 C | 验证租户隔离,不得查看或操作其他租户数据 |
| 移动端用户 U1、U2、U3 | U1/U2 为普通用户,U3 用作好友或宵禁白名单 |
| 群组 G1 | U1、U2、U3 入群,用于群消息及群成员入口测试 |
| 消息样本 | 文本、图片、视频、语音、文件、名片、系统消息各至少 1 条 |
| 登录样本 | 密码成功、密码错误、账号不存在、账号禁用、退出各至少 1 条 |
执行前确认:
- Admin Server 的 IM Internal API 地址与共享密钥配置正确。
- Admin Server 和 IM Server 的 Kafka broker、topic 和鉴权配置一致。
- Admin 库已存在策略、敏感词、策略分组及消息命令相关表。
- IM 库已存在 `im_user_login_logs`、`im_login_failure_logs`、`im_message_admin_actions`。
- 浏览器清理旧缓存后重新登录,确认当前构建版本与待测提交一致。
### 2.2 通用检查
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| COM-001 | P0 | 使用超级管理员登录并展开 IM 管理菜单 | 已交付的 8 个功能域可见,页面可打开,无 404、白屏和控制台致命错误 |
| COM-002 | P0 | 使用只读管理员打开各页面 | 可查询和查看;新增、编辑、删除、发布、隐藏、封禁等无权限按钮不可见或禁用;直接请求写接口返回无权限 |
| COM-003 | P0 | 使用租户管理员查询列表、详情和导出 | 后端强制按当前租户过滤,修改 `tenantId` 不能越权读取或操作其他租户数据 |
| COM-004 | P1 | 对列表执行查询、重置、翻页、切换每页条数 | 查询条件生效;重置恢复默认值;总数和分页正确;每页超过 200 时接口明确报错 |
| COM-005 | P1 | 查询无匹配数据、模拟 IM 超时或不可用 | 空数据展示为空列表而非报错;依赖异常有明确提示,不把历史缓存伪装成实时成功 |
| COM-006 | P1 | 连续快速点击保存、发布、删除等按钮 | 提交期间按钮有加载/防重复状态,不产生重复记录或重复处置 |
| COM-007 | P1 | 检查新增写操作后的操作日志 | 操作者、租户、目标对象、动作、时间、结果和原因可追溯,不记录密码、验证码、Token 或完整敏感账号 |
## 3. 页面测试用例
### 3.1 数据概览
当前按钮和字段:`刷新`,时间范围 `近7天/近14天/近30天`;指标包含总用户数、活跃用户、总群组数、总会话数、总消息数、今日消息;趋势表包含日期、新增用户、新增消息。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| DASH-001 | P0 | 打开数据概览 | 调用 `/imDashboard/overview` 和 `/imDashboard/trends`;六项指标展示真实数字,不显示 `undefined`、`NaN` 或静态占位值 |
| DASH-002 | P0 | 记录 IM 库统计结果后刷新页面 | 总用户、活跃用户、群组、会话、消息及今日消息与 IM Server 同口径统计一致 |
| DASH-003 | P1 | 分别选择近 7、14、30 天 | 趋势行数和日期区间正确,日期连续;切换后重新请求,新增用户和新增消息按日聚合 |
| DASH-004 | P1 | 在 IM 端新增用户并发送一条消息,再点刷新 | 对应总量和当日趋势增加;刷新按钮显示加载状态且不会重复并发请求 |
| DASH-005 | P1 | 检查跨天边界和无数据日期 | 按 `Asia/Shanghai` 自然日统计;无数据日期补 0,不遗漏日期 |
| DASH-006 | P0 | 使用租户管理员查看概览 | 只统计本租户数据;不能通过修改请求参数获得全局或其他租户数据 |
### 3.2 用户管理
本页属于既有能力回归。重点回归查询、详情、状态修改、重置密码、IP 白名单、强制下线、注销、批量封禁、按 IP 封禁和导入。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| USER-001 | P1 | 按用户 ID、用户名、昵称、手机号、状态和时间查询 | 条件可组合,表格字段与详情一致,手机号等敏感字段按权限脱敏 |
| USER-002 | P1 | 新增或编辑用户,切换启用/禁用 | 校验必填项和唯一性;状态写入成功,禁用用户不能继续登录 |
| USER-003 | P1 | 执行重置密码、强制下线、注销 | 均需二次确认;成功后客户端会话失效;操作日志记录动作但不记录明文密码 |
| USER-004 | P1 | 执行批量封禁及按 IP 封禁 | 仅命中目标租户和目标用户;返回成功、跳过、失败数量,重复提交保持幂等 |
### 3.3 用户分组
当前页面为“用户策略分组”,按钮包括新增/编辑、成员、启用/禁用、删除;成员弹窗支持添加成员、移除成员。表格重点字段:分组编码、分组名称、优先级、状态、版本、成员数、备注、更新时间。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| UPG-001 | P1 | 按租户、编码/名称关键字、状态查询并重置 | 条件生效;状态 `1=启用、2=禁用` 显示字典标签;成员数与成员关系表一致 |
| UPG-002 | P1 | 新增分组,填写合法编码、名称、优先级、策略 JSON、IP 白/黑名单 | 保存成功并生成版本;编码以字母开头且仅含字母、数字、下划线、中划线,长度 2-64 |
| UPG-003 | P1 | 输入数字开头、空名称、非法 JSON、超过 128 字名称 | 前后端拒绝保存并给出明确中文错误,数据库不产生脏数据 |
| UPG-004 | P1 | 编辑分组策略后保存 | 原记录更新,版本按实现规则变化;详情与列表一致;其他租户同编码记录不受影响 |
| UPG-005 | P1 | 点击状态开关切换启用/禁用 | 调用 `/imUserPolicyGroup/status`;页面状态即时刷新,非法状态值被后端拒绝 |
| UPG-006 | P1 | 打开成员列表,添加 U1,设置有效开始/结束时间 | 成员成功加入,成员数 +1;结束时间早于开始时间时拒绝保存 |
| UPG-007 | P1 | 重复添加同一用户,然后移除成员 | 不产生重复有效关系;移除后成员列表和成员数同步更新 |
| UPG-008 | P1 | 删除含成员的分组 | 显示二次确认;按业务约束拒绝删除或级联解除关系,不能遗留指向不存在分组的成员关系 |
### 3.4 群组管理
本页属于既有能力回归,验证查询、详情、封禁/解封、解散、成员、文件和快速建群。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| GROUP-001 | P1 | 按群 ID、群号、群名、群主、状态查询 | 列表与 IM 群数据一致,分页及租户隔离正确 |
| GROUP-002 | P1 | 封禁 G1 后尝试群内发消息,再解封 | 封禁后发送被 IM Server 拒绝;解封后恢复;后台和客户端状态一致 |
| GROUP-003 | P1 | 查看成员、文件并删除一个群文件 | 只展示 G1 数据;删除需确认且客户端不可再访问已删除文件 |
| GROUP-004 | P1 | 快速创建群组并解散 | 成员、群主校验正确;解散后群不可继续使用,操作有审计记录 |
### 3.5 群成员管理
本页属于既有能力回归,重点验证踢出、禁言、解除禁言,以及全局禁加好友时的群成员隐私联动。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| GMEM-001 | P1 | 查询 G1 成员并按用户筛选 | 角色、入群时间、禁言状态正确,只返回该群成员 |
| GMEM-002 | P1 | 对 U2 执行禁言、解除禁言、踢出 | 对应客户端行为立即生效;群主/管理员等受保护角色遵守业务限制 |
| GMEM-003 | P0 | 发布“全局禁止加好友”策略后,U1 在群成员列表点击 U2 | 不显示可识别的个人详情或个人名片,不显示加好友入口;抓包直接访问资料/加好友接口也被后端拒绝 |
### 3.6 邀请码管理
本页属于既有能力回归。邀请码创建、删除、使用记录必须与全局好友开关联动。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| INV-001 | P1 | 创建邀请码并设置有效期、次数、状态 | 校验正确,创建成功后可查询,过期或耗尽后不可继续使用 |
| INV-002 | P1 | 查看使用记录并删除未使用邀请码 | 使用人、时间、状态正确;删除需确认,已删除邀请码失效 |
| INV-003 | P0 | 全局禁止加好友期间通过邀请码注册或自动加好友 | 注册能力按私域设置执行,但 `invite_auto_add` 入口必须被后端阻断,不能自动建立好友关系 |
### 3.7 单聊消息
筛选项:租户、会话 Topic、发送用户 ID、接收用户 ID、内容类型、消息状态、内容关键词、发送时间。表格字段:Topic、消息序号、发送人、接收人、类型、内容摘要、状态、发送时间;按钮:详情、隐藏、删除、导出。
内容类型枚举:`text` 文本、`image` 图片、`video` 视频、`audio/voice` 语音、`file` 文件、`card` 名片、`system` 系统。状态枚举:`normal` 正常、`deleted` 后台删除、`recalled` 用户撤回、`blocked` 风控拦截、`hidden` 已隐藏。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| PM-001 | P0 | 打开页面并点击查询 | `/imPrivateMessage/list` 返回真实 IM 消息;单聊双方、Topic、Seq、类型、摘要、状态和时间与 IM 库一致 |
| PM-002 | P1 | 组合发送人、接收人、类型、状态、关键词和时间查询 | 所有条件正确下传并组合生效;非法类型、非法状态、每页大于 200 被后端拒绝 |
| PM-003 | P1 | 点击详情 | `/imPrivateMessage/view` 返回消息头、完整内容、元数据、回复序号、@用户等;空 Topic 或 `seqId<=0` 被拒绝 |
| PM-004 | P0 | 点击隐藏,输入处置原因并确认 | 原因必填且不超过 500 字;Admin 生成唯一 command/event,返回待处理状态,Kafka 发布 `message.hide` |
| PM-005 | P0 | 等待隐藏命令完成后刷新并在移动端查看 | Admin 命令状态变为成功;IM `im_message_admin_actions` 有一条成功记录;列表状态为 `hidden`,普通客户端不再展示该消息 |
| PM-006 | P0 | 点击删除,确认后等待执行 | Kafka 发布 `message.delete`;IM 幂等落库并清除相关缓存;列表状态为 `deleted`,详情/客户端不能再获得可展示原文 |
| PM-007 | P0 | 使用同一 command/event 重放隐藏或删除命令 | 不重复执行、不新增重复有效记录,最终状态保持一致,并可追踪重复消费结果 |
| PM-008 | P1 | Kafka 不可用时执行隐藏/删除 | 页面明确提示失败或命令进入失败/待重试状态,不显示为已成功;恢复后可按设计重试 |
| PM-009 | P1 | 按当前筛选条件导出 | 导出字段含 Topic、Seq、双方 ID、类型、摘要、状态、发送时间;数据范围与筛选及租户权限一致 |
### 3.8 群聊消息
筛选项:租户、群 Topic、发送用户 ID、内容类型、消息状态、关键词、发送时间。表格除公共消息字段外还应展示群名称、群号;按钮与单聊一致。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| GM-001 | P0 | 按 G1 Topic 查询 | `/imGroupMessage/list` 仅返回群消息,群名称、群号、发送人、Seq、类型、摘要和状态正确 |
| GM-002 | P1 | 按发送人、类型、状态、关键词、时间组合查询并查看详情 | 条件准确,详情完整;不能用单聊 Topic 越界取得群消息 |
| GM-003 | P0 | 隐藏一条群消息并在多个群成员客户端刷新 | `message.hide` 执行成功;所有普通成员均不可见,后台状态和 IM 动作记录一致 |
| GM-004 | P0 | 删除一条群消息 | `message.delete` 执行成功;群消息缓存被清除,各客户端不能继续通过历史缓存看到原内容 |
| GM-005 | P1 | 导出群消息 | 导出包含群名、Topic、Seq、发送人、类型、摘要、状态、时间,范围与查询一致 |
| GM-006 | P0 | 重放相同命令、乱序投递隐藏与删除命令 | IM Server 幂等处理;删除是最终不可见状态,不因迟到的隐藏命令恢复内容 |
### 3.9 敏感词库
按钮:新增、编辑、启用/禁用、删除、批量导入、分组管理。筛选字段:租户、分组、敏感词、匹配类型、命中动作、风险等级、状态。
匹配枚举:`exact` 精确、`contains` 包含、`regex` 正则、`pinyin` 拼音。动作枚举:`replace` 替换、`block` 拦截、`audit_only` 仅审计、`manual_review` 人工审核。风险等级:`low/medium/high`。场景:单聊、群聊、用户资料、群资料、反馈、举报。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| SW-001 | P1 | 新建敏感词分组 | 编码格式、名称 1-128 字和状态校验正确;同租户编码保持唯一 |
| SW-002 | P1 | 新增敏感词并逐项选择匹配方式、动作、风险、场景 | 保存后列表字典显示正确;敏感词长度 1-255;至少选择一个生效场景 |
| SW-003 | P1 | 动作选择“替换”但不填替换文本 | 前后端均拒绝并提示“替换动作必须填写替换文本” |
| SW-004 | P1 | 输入非法匹配类型、动作、风险、状态或无效正则 | 后端拒绝;无效正则不得发布到 IM 运行时 |
| SW-005 | P1 | 切换启用/禁用并发送命中消息 | 禁用后不执行该词规则;启用后按所选场景和动作处理,后台状态与 IM 行为一致 |
| SW-006 | P1 | 批量导入含空行、重复词和已存在词的数据 | 单次仅允许 1-5000 条;返回 `inserted` 和 `skipped`,重复数据不产生多条记录 |
| SW-007 | P1 | 删除敏感词后再次发送命中内容 | 删除需确认;运行时规则及时失效,且操作有审计记录 |
### 3.10 文件管理
本页属于既有能力回归。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| FILE-001 | P1 | 按上传人、文件类型、大小、来源、审核状态、时间查询 | 表格元数据、下载地址、引用对象与存储记录一致,租户隔离正确 |
| FILE-002 | P1 | 预览、下载、删除文件 | 权限校验正确;删除需确认,引用中的已删除文件不能继续匿名访问 |
| FILE-003 | P1 | 执行审核通过/拒绝及批量处置 | 状态流转正确,拒绝原因必填,相关消息或群文件同步更新 |
### 3.11 用户反馈
本页属于既有能力回归。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| FB-001 | P1 | 按反馈类型、状态、优先级、用户和时间查询 | 查询、分页、详情及附件展示正确 |
| FB-002 | P1 | 修改处理状态、优先级和回复内容 | 状态流转符合约束,处理人和处理时间自动记录 |
| FB-003 | P2 | 批量处理并导出 | 成功/失败数量准确,导出范围与当前筛选一致 |
### 3.12 操作日志
本页属于既有能力回归,也是所有后台写操作的审计入口。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| OPLOG-001 | P0 | 完成策略发布、消息删除、用户封禁后查询日志 | 三类操作均可按模块、操作者、目标和时间检索,结果、原因、请求 ID 完整 |
| OPLOG-002 | P0 | 检查请求参数与响应摘要 | 密码、验证码、Token、共享密钥、完整手机号/邮箱等不以明文出现 |
| OPLOG-003 | P1 | 只读管理员尝试越权写接口 | 记录拒绝结果及必要上下文,不记录敏感正文,不改变业务数据 |
### 3.13 用户账号审计
本页属于既有能力回归。动作至少覆盖修改资料、修改密码、注销、管理员改用户名/状态、重置密码、更新安全权限、强制下线、强制注销、调整策略分组、批量封禁、按 IP 封禁。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| UAUD-001 | P1 | 分别触发用户自助和管理员账号变更 | 动作枚举显示中文,操作者类型、目标用户、变更前后摘要和时间正确 |
| UAUD-002 | P0 | 执行重置密码并查看审计详情 | 仅记录“已重置”等安全摘要,绝不保存新旧明文密码 |
| UAUD-003 | P1 | 调整 U1 策略分组 | 产生 `assign_policy_group` 审计,旧分组、新分组及管理员可追溯 |
### 3.14 群事件审计
本页属于既有能力回归。事件包括建群、加入/退出/踢出、禁言/解禁、转让群主、增删管理员、改群名、封禁/解封和解散。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| GAUD-001 | P1 | 在 G1 触发踢人、禁言、改群名 | 对应事件各产生一条审计,操作人、目标人、群、时间和原因正确 |
| GAUD-002 | P1 | 系统执行群封禁或解散 | 操作人显示系统或实际管理员,事件类型与最终群状态一致 |
### 3.15 管理员登录历史
本页属于 HotGo 既有能力回归。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| ALOGIN-001 | P1 | 管理员成功登录、失败登录、退出后查询 | 账号、IP、浏览器、系统、结果和时间正确;失败原因不泄露密码 |
| ALOGIN-002 | P1 | 使用日期、账号、IP、状态筛选并导出 | 列表和导出范围一致,普通管理员不能查看超出权限的数据 |
### 3.16 用户登录历史
筛选项:租户、用户 ID、脱敏账号、登录方式、事件类型、客户端平台、IP、登录时间。表格字段:ID、用户、脱敏账号、登录方式、事件、客户端、版本、IP、地区、风险等级、登录时间、退出时间。
登录方式:`password` 密码、`sms_code` 短信验证码、`sso` 单点登录、`qr_code` 二维码、`token_refresh` 令牌刷新。事件:`login` 登录、`refresh` 刷新会话、`logout` 主动退出、`kicked` 被踢下线、`session_expired` 会话过期。平台:iOS、Android、Web、桌面端、未知。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| ULOGIN-001 | P0 | U1 密码登录成功后在后台查询 | IM `im_user_login_logs` 新增成功记录;后台展示用户、脱敏账号、会话、设备、平台、IP、请求 ID 和登录时间 |
| ULOGIN-002 | P0 | U1 主动退出后刷新列表 | 对应会话 `logout_at` 被更新或产生退出事件;退出时间不早于登录时间 |
| ULOGIN-003 | P1 | 分别模拟刷新会话、被踢、会话过期 | 事件字典和发生时间正确,同一 session 可串联追踪 |
| ULOGIN-004 | P1 | 按全部筛选项组合查询 | Admin 参数正确透传到 IM Internal API;结果按登录时间倒序,分页不重复不漏项 |
| ULOGIN-005 | P0 | 检查列表、详情、Admin/IM 日志及数据库 | 只保存账号掩码和不可逆哈希;密码、短信验证码、Token、Cookie 不得出现 |
| ULOGIN-006 | P1 | 导出当前筛选结果 | 含 ID、用户、脱敏账号、事件、平台、IP、地区、风险、原因、时间;租户隔离和脱敏规则不因导出而放宽 |
### 3.17 IP 账号统计
本页属于后续改造/回归能力。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| IPSTAT-001 | P1 | 按 IP、账号数、风险和时间查询 | 同一 IP 关联账号数、成功/失败次数和最近时间统计准确 |
| IPSTAT-002 | P1 | 查看关联用户并按 IP 封禁 | 关联列表受租户隔离;封禁需确认并记录命中用户数、成功数和失败数 |
### 3.18 登录失败日志
筛选项:租户、用户 ID、脱敏账号、失败类型、平台、IP、尝试时间。表格字段:ID、用户、脱敏账号、失败类型、原因、IP、地区、客户端、版本、风险等级、风控动作、尝试时间;按钮:详情、加入 IP 黑名单、封禁用户、导出。
失败类型:`password_error` 密码错误、`code_error` 验证码错误、`account_not_found` 账号不存在、`account_disabled` 账号已禁用、`rate_limited` 访问频率受限、`risk_blocked` 风控拦截。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| LFAIL-001 | P0 | 使用错误密码登录 U1 | IM `im_login_failure_logs` 新增 `password_error`;后台展示脱敏账号、IP、设备、平台、风险及尝试时间 |
| LFAIL-002 | P0 | 分别触发验证码错误、账号不存在、账号禁用、限频和风控拦截 | 失败类型、原因码、详情、风险动作正确;未知账号场景不泄露账号是否真实存在 |
| LFAIL-003 | P1 | 点击详情 | 根据日志 ID 返回完整安全上下文;`id<=0` 或跨租户 ID 被拒绝 |
| LFAIL-004 | P0 | 点击“加入 IP 黑名单”并确认 | IP 必填;在安全策略 IP 黑名单生成规则,不重复插入相同有效规则,后续登录按场景被阻断 |
| LFAIL-005 | P0 | 对有关联用户的记录点击“封禁用户”,填写原因 | 用户 ID 和原因必填;用户状态变为禁用,已有会话按设计失效,账号审计和操作日志可追溯 |
| LFAIL-006 | P1 | 对账号不存在、无关联用户的记录点击封禁 | 按钮不可用或接口明确提示无法关联用户,不得误封其他用户 |
| LFAIL-007 | P1 | 导出失败日志 | 结果与筛选一致,账号继续脱敏,不包含提交的密码、验证码和完整鉴权数据 |
### 3.19 私域设置
本页属于既有/后续能力回归,重点验证注册与验证码开关不绕过安全策略。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| TENANT-001 | P1 | 修改短信验证码、邀请码注册等开关并保存 | 保存后重新进入值保持一致,移动端登录/注册入口按租户配置变化 |
| TENANT-002 | P0 | 关闭短信验证码并尝试短信登录、忘记密码、验证码注册 | 后端拒绝被关闭的流程,不能仅靠隐藏移动端按钮 |
| TENANT-003 | P0 | 全局禁加好友期间通过注册流程触发自动加好友 | 注册成功与否按私域设置判断,但任何自动加好友均被安全策略统一拒绝 |
### 3.20 安全策略
页面包含“策略版本”和“IP 黑白名单”两个页签。策略按钮:新建草稿、编辑/查看、发布、恢复默认;策略内含好友控制、消息宵禁和其他 JSON 策略。IP 规则按钮:新增、编辑、删除。
发布状态:`draft` 草稿、`published` 已发布、`archived` 已归档、`failed` 发布失败。IP 规则类型:白名单/黑名单;动作:`allow` 放行、`challenge` 二次验证、`block` 阻断、`audit_only` 仅审计;场景:登录、注册、发消息、上传文件;风险:低、中、高。
#### 3.20.1 策略版本与发布
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| SEC-001 | P0 | 点击新建草稿,填写策略名称并保存 | `/imSecurityPolicy/save` 创建 `draft`;只有草稿可编辑,已发布版本只读 |
| SEC-002 | P0 | 修改草稿但不发布,在移动端验证旧策略 | 草稿不影响 IM 运行时,移动端继续执行最后一个已发布版本 |
| SEC-003 | P0 | 点击发布并确认 | 发布成功后当前版本为 `published`,原已发布版本归档;IM Server 收到策略更新并按新版本执行 |
| SEC-004 | P0 | 模拟 Kafka/IM 策略应用失败 | 不得提示完全成功;版本进入失败或可重试状态,运行时继续使用上一个有效版本 |
| SEC-005 | P1 | 点击恢复默认 | 仅创建一个默认草稿,不直接发布、不改变当前生效策略 |
| SEC-006 | P1 | 输入非法 JSON、空策略名或超过 500 字发布原因 | 前后端拒绝提交并明确指出错误字段 |
#### 3.20.2 全局禁止加好友
关闭“允许加好友”时,后端必须强制设置:隐藏个人信息、禁止二维码、禁止群成员入口,并封锁 `search`、`profile_card`、`group_member`、`qrcode`、`invite_auto_add`、`recommendation` 六类入口。该限制必须由后端执行,移动端隐藏按钮不能作为唯一控制。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| FRIEND-001 | P0 | 关闭“允许加好友”并观察表单 | 隐藏资料、禁止二维码、禁止群成员入口自动开启且不可取消;六类入口全部选中 |
| FRIEND-002 | P0 | 仅保存草稿,不发布;U1 从用户搜索添加 U2 | 仍按旧策略执行,证明草稿不直接生效 |
| FRIEND-003 | P0 | 发布禁加好友策略;U1 从用户搜索添加 U2 | 页面不展示添加入口或提示“当前时段暂不允许添加好友”;直接调用加好友接口同样被拒绝 |
| FRIEND-004 | P0 | U1 打开 U2 个人名片并尝试添加 | 个人资料按策略隐藏,不展示可用个人名片及加好友入口;直接请求资料/好友接口不可绕过 |
| FRIEND-005 | P0 | U1 从 G1 群成员列表点击 U2 | 不显示 U2 可识别的个人信息、个人名片和加好友入口 |
| FRIEND-006 | P0 | U1 扫描 U2 二维码并构造二维码加好友请求 | 二维码入口不可用,后端拒绝建立好友关系 |
| FRIEND-007 | P0 | 使用邀请码注册并触发自动添加,再查看推荐联系人 | `invite_auto_add` 和 `recommendation` 均被阻断,不产生好友关系 |
| FRIEND-008 | P0 | 修改客户端请求绕过 UI、伪造入口类型或不传入口类型 | 后端按全局开关统一拒绝,不能因缺失/伪造入口字段放行 |
| FRIEND-009 | P0 | 将 U3 配置为例外用户(若本版本支持)并重复六入口测试 | 仅 U3 按明确白名单规则放行,U1/U2 仍被阻断;未实现例外时页面和接口不得暗示支持 |
| FRIEND-010 | P0 | 重新开启并发布,再从六入口验证 | 限制解除,资料展示及加好友恢复为常规权限;已被拒绝的历史请求不会自动补建好友关系 |
#### 3.20.3 消息宵禁
规则字段:规则编码、名称、日期类型、作用范围、开始/结束时间、时区、状态、提示语、白名单用户 ID;支持每天、工作日、周末、自定义日期,范围为单聊/私聊或群聊。默认时区为 `Asia/Shanghai`,支持跨午夜时间段。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| CURFEW-001 | P0 | 开启宵禁但不添加规则并保存 | 后端拒绝;开启时至少存在一条规则,且至少一条启用的单聊/私聊规则 |
| CURFEW-002 | P0 | 新增工作日 18:00-09:00、范围单聊、状态启用的规则并发布 | 保存和发布成功,规则编码合法且唯一,IM Server 使用 `Asia/Shanghai` 解释跨午夜区间 |
| CURFEW-003 | P0 | 命中宵禁时段,U1 向 U2 发送文本、图片、语音、文件和名片 | 所有单聊/私聊消息均由后端拒绝,不写入可投递消息;客户端显示配置的宵禁提示语 |
| CURFEW-004 | P0 | 在开始时刻前 1 秒、开始时刻、结束时刻前 1 秒、结束时刻发送 | 边界按统一规则执行;开始时刻进入限制,结束时刻解除,Admin 与 IM 时间口径一致 |
| CURFEW-005 | P0 | 分别验证每天、工作日、周末、自定义日期 | 仅命中日期生效;自定义日期为空或格式非法时拒绝保存 |
| CURFEW-006 | P0 | 将 U3 加入白名单,在命中时段由 U3 与 U1 发消息 | 白名单方向规则符合产品定义且保持一致;非白名单 U1/U2 仍被拒绝 |
| CURFEW-007 | P0 | 增加群聊范围规则并发布,在 G1 发消息 | 命中时群消息被后端拒绝;只有单聊规则时群聊不受影响 |
| CURFEW-008 | P0 | 禁用规则或关闭宵禁并发布 | 消息恢复发送;旧策略缓存及时失效,不需重启 IM Server |
| CURFEW-009 | P0 | 仅保存新的时间规则但不发布 | IM Server 继续执行最后已发布规则,不能提前生效 |
| CURFEW-010 | P0 | 客户端修改本地时间、时区或直接调用发消息接口 | 后端以策略时区和服务器可信时间判定,无法绕过宵禁 |
#### 3.20.4 IP 黑白名单
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| IPRULE-001 | P1 | 新增黑名单规则,填写单 IP、阻断、登录场景、高风险 | 保存成功,列表字段及字典显示正确;登录请求从该 IP 发起时被阻断 |
| IPRULE-002 | P1 | 新增 CIDR 白名单并选择多个场景 | CIDR 校验正确,命中范围内 IP 按白名单优先级执行,其他场景不受影响 |
| IPRULE-003 | P1 | 输入非法 IP/CIDR、空场景、非法动作/风险/状态 | 后端拒绝且不产生记录 |
| IPRULE-004 | P1 | 编辑规则后重新触发对应场景 | 新规则生效,旧匹配缓存失效;详情和列表一致 |
| IPRULE-005 | P1 | 删除规则并确认 | 记录删除且运行时规则失效;取消确认不产生变化 |
| IPRULE-006 | P0 | 从登录失败日志加入同一 IP 黑名单 | 安全策略页可查询到规则;重复操作不产生冲突规则,审计来源可追踪 |
## 4. Admin Server 与 IM Server 联调核验
### 4.1 接口映射
| 后台操作 | Admin Server | IM Server Internal / Kafka |
|---|---|---|
| 概览指标 | `GET /imDashboard/overview` | `GET /internal/admin-query/tenants/{tenant}/dashboard/overview` |
| 趋势数据 | `GET /imDashboard/trends` | `GET /internal/admin-query/tenants/{tenant}/dashboard/trends` |
| 单聊列表 | `GET /imPrivateMessage/list` | `GET /internal/admin-query/tenants/{tenant}/private-messages` |
| 群聊列表 | `GET /imGroupMessage/list` | `GET /internal/admin-query/tenants/{tenant}/group-messages` |
| 消息详情 | `GET /imPrivateMessage/view` 或 `/imGroupMessage/view` | `GET /internal/admin-query/tenants/{tenant}/messages/{topic}/{seq}` |
| 隐藏/删除消息 | `POST .../hide`、`POST .../delete` | Kafka `message.hide`、`message.delete` |
| 用户登录历史 | `GET /imUserLoginHistory/list` | `GET /internal/admin-query/tenants/{tenant}/user-login-logs` |
| 登录失败列表/详情 | `GET /imLoginFailureLog/list|view` | `GET /internal/admin-query/tenants/{tenant}/login-failure-logs[/{id}]` |
### 4.2 数据库抽查
以下 SQL 仅用于测试环境核验,查询条件必须带测试租户或测试数据标识:
```sql
-- IM 登录成功/退出审计
SELECT id, tenant_id, user_id, account_mask, login_type, event_type,
session_id, platform, ip, risk_level, login_at, logout_at
FROM im_user_login_logs
WHERE tenant_id = ? AND user_id = ?
ORDER BY id DESC LIMIT 20;
-- IM 登录失败审计
SELECT id, tenant_id, user_id, account_mask, failure_type, reason,
ip, platform, risk_level, risk_action, attempt_at
FROM im_login_failure_logs
WHERE tenant_id = ? AND ip = ?
ORDER BY id DESC LIMIT 20;
-- IM 消息后台处置结果
SELECT tenant_id, command_id, event_id, action, topic, seq_id,
status, reason, created_at, updated_at
FROM im_message_admin_actions
WHERE tenant_id = ? AND topic = ? AND seq_id = ?
ORDER BY id DESC;
```
核验要求:同一幂等键不得产生多条有效执行;失败必须保留错误状态和可追踪信息;表中不得出现密码、验证码、Token 或完整认证请求。
## 5. 非功能与异常测试
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| NFT-001 | P1 | 列表准备 10 万级数据并执行常用筛选 | 首屏和翻页在项目约定时间内返回,无全表扫描导致的持续高负载 |
| NFT-002 | P1 | 并发 20 次概览、消息、登录审计查询 | 服务稳定,连接池无耗尽,租户数据不串读 |
| NFT-003 | P0 | 伪造 Internal API 请求、错误共享密钥或重放过期请求 | IM Server 拒绝未授权请求并记录安全日志,不返回业务数据 |
| NFT-004 | P0 | 在 Topic、关键词、原因、备注中输入 SQL/XSS 字符串 | 使用参数化查询;页面转义展示,不执行脚本,不污染导出文件 |
| NFT-005 | P1 | Admin 与 IM 短时网络中断后恢复 | 查询可恢复;异步命令状态可追踪,不丢命令、不重复执行 |
| NFT-006 | P1 | 检查 PC 常用分辨率及窄屏 | 搜索区、表格、弹窗和操作列无重叠;横向滚动可用,按钮文本不被截断为不可辨识状态 |
## 6. 上线验收门槛
满足以下条件方可通过本轮验收:
1. 所有 P0 用例通过,尤其是 `FRIEND-*`、`CURFEW-*`、消息处置、登录审计和租户隔离。
2. P1 无阻断主流程的问题;遗留问题有负责人、修复版本和回归日期。
3. Admin Server、IM Server、Kafka 和两侧数据库结果一致,不以页面提示代替最终状态核验。
4. 全局禁加好友和宵禁均通过“直接调用后端接口”的绕过测试。
5. 数据库、应用日志、操作日志和导出文件均通过敏感信息检查。
6. 其余未纳入本轮改造的页面必须完成基本回归,但不应据此宣称 20 个页面已全部完成重构。
## 7. 执行记录
| 字段 | 内容 |
|---|---|
| Admin Server 版本/提交 | `16cc456`(功能提交:`bb87d01`) |
| IM Server 版本/提交 | `b92154b`(功能提交:`6b20a63`) |
| 前端版本/构建时间 | |
| 测试环境 | `https://admin.118im.com` |
| 测试租户 | |
| 测试人员 | |
| 执行日期 | |
| P0 通过/失败/阻塞 | |
| P1 通过/失败/阻塞 | |
| 缺陷链接 | |
| 最终结论 | 通过 / 有条件通过 / 不通过 |
@@ -0,0 +1,353 @@
# 鸽达达管理后台测试问题记录
> 测试环境:`https://admin.118im.com/admin#/dashboard/console`
> 执行日期:2026-09-07
> 参考用例:`测试用例文档.md`
> 测试方式:浏览器黑盒测试、HTTP 只读探测、测试库只读核验、当前代码静态核查
## 1. 执行摘要
| 项目 | 结果 | 说明 |
|---|---|---|
| 测试站点可达性 | 通过 | `/admin/` 返回 HTTP 200,响应约 0.2-0.5 秒 |
| 前端功能部署 | 通过 | 部署包包含 `imDashboard`、`imPrivateMessage`、`imGroupMessage`、`imUserLoginHistory`、`imLoginFailureLog`、`imSecurityPolicy`、`imSensitiveWord`、`imUserPolicyGroup` |
| Admin 新接口路由 | 通过 | 8 个列表/概览接口均已注册;无登录态请求统一返回业务码 `61`“登录身份已失效” |
| 浏览器页面操作 | 已恢复 | 已接管用户登录标签页,完成 8 个新增菜单及查询/刷新按钮的只读复测 |
| Admin 测试库结构 | 已修复 | 已在指定测试库 `hotgo` 创建 7 张策略相关表、8 个页面菜单及按钮权限 |
| IM 测试库结构 | 通过 | `tinode` 已存在 3 张新增审计/处置表 |
| IM 登录审计数据 | 部分通过 | IM Internal API 已恢复,成功登录历史返回 77 条;失败日志返回成功空列表,仍缺少失败场景验收数据 |
| Kafka Admin 命令 | 风险 | 13 条历史命令中 12 条长期待处理、1 条死信,需检查消费链路 |
| 消息隐藏/删除 | 未执行 | `im_message_admin_actions` 为 0;尚未准备专用消息数据,且 Kafka 命令链路存在长期积压风险 |
| 好友总开关/宵禁 | 未通过实现核查 | Admin 可配置和发布,但当前 IM Server 未找到运行时消费及拦截代码 |
当前结论:**不通过。页面菜单和 Admin 本地策略表已可用,但 IM Internal 查询接口不可用,真实概览、消息和登录日志无法展示;IM 运行时策略执行也尚未完成。**
## 2. 问题清单
### BUG-001 Admin 测试库缺少策略相关表
| 字段 | 内容 |
|---|---|
| 严重级别 | P0 |
| 类型 | 数据库部署/环境配置 |
| 状态 | 已修复并完成只读页面复测 |
| 关联用例 | `UPG-*`、`SW-*`、`SEC-*`、`FRIEND-*`、`CURFEW-*`、`IPRULE-*` |
**操作步骤**
1. 连接用户指定测试数据库 `64.118.137.182:53306`。
2. 查询 `information_schema.tables` 中 `hotgo` 库的本轮新增表。
**实际结果**
以下表均不存在:
- `im_security_policy`
- `im_message_curfew_rules`
- `im_security_ip_rules`
- `im_sensitive_word_groups`
- `im_sensitive_words`
- `im_user_policy_groups`
- `im_user_policy_group_members`
**预期结果**
测试环境 Admin Server 所连接的数据库应包含全部新增表,并已执行初始化数据。
**影响**
若线上 Admin Server 确实连接此 `hotgo` 库,用户策略分组、敏感词、安全策略、宵禁和 IP 规则页面会查询失败或无法保存。如果 Admin Server 实际连接其他数据库,则说明当前提供的测试库地址与部署配置不一致,需要明确真实连接库后重新核验。
**建议**
核对 Admin Server 生产配置中的数据库 DSN;确认无误后执行已提交的 Admin 建表 SQL,并对表结构、索引和初始化数据做一次部署校验。
**处理记录(2026-09-07)**
已在 `hotgo` 执行 `docs/admin-adfahi-research/sql/Admin-Server改造.sql`。7 张表均创建成功,`im_security_policy` 已初始化 1 条全局默认已发布策略,其余新增业务表初始为空。页面复测中,安全策略正确展示该默认策略,敏感词和用户分组正确展示空列表;写操作尚未执行。
### BUG-002 测试库缺少 8 个新增页面菜单记录
| 字段 | 内容 |
|---|---|
| 严重级别 | P0 |
| 类型 | 菜单/权限初始化 |
| 状态 | 已修复并完成超级管理员复测 |
| 关联用例 | `COM-001`、`COM-002` |
**实际结果**
在 `hotgo.hg_admin_menu` 中未查询到以下菜单:
- `imDashboardIndex`
- `imPrivateMessageIndex`
- `imGroupMessageIndex`
- `imUserLoginHistoryIndex`
- `imLoginFailureLogIndex`
- `imUserPolicyGroupIndex`
- `imSensitiveWordIndex`
- `imSecurityPolicyIndex`
部署前端包已经包含对应页面模块,后端接口也已注册,说明代码已部署但数据库菜单初始化可能未执行。
**预期结果**
超级管理员登录后可在 IM 管理下看到 8 个页面;功能权限记录完整,普通角色按授权显示按钮。
**建议**
确认部署库后执行:
- `server/storage/data/generate/im_adfahi_batch2_menu.sql`
- `server/storage/data/generate/im_adfahi_policy_menu.sql`
执行后分别用超级管理员、只读管理员和租户管理员回归菜单及按钮权限。
**处理记录(2026-09-07)**
已执行 `im_adfahi_policy_menu.sql` 和 `im_adfahi_batch2_menu.sql`。8 个页面菜单均启用并挂载到 `imManager`(ID 2431)下;页面与按钮权限合计 43 条。用户需重新登录或刷新权限缓存后复测菜单显示。
**页面复测(2026-09-07)**
超级管理员展开“IM管理”后,数据概览、用户分组、单聊消息、群聊消息、用户登录历史、登录失败日志、敏感词库、安全策略 8 个页面菜单均可见并可打开。`COM-001` 的菜单可见性通过;只读管理员和租户管理员权限仍需使用对应账号复测。
### BUG-003 Kafka Admin 命令存在长期 Pending 和 Dead 记录
| 字段 | 内容 |
|---|---|
| 严重级别 | P0 |
| 类型 | Kafka/异步命令链路 |
| 状态 | 待处理 |
| 关联用例 | `PM-004` 至 `PM-008`、`GM-003` 至 `GM-006`、`NFT-005` |
**实际结果**
`hotgo.im_admin_commands` 共 13 条记录:
| 事件类型 | 状态 | 数量 |
|---|---:|---:|
| `user.password.reset` | `pending` | 12 |
| `user.password.reset` | `dead` | 1 |
最新死信记录时间为 2026-08-28,原因 `RESOURCE_NOT_FOUND`,消息 `not found`。其余 Pending 记录同样创建于 2026-08-28,长期未进入成功或失败终态。
**预期结果**
命令应在约定时间内进入 `succeeded`、`failed` 或 `dead` 终态;Pending 不应长期积压。死信应可查看、重放、忽略或解决。
**影响**
消息隐藏/删除、用户治理等 Kafka 命令可能出现后台提示已提交但 IM 不执行,属于治理能力失效风险。
**建议**
检查 Admin outbox worker、Kafka topic、consumer group、IM consumer 启动配置及结果回传 topic;处理历史积压后再执行消息隐藏/删除端到端测试。
### BUG-004 IM Server 尚未执行安全策略、敏感词和用户策略分组
| 字段 | 内容 |
|---|---|
| 严重级别 | P0 |
| 类型 | 功能未完成 |
| 状态 | 待开发 |
| 关联用例 | `SW-005`、`SW-007`、`FRIEND-*`、`CURFEW-*`、`IPRULE-*` |
**代码核查结果**
Admin Server 已实现策略数据保存、发布和 `security.policy.updated` 等事件写出;当前 IM Server 仅发现 `message.hide`、`message.delete` 等 Admin 命令消费,未发现:
- `security.policy.updated` 事件消费与策略缓存刷新。
- 全局禁止加好友在用户搜索、个人名片、群成员、二维码、邀请自动添加、推荐联系人六类入口的后端校验。
- 禁加好友时个人资料、个人名片的隐藏处理。
- 消息发送路径中的宵禁日期、时区、跨午夜、范围和白名单判定。
- 敏感词及用户策略分组在 IM 运行时的加载与执行。
**预期结果**
后台发布后,IM Server 接收事件、加载对应版本,并在实际移动端请求链路强制执行;直接调用接口也不能绕过。
**影响**
后台可以显示“发布成功”,但移动端行为不会变化。全局禁止加好友和宵禁属于明确要求,未完成时禁止上线。
### DATA-001 IM 测试数据不足,无法覆盖失败日志和消息处置
| 字段 | 内容 |
|---|---|
| 严重级别 | P1 |
| 类型 | 测试数据 |
| 状态 | 待准备 |
| 关联用例 | `PM-*`、`GM-*`、`LFAIL-*` |
**实际结果**
- `tinode.im_user_login_logs`:43 条。
- `tinode.im_login_failure_logs`:0 条。
- `tinode.im_message_admin_actions`:0 条。
登录日志分布以 `password/login` 和 `token_refresh/login` 为主,尚不足以覆盖主动退出、被踢、会话过期等事件。账号掩码和 metadata 的只读抽查未发现手机号全明文或密码、验证码、Token 键值。
**建议**
准备专用测试账号,分别触发密码错误、验证码错误、账号不存在、账号禁用、限频和风控拦截;准备单聊和群聊消息后执行隐藏、删除及幂等重放。
### BUG-005 IM Internal 查询接口返回 resource not found
| 字段 | 内容 |
|---|---|
| 严重级别 | P0 |
| 类型 | 服务部署/路由联调 |
| 状态 | 已修复,接口复测通过 |
| 关联用例 | `DASH-001`、`DASH-002`、`PM-001`、`GM-001`、`ULOGIN-001`、`ULOGIN-004`、`LFAIL-001` |
**操作步骤**
1. 使用超级管理员进入“IM管理”。
2. 打开数据概览并点击“刷新”。
3. 分别进入单聊消息、群聊消息、用户登录历史、登录失败日志并点击“查询”。
**实际结果**
- 数据概览六项指标全部显示 0,趋势表显示“无数据”,页面提示 `resource not found`。
- 单聊消息、群聊消息、用户登录历史、登录失败日志均显示 0 条,查询后提示 `resource not found`。
- 浏览器控制台在每次查询后记录同名接口错误。
- 数据库已有 `tinode.im_user_login_logs` 43 条,因此用户登录历史页面的 0 条不是正常空数据。
**预期结果**
Admin Server 应能调用 IM Server Internal API,概览、消息及登录日志返回真实数据;确实无数据的接口应返回成功的空列表,不能返回资源不存在。
**根因定位(2026-09-07)**
- 当前 IM Server 源码已注册 `/internal/admin-query/`,并已实现 dashboard overview/trends、private-messages、group-messages、user-login-logs、login-failure-logs 路由,不是五个查询分别缺少实现。
- `authorizeInternalAPI` 在 `internal_api.enabled=false` 时固定返回 HTTP 404、业务码 `30004`、消息 `resource not found`;该响应与页面错误完全一致。
- 对测试环境 `https://api.118im.com/internal/admin-query/tenants/probe/dashboard/overview` 进行未签名只读探测,实际返回 HTTP 404、业务码 `30004`、消息 `resource not found`。若 Internal API 已启用,同一未签名请求应返回 HTTP 401、消息 `internal signature required`。
- Gitea Actions 第 66 次部署(run 99,提交 `b92154b`)日志显示工作流调用 `deploy.sh` 时已经设置 `INTERNAL_API_ENABLED=true`,且部署成功切换到 green 实例;但部署后的公开实例仍表现为 disabled。
由此确认:提交 `c68c5ac` 将 Docker 配置模板中原本固定启用的 Internal API 改成了额外环境开关,但现有蓝绿部署并不会把这些新增变量注入容器,导致容器继承镜像默认值 `false`。这是新增启动配置造成的部署回归,不应要求部署机再增加一组开关配置。
**建议**
已将 `docker/tinode/config.template` 恢复为改造前的启动方式:Internal API 固定开启、签名窗口固定 300 秒、调用方固定为 `hotgo-admin`,继续复用原有共享密钥配置;同时移除部署工作流中新增的开关、窗口和服务名变量。部署机无须增加 Compose 配置。
重新构建并部署 IM Server 后,先执行未签名探测:预期由当前的 HTTP 404 `resource not found` 变为 HTTP 401 `internal signature required`;再使用正确签名验证 overview、trends、private/group message、login history、login failure 六类接口,最后回归后台页面。
**接口复测(2026-09-07,IM `35da302`)**
- Gitea Actions run 100 部署成功,线上未签名探测由 HTTP 404 变为预期的 HTTP 401 `internal signature required`。
- 正确签名请求全部返回 HTTP 200、业务码 `0`:概览返回总用户 211、活跃用户 12、群组 95、会话 197、消息 2182、今日消息 167;7 日趋势返回 7 行。
- 单聊列表返回 1528 条,群聊列表返回 654 条;分页、关键词筛选及首条消息详情均通过。
- 用户登录历史返回 77 条;登录方式、事件、脱敏账号及组合筛选均通过。
- 登录失败日志返回 HTTP 200 的空列表,总数 0,属于当前无测试数据,不再是接口错误。
- 不存在租户正确返回 HTTP 404、业务码 `30010`、`tenant unavailable`。
- Admin Server 的 IM 签名客户端和字段转换相关 Go 测试通过。
### BUG-006 新页面调用的字典 Store API 与部署版本不兼容
| 字段 | 内容 |
|---|---|
| 严重级别 | P1 |
| 类型 | 前端兼容性 |
| 状态 | 已发布,静态包复测通过,待登录态页面点击确认 |
| 关联用例 | `UPG-001`、`SW-002`、`SEC-001`、`COM-004` |
**实际结果**
- 用户分组页面加载及点击“查询”时抛出 `TypeError: D.getOptions is not a function`。
- 敏感词库页面抛出 3 次 `c.getOptions is not a function`。
- 安全策略页面抛出 `q.getOptions is not a function`。
- 安全策略表格仍能显示 1 条默认策略,敏感词和用户分组仍能显示空列表,但字典筛选项和后续编辑表单存在失效风险。
**预期结果**
页面应通过当前项目字典 Store 的公开方法加载选项,控制台无异常;状态、类型、动作、风险等级等枚举应正确展示和筛选。
**建议**
已对照当前 `useDictStore` 实际 API,将用户分组、敏感词、安全策略页面统一改为 `dict.getOption()`;额外排查并修复 IM 文件策略同类调用,需过滤选项时使用 `dict.getOptionUnRef()`。源码扫描已无业务页面调用 `dict.getOptions()`,ESLint 和生产构建通过。重新发布 Admin 前端后回归筛选、编辑弹窗和字典标签。
**发布复测(2026-09-07,Admin `7839dce`)**
- Gitea Actions run 103 发布成功,线上入口包已切换到 `index-Ch8uxbvK.js`。
- 用户分组、敏感词、安全策略、文件策略四个线上动态页面包均可下载,且均不再包含 `getOptions` 调用,确认不是旧分包或 CDN 缓存。
- 十条相关 Admin 路由的未登录请求均返回业务码 `61`,鉴权边界正常。
- 本轮无法取得内置浏览器已登录标签页的会话控制,筛选下拉、编辑弹窗和写操作仍需登录态页面点击确认,不能仅凭静态包核对标记为完整通过。
### BLOCK-001 浏览器安全检查阻塞自动化页面测试
| 字段 | 内容 |
|---|---|
| 严重级别 | 环境阻塞,非产品缺陷 |
| 状态 | 已解除 |
| 关联用例 | 所有需要登录态和页面点击的用例 |
已识别到 Codex 内置浏览器中的测试环境标签页,页面标题为“主控台”,URL 和登录状态由用户确认。但自动化接管域名时,Codex 浏览器安全检查返回“permission request could not complete”,因此未读取或复用登录 Token,也未绕过安全控制。
恢复浏览器授权后,应从 `COM-001` 开始继续,并优先执行只读查询。涉及删除消息、删除规则等操作时仅使用专用测试数据。
**复测记录(2026-09-07,建表后)**
再次读取内置浏览器状态,测试标签页仍为“主控台”且 URL 正确;调用浏览器接管接口时仍返回 `Auto-review could not complete for https://admin.118im.com`。本次复测未绕过安全控制,页面用例继续标记为阻塞。
**解除记录(2026-09-07)**
再次连接后浏览器授权恢复,已成功接管同一登录标签页并完成菜单展开、页面切换、查询/刷新和表格读取。该项不再阻塞后续自动化测试。
## 3. 已执行用例
| 执行 ID | 对应测试 | 结果 | 实际结果 |
|---|---|---|---|
| RUN-001 | 站点可达性 | 通过 | `/admin/` HTTP 200,HTML 和静态资源可加载 |
| RUN-002 | 前端部署完整性 | 通过 | 8 个新增页面模块均存在于部署 JS 包 |
| RUN-003 | 未登录接口鉴权 | 通过 | 8 条接口统一返回业务码 `61`,未泄露业务数据 |
| RUN-004 | COM-001 菜单检查 | 通过 | 超级管理员可见并可打开 8 个新增页面;其他角色权限待测 |
| RUN-005 | Admin 新表检查 | 失败 | 7 张策略相关表缺失 |
| RUN-006 | IM 新表检查 | 通过 | 3 张表均存在 |
| RUN-007 | ULOGIN-001 数据检查 | 失败 | 数据库有 43 条登录记录,后台查询为 0 条并提示 `resource not found` |
| RUN-008 | ULOGIN-005 敏感字段抽查 | 通过 | 当前 SQL 规则未发现可疑明文记录 |
| RUN-009 | LFAIL-001 至 LFAIL-007 | 部分阻塞 | 页面可打开和查询,但接口返回 `resource not found`;失败日志表为 0 条,缺少动作数据 |
| RUN-010 | PM/GM 消息处置 | 阻塞 | 动作表为 0 条,Kafka 另有长期积压风险 |
| RUN-011 | FRIEND/CURFEW | 失败(实现核查) | IM 运行时执行代码未完成 |
| RUN-012 | BUG-001 建表复测 | 通过 | 7 张 Admin 策略表均存在,默认安全策略 1 条 |
| RUN-013 | BUG-002 菜单复测 | 通过 | 8 个页面菜单、43 条页面/按钮权限存在且状态启用 |
| RUN-014 | BLOCK-001 浏览器接管复测 | 阻塞 | 建表后重试仍被 Codex 域名安全检查拒绝,未执行页面点击 |
| RUN-015 | BLOCK-001 浏览器接管再次复测 | 通过 | 授权恢复,已完成登录态页面操作 |
| RUN-016 | DASH-001、DASH-002 | 失败 | 刷新后六项指标均为 0,趋势无数据,接口返回 `resource not found` |
| RUN-017 | UPG-001 | 失败 | 表格正确显示 0 条,但查询触发 `getOptions is not a function` |
| RUN-018 | PM-001 | 失败 | 单聊消息显示 0 条,查询接口返回 `resource not found` |
| RUN-019 | GM-001 | 失败 | 群聊消息显示 0 条,查询接口返回 `resource not found` |
| RUN-020 | ULOGIN-001、ULOGIN-004 | 失败 | 数据库 43 条,页面为 0 条,查询接口返回 `resource not found` |
| RUN-021 | LFAIL-001 页面查询 | 失败 | 页面可操作,但查询接口返回 `resource not found`;暂无失败登录测试数据 |
| RUN-022 | SW 页面只读查询 | 部分通过 | 正确返回 0 条;控制台存在 3 个字典 `getOptions` 异常 |
| RUN-023 | SEC 页面只读查询 | 部分通过 | 正确展示默认已发布策略 1 条;控制台存在字典 `getOptions` 异常 |
| RUN-024 | BUG-005 Internal API 启用复测 | 通过 | 未签名请求返回预期的 HTTP 401,不再返回 `resource not found` |
| RUN-025 | DASH-001、DASH-002 接口复测 | 通过 | 概览返回 211 用户、95 群组、2182 消息;7 日趋势返回 7 行 |
| RUN-026 | PM-001、GM-001 接口复测 | 通过 | 单聊 1528 条、群聊 654 条;分页、关键词和详情查询通过 |
| RUN-027 | ULOGIN 接口复测 | 通过 | 登录历史 77 条;登录方式、事件、脱敏账号及组合筛选通过 |
| RUN-028 | LFAIL-001 接口复测 | 部分通过 | 接口成功返回空列表;仍需准备失败登录数据验证详情及处置按钮 |
| RUN-029 | BUG-006 代码复测 | 通过 | 4 个页面同类字典调用已修复,ESLint 和生产构建通过;待发布后页面复测 |
| RUN-030 | BUG-006 发布包复测 | 部分通过 | run 103 发布成功,4 个线上动态页面包无 `getOptions`;待登录态页面点击确认 |
## 4. 后续复测顺序
1. 回归 BUG-005 对应后台页面,确认概览卡片和四个列表正确显示接口数据。
2. 发布 BUG-006 前端修复并回归筛选、编辑弹窗和字典标签。
3. 修复 Kafka Pending/Dead 链路,验证命令可进入终态。
4. 补齐 IM Server 安全策略、好友入口、宵禁和敏感词运行时实现。
5. 使用对应角色账号回归租户隔离、按钮权限、筛选、详情、分页和导出。
6. 使用专用数据执行新增、编辑、发布、隐藏、删除、IP 黑名单和封禁用户。
7. 使用移动端完成好友六入口、直接接口绕过、宵禁边界及白名单验收。
## 5. 复测记录
| 日期 | 版本 | 问题编号 | 复测结果 | 证据/备注 | 测试人 |
|---|---|---|---|---|---|
| 2026-09-07 | Admin `16cc456` | BUG-001 | 通过 | 7 张表存在,默认策略 1 条 | Codex |
| 2026-09-07 | Admin `16cc456` | BUG-002 | 通过 | 8 个菜单、43 条权限启用 | Codex |
| 2026-09-07 | Admin `16cc456` | BLOCK-001 | 阻塞 | 浏览器域名安全检查仍不可用 | Codex |
| 2026-09-07 | 测试环境部署版本 | BLOCK-001 | 通过 | 浏览器授权恢复并完成页面点击 | Codex |
| 2026-09-07 | 测试环境部署版本 | BUG-005 | 失败 | 5 类 IM 数据页面和概览统一返回 `resource not found` | Codex |
| 2026-09-07 | 测试环境部署版本 | BUG-006 | 失败 | 用户分组、敏感词、安全策略存在字典 Store API 异常 | Codex |
| 2026-09-07 | IM `35da302` / run 100 | BUG-005 | 通过 | 六类签名查询均为 HTTP 200;分页、筛选和消息详情通过 | Codex |
| 2026-09-07 | Admin 待发布版本 | BUG-006 | 代码通过 | 修复 4 个页面的字典 Store API 调用,生产构建通过 | Codex |
| 2026-09-07 | Admin `7839dce` / run 103 | BUG-006 | 部分通过 | 线上入口及 4 个动态页面包已更新,无错误方法调用;待登录态页面确认 | Codex |
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,582 @@
# IM Admin 内部查询接口缺口清单
> 目标:梳理 HotGo/Admin 迁移到“Admin 只访问 hotgo,IM Server 只访问租户库”后,IM Server 需要补齐的 `/internal/admin-query/*` 只读接口。
> 当前日期:2026-08-27
> 适用项目:`im_manager` Admin 后台、`im_server` IM 服务端。
## 1. 背景
Kafka 写链路已经在 Admin 侧补齐:Admin 写操作落 `im_admin_commands`、`im_admin_outbox`,通过 Kafka 发给 IM Server,IM Server 执行业务后回传 `im.admin.command.result`。
查询链路还没有完全隔离。Admin 当前仍有不少页面通过 `imtenant.DB()` 或 `resolveImTenantDB()` 直连租户库。若要严格满足目标架构,Admin 查询租户业务数据时应调用 IM Server 的内部只读接口:
```text
GET /internal/admin-query/tenants/{tenant_code}/...
```
IM Server 当前已经具备部分内部查询接口,但还不能覆盖 Admin 后台页面。
## 2. 已有接口
以下接口在 IM Server 当前实现中已经存在,可供 Admin 先迁移:
| 接口 | 用途 | 备注 |
|---|---|---|
| `GET /internal/admin-query/tenants/{tenant_code}/users/{user_id}` | 用户详情 | 需要增强字段,见 P0。 |
| `GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}` | 群详情和治理配置 | 需要增强 topic 基础字段,见 P0。 |
| `GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/members` | 群成员列表 | 当前偏 cursor 分页,Admin 表格还需要 total。 |
| `GET /internal/admin-query/tenants/{tenant_code}/messages/{topic}/{seq_id}` | 消息详情 | 当前 Admin 暂未完整迁移消息页面。 |
| `GET /internal/admin-query/tenants/{tenant_code}/files/{file_id}` | 文件详情 | 还缺文件列表和引用列表。 |
| `GET /internal/admin-query/tenants/{tenant_code}/kafka/dead-letters` | Kafka 死信列表 | Admin 已接入。 |
| `GET /internal/admin-query/tenants/{tenant_code}/kafka/dead-letters/{dead_id}` | Kafka 死信详情 | Admin 已接入。 |
## 3. 统一约定
### 3.1 鉴权
沿用 IM Server 内部 API HMAC 鉴权:
```text
X-Service-Name
X-Timestamp
X-Nonce
X-Signature
```
服务端需要校验:
1. service 是否存在。
2. service 是否包含 `admin_query` capability。
3. timestamp 是否在允许窗口内。
4. nonce 是否防重放。
5. 签名是否匹配。
### 3.2 分页返回
Admin 的 `BasicTable` 更适合 page/per_page/total 格式。建议所有列表接口统一返回:
```json
{
"code": 0,
"data": {
"items": [],
"total": 0,
"page": 1,
"per_page": 20
}
}
```
如果 IM Server 内部更适合 cursor,也建议同时返回:
```json
{
"code": 0,
"data": {
"items": [],
"total": 0,
"page": 1,
"per_page": 20,
"next_cursor": "",
"has_more": false
}
}
```
### 3.3 详情返回
详情接口统一返回:
```json
{
"code": 0,
"data": {}
}
```
### 3.4 时间与 ID
1. 时间统一返回 RFC3339/RFC3339Nano 字符串。
2. 雪花 ID、Tinode 用户 ID、19 位整数 ID 建议以字符串返回,避免前端丢精度。
3. 入参同时兼容字符串 ID 和纯数字 ID 时,IM Server 内部自行解析。
## 4. P0:优先补齐
P0 是 Admin 当前核心页面迁移所需接口,补完后可以先迁移用户、群组、文件等高频页面。
### 4.1 用户列表
```text
GET /internal/admin-query/tenants/{tenant_code}/users
```
用途:IM 用户管理列表。
建议查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| `user_id` | string | 用户 ID,支持精确查询。 |
| `nickname` | string | 昵称模糊查询。 |
| `auth_name` | string | 登录标识,匹配 username/phone/email/凭证。 |
| `state` | int | 用户状态。 |
| `created_at_start` | string | 创建时间开始。 |
| `created_at_end` | string | 创建时间结束。 |
| `last_seen_start` | string | 最后在线开始。 |
| `last_seen_end` | string | 最后在线结束。 |
| `page` | int | 页码。 |
| `per_page` | int | 每页数量,最大建议 200。 |
建议返回字段:
| 字段 | 说明 |
|---|---|
| `user_id` | 字符串用户 ID。 |
| `username` | 用户名。 |
| `phone` | 手机号。 |
| `nickname` | 昵称。 |
| `public_account` | 公开账号。 |
| `state` | 用户状态。 |
| `gender` | 性别。 |
| `region` | 地区。 |
| `auth_names` | 登录标识摘要。 |
| `credentials` | 凭证摘要。 |
| `auth_version` | 鉴权版本。 |
| `password_updated_at` | 密码更新时间。 |
| `last_seen` | 最后在线时间。 |
| `user_agent` | 最后设备 UA。 |
| `created_at` | 创建时间。 |
| `updated_at` | 更新时间。 |
当前 Admin 直连涉及表:`im_users`、`im_user_profiles`、`im_user_security`、`im_credentials`。
### 4.2 用户详情增强
现有接口:
```text
GET /internal/admin-query/tenants/{tenant_code}/users/{user_id}
```
建议增强返回字段:
| 字段 | 说明 |
|---|---|
| `profile` | 结构化用户资料。 |
| `public` | Tinode public JSON。 |
| `trusted` | Tinode trusted JSON。 |
| `access` | 访问权限 JSON。 |
| `tags` | 标签。 |
| `security` | `auth_version/password_updated_at` 等安全信息。 |
| `credentials` | 凭证列表,包含 method/value/done。 |
### 4.3 用户设备列表
```text
GET /internal/admin-query/tenants/{tenant_code}/users/{user_id}/devices
```
用途:用户详情中的登录设备列表。
建议查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| `device_id` | string | 设备 ID。 |
| `platform` | string | 平台。 |
| `page` | int | 页码。 |
| `per_page` | int | 每页数量。 |
建议返回字段:
| 字段 | 说明 |
|---|---|
| `device_id` | 设备 ID。 |
| `platform` | 平台。 |
| `user_agent` | UA。 |
| `last_seen` | 最后在线。 |
| `created_at` | 创建时间。 |
| `updated_at` | 更新时间。 |
当前 Admin 直连涉及表:`im_devices`。
### 4.4 群列表
```text
GET /internal/admin-query/tenants/{tenant_code}/groups
```
用途:IM 群组管理列表。
建议查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| `topic` | string | 群 Topic。 |
| `owner_id` | string | 群主 ID。 |
| `state` | int | Tinode topic 状态。 |
| `group_status` | string | 业务群状态。 |
| `created_at_start` | string | 创建时间开始。 |
| `created_at_end` | string | 创建时间结束。 |
| `touched_at_start` | string | 活跃时间开始。 |
| `touched_at_end` | string | 活跃时间结束。 |
| `page` | int | 页码。 |
| `per_page` | int | 每页数量。 |
建议返回字段:
| 字段 | 说明 |
|---|---|
| `topic` | 群 Topic。 |
| `state` | Tinode 状态。 |
| `owner_id` | 群主 ID。 |
| `seq_id` | 最新消息序号。 |
| `del_id` | 删除序号。 |
| `sub_count` | Tinode 订阅成员数。 |
| `touched_at` | 活跃时间。 |
| `created_at` | 创建时间。 |
| `updated_at` | 更新时间。 |
| `public` | Tinode public JSON。 |
| `trusted` | Tinode trusted JSON。 |
| `tags` | 标签。 |
| `aux` | 辅助 JSON。 |
| `group_name` | 群名称。 |
| `avatar_file_id` | 群头像文件 ID。 |
| `description` | 群简介。 |
| `group_status` | 业务群状态。 |
| `join_approval` | 入群审批开关。 |
| `mute_all` | 全员禁言。 |
| `member_upload_files` | 成员上传文件。 |
| `member_private_chat` | 成员私聊。 |
| `member_invite` | 成员邀请。 |
| `max_member_count` | 最大成员数。 |
| `max_admin_count` | 最大管理员数。 |
| `business_member_count` | 业务成员数。 |
| `settings_version` | 治理配置版本。 |
| `settings_updated_at` | 治理配置更新时间。 |
当前 Admin 直连涉及表:`im_topics`、`im_group_settings`。
### 4.5 群详情增强
现有接口:
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}
```
当前接口主要返回 `im_group_settings`。建议增强 Tinode topic 基础字段:
| 字段 | 说明 |
|---|---|
| `state` | Topic 状态。 |
| `seq_id` | 最新消息序号。 |
| `del_id` | 删除序号。 |
| `sub_count` | 订阅数量。 |
| `public` | public JSON。 |
| `trusted` | trusted JSON。 |
| `access` | access JSON。 |
| `tags` | tags JSON。 |
| `aux` | aux JSON。 |
| `touched_at` | 活跃时间。 |
### 4.6 群成员列表增强
现有接口:
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/members
```
建议增强查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| `user_id` | string | 用户 ID 精确过滤。 |
| `role` | string | `owner/admin/member`。 |
| `member_status` | string | 成员状态。 |
| `q` | string | 昵称、公开账号等关键词。 |
| `eligible_role` | string | 候选角色筛选。 |
| `page` | int | 页码。 |
| `per_page` | int | 每页数量。 |
| `cursor` | string | 兼容 cursor 分页。 |
| `limit` | int | 兼容 cursor 分页。 |
建议增强返回:
| 字段 | 说明 |
|---|---|
| `total` | 总数,Admin 表格需要。 |
| `items[].user_id` | 用户 ID。 |
| `items[].nickname` | 昵称。 |
| `items[].state` | 用户状态。 |
| `items[].mode_want` | 期望权限。 |
| `items[].mode_given` | 授予权限。 |
| `items[].recv_seq_id` | 接收序号。 |
| `items[].read_seq_id` | 已读序号。 |
| `items[].del_id` | 删除序号。 |
| `items[].role` | 业务角色。 |
| `items[].member_status` | 成员状态。 |
| `items[].group_nickname` | 群昵称。 |
| `items[].pinned` | 置顶。 |
| `items[].mute_notifications` | 免打扰。 |
| `items[].join_source` | 加入来源。 |
| `items[].joined_by` | 邀请或处理人。 |
| `items[].joined_at` | 加入时间。 |
| `items[].left_at` | 离开时间。 |
| `items[].removed_by` | 移除人。 |
| `items[].version` | 版本。 |
当前 Admin 直连涉及表:`im_group_members`、`im_subscriptions`、`im_users`、`im_user_profiles`。
### 4.7 入群申请列表
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/join-requests
```
建议查询参数:`status`、`applicant_user_id`、`page`、`per_page`。
建议返回字段:`request_id`、`topic`、`applicant_user_id`、`inviter_user_id`、`source`、`message`、`status`、`reject_reason`、`handled_by`、`handled_at`、`expires_at`、`version`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_group_join_requests`。
### 4.8 群邀请列表
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/invitations
```
建议查询参数:`status`、`target_user_id`、`inviter_user_id`、`page`、`per_page`。
建议返回字段:`invitation_id`、`topic`、`inviter_user_id`、`target_user_id`、`message`、`status`、`result`、`join_request_id`、`expires_at`、`handled_at`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_group_invitations`。
### 4.9 群公告列表
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/announcements
```
建议查询参数:`status`、`page`、`per_page`。
建议返回字段:`announcement_id`、`topic`、`author_user_id`、`content`、`status`、`version`、`published_at`、`deleted_by`、`deleted_at`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_group_announcements`。
### 4.10 群禁言列表
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/mutes
```
建议查询参数:`status`、`user_id`、`page`、`per_page`。
建议返回字段:`topic`、`user_id`、`muted_by`、`muted_at`、`expires_at`、`reason`、`status`、`revoked_by`、`revoked_at`、`version`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_group_mutes`。
### 4.11 群管理日志
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/admin-logs
```
建议查询参数:`action`、`operator_id`、`page`、`per_page`。
建议返回字段:`log_id`、`topic`、`operator_id`、`target_user_id`、`action`、`object_type`、`object_id`、`before_data`、`after_data`、`request_id`、`idempotency_key`、`created_at`。
当前 Admin 直连涉及表:`im_group_admin_logs`。
### 4.12 群文件列表
```text
GET /internal/admin-query/tenants/{tenant_code}/groups/{topic}/files
```
用途:群治理页面中的群文件列表。不要和已有单文件详情接口混淆。
建议查询参数:`status`、`file_type`、`file_name`、`uploader_id`、`page`、`per_page`。
建议返回字段:`group_file_id`、`topic`、`file_id`、`source_topic`、`source_seq`、`uploader_id`、`file_name`、`mime_type`、`file_type`、`file_size`、`status`、`deleted_by`、`deleted_at`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_group_files`。
### 4.13 文件列表
```text
GET /internal/admin-query/tenants/{tenant_code}/files
```
用途:IM 文件管理列表。
建议查询参数:`file_id`、`user_id`、`status`、`mime_type`、`created_at_start`、`created_at_end`、`page`、`per_page`。
建议返回字段:`id`、`file_id`、`user_id`、`mime_type`、`file_size`、`file_name`、`location`、`etag`、`status`、`quarantine_reason`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_fileuploads`。
### 4.14 文件引用列表
```text
GET /internal/admin-query/tenants/{tenant_code}/files/{file_id}/links
```
用途:文件详情中展示消息引用。
建议返回字段:`topic`、`seq_id`、`user_id`、`created_at`。
当前 Admin 直连涉及表:`im_filemsglinks`。
## 5. P1:后台审核和运营页面
### 5.1 审计日志
```text
GET /internal/admin-query/tenants/{tenant_code}/audit-logs
GET /internal/admin-query/tenants/{tenant_code}/audit-logs/{id}
```
建议查询参数:`actor_user_id`、`action`、`target_type`、`target_id`、`created_at_start`、`created_at_end`、`page`、`per_page`。
建议返回字段:`id`、`request_id`、`actor_user_id`、`action`、`target_type`、`target_id`、`ip`、`user_agent`、`detail`、`created_at`。
当前 Admin 直连涉及表:`im_audit_logs`。
### 5.2 实名审核
```text
GET /internal/admin-query/tenants/{tenant_code}/realname-verifications
GET /internal/admin-query/tenants/{tenant_code}/realname-verifications/{id}
```
建议查询参数:`user_id`、`status`、`real_name`、`created_at_start`、`created_at_end`、`page`、`per_page`。
建议返回字段:`id`、`user_id`、`real_name`、`id_card_no_masked`、`front_file_id`、`back_file_id`、`status`、`reject_reason`、`reviewed_by`、`reviewed_at`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_realname_verifications`。
### 5.3 举报工单
```text
GET /internal/admin-query/tenants/{tenant_code}/abuse-reports
GET /internal/admin-query/tenants/{tenant_code}/abuse-reports/{id}
GET /internal/admin-query/tenants/{tenant_code}/reports/abuse/{report_id}/target
```
建议查询参数:`reporter_user_id`、`target_type`、`target_id`、`status`、`created_at_start`、`created_at_end`、`page`、`per_page`。
建议返回字段:`id`、`report_id`、`reporter_user_id`、`target_type`、`target_id`、`reason_code`、`description`、`evidence_file_ids`、`status`、`handler_id`、`handled_at`、`handle_result`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_abuse_reports`。
### 5.4 意见反馈
```text
GET /internal/admin-query/tenants/{tenant_code}/feedback-submissions
GET /internal/admin-query/tenants/{tenant_code}/feedback-submissions/{id}
```
建议查询参数:`user_id`、`category`、`status`、`created_at_start`、`created_at_end`、`page`、`per_page`。
建议返回字段:`id`、`feedback_id`、`user_id`、`category`、`content`、`contact`、`file_ids`、`status`、`handler_id`、`handled_at`、`reply`、`created_at`、`updated_at`。
当前 Admin 直连涉及表:`im_feedback_submissions`。
### 5.5 业务 Outbox
```text
GET /internal/admin-query/tenants/{tenant_code}/outbox/business-events
GET /internal/admin-query/tenants/{tenant_code}/outbox/group-events
GET /internal/admin-query/tenants/{tenant_code}/outbox/business-events/{id}
GET /internal/admin-query/tenants/{tenant_code}/outbox/group-events/{id}
```
建议查询参数:`event_type`、`status`、`target_type`、`target`、`created_at_start`、`created_at_end`、`page`、`per_page`。
当前 Admin 直连涉及表:`im_business_events_outbox`、`im_group_events_outbox`。
## 6. P2:如果要完全消除租户库直连
这些不是 Kafka 联调第一优先级,但如果最终要求 Admin 完全不碰租户库,也需要 IM Server 提供内部查询或改为 hotgo 摘要表。
### 6.1 组织架构
```text
GET /internal/admin-query/tenants/{tenant_code}/org/departments
GET /internal/admin-query/tenants/{tenant_code}/org/members
```
当前 Admin 直连涉及表:`im_org_departments`、`im_org_members`、`im_users`。
### 6.2 工作台应用
```text
GET /internal/admin-query/tenants/{tenant_code}/workbench/apps
```
当前 Admin 直连涉及工作台相关租户表。
### 6.3 租户任务看板摘要
```text
GET /internal/admin-query/tenants/{tenant_code}/dashboard/summary
```
当前 Admin 会统计多个租户库中的待处理实名、举报、反馈、Outbox 等数量。
### 6.4 租户配置类查询
如果这些配置仍放在租户库,也需要补接口;如果后续迁到 hotgo 控制库,则不需要通过 IM Server 查询。
```text
GET /internal/admin-query/tenants/{tenant_code}/feedback-config
GET /internal/admin-query/tenants/{tenant_code}/mailbox-config
GET /internal/admin-query/tenants/{tenant_code}/abuse-reasons
GET /internal/admin-query/tenants/{tenant_code}/app-releases
```
涉及模块:反馈配置、总经理信箱配置、举报原因、App 发布策略。
## 7. Admin 侧迁移建议
### 7.1 第一批迁移
在 IM Server 补完 P0 后,Admin 优先迁移:
1. 用户列表、用户详情、用户设备。
2. 群列表、群详情、群成员。
3. 群入群申请、邀请、公告、禁言、管理日志、群文件。
4. 文件列表、文件详情、文件引用。
### 7.2 第二批迁移
在 IM Server 补完 P1 后,Admin 再迁移:
1. 审计日志。
2. 实名审核。
3. 举报工单。
4. 意见反馈。
5. 业务 Outbox。
### 7.3 保留 hotgo 查询的范围
以下数据天然属于 hotgo 控制库,Admin 可继续查 hotgo,不需要走 IM Server:
1. 租户表和租户绑定表。
2. Admin 命令表:`im_admin_commands`。
3. Admin Outbox 表:`im_admin_outbox`。
4. Admin Kafka 审计事件表:`im_audit_events`。
5. 菜单、角色、管理员、系统配置、字典等 HotGo 系统表。
## 8. 验收标准
1. Admin 查询租户业务数据时,不再出现新的 `imtenant.DB()` 调用。
2. Admin 查询 IM Server 内部接口时,统一使用 HMAC 签名。
3. 列表接口支持 `page/per_page/total`,兼容后台表格分页。
4. 详情接口返回结构化 JSON,不返回数据库内部敏感字段。
5. 用户 ID、雪花 ID 等大整数以字符串返回。
6. IM Server 为内部查询写审计日志,至少包含 `tenant_code`、`service_name`、`path`、`query`、`trace_id`、`status`、`duration_ms`。
7. 关闭 IM Server 内部查询能力时,Admin 应得到明确错误,不泄露租户库信息。
+51 -2
View File
@@ -1,7 +1,7 @@
# IM Kafka 接入方案
> 日期:2026-08-26
> 关联文档:[IM Kafka 接入范围识别](./IM-Kafka接入范围识别.md)、[IM 前后端逐图对接协议](./IM-前后端对接文档.md)
> 日期:2026-08-26
> 关联文档:[IM Kafka 接入范围识别](./IM-Kafka接入范围识别.md)、[IM 前后端逐图对接协议](./IM-前后端对接文档.md)
> 目标:明确 Admin 后台、IM 服务、HotGo 控制库、租户业务库之间的 Kafka 接入边界、查询方式、鉴权方式和首批落地范围。
## 0. 与范围识别文档的关系
@@ -786,6 +786,55 @@ Admin 处理命令:
2. IM 按死信记录中的 `source_topic` 重新投递原 envelope。
3. 投递成功后把死信标记为 `replayed`,并累计 `replay_count`。
4. 如果重放后仍失败,普通消费者会再次写入新的 dead 记录,保留完整处理痕迹。
### 11.12 IM Server 已接入的用户治理命令
IM Server 已在 `im.admin.command` 中接入用户治理类后台动作。Admin 侧写 hotgo 操作单并发送 Kafka Command,IM 消费后修改租户库用户状态、踢在线连接、清理缓存并回传 `im.admin.command.result`。
支持命令:
| `event_type` | 行为 | Payload 关键字段 |
| --- | --- | --- |
| `user.ban` | 封禁用户,设置 `StateSuspended` 并踢所有在线会话。 | `actor_user_id`、`user_id` 或 `target_user_id`、`reason` |
| `user.unban` | 解封用户,设置 `StateOK`。 | `actor_user_id`、`user_id` 或 `target_user_id`、`reason` |
| `user.freeze` | 冻结账号,设置 `StateSuspended` 并踢所有在线会话。 | `actor_user_id`、`user_id` 或 `target_user_id`、`reason` |
| `user.unfreeze` | 恢复账号,设置 `StateOK`。 | `actor_user_id`、`user_id` 或 `target_user_id`、`reason` |
| `user.device.kick` | 踢指定设备在线连接,并删除设备推送记录。 | `actor_user_id`、`user_id` 或 `target_user_id`、`device_id`、`reason` |
| `user.logout_all` | 踢用户所有在线连接,不修改账号状态。 | `actor_user_id`、`user_id` 或 `target_user_id`、`reason` |
| `user.account.cancel` | 注销账号,默认软删除;`hard=true` 时硬删除。 | `actor_user_id`、`user_id` 或 `target_user_id`、`hard`、`reason` |
| `user.account.delete` | 删除账号,按硬删除处理。 | `actor_user_id`、`user_id` 或 `target_user_id`、`reason` |
命令示例:
```json
{
"event_id": "admin:user:ban:default:usrabc:001",
"tenant_code": "default",
"source_service": "hotgo-admin",
"target_service": "im-server",
"event_type": "user.ban",
"biz_type": "user_governance",
"biz_id": "usrabc",
"idempotency_key": "admin-operation-id",
"occurred_at": "2026-08-26T10:10:00.000Z",
"payload": {
"command_id": "admin-operation-id",
"actor_user_id": "usrAdmin",
"user_id": "usrabc",
"reason": "违规处理"
}
}
```
处理说明:
1. 封禁/冻结共用 Tinode 用户状态 `StateSuspended`;解封/恢复共用 `StateOK`。
2. 状态变更后 IM 会清理用户缓存,并向本节点 Hub 投递用户状态变更。
3. 踢设备只影响匹配 `device_id` 的在线连接,同时删除该设备 Push token 记录。
4. 全端登出只踢在线连接,不修改用户状态。
5. 注销/删除会停止用户相关 Topic、清理缓存并调用 `store.Users.Delete`。
6. 命令执行成功后统一回传 `im.admin.command.result`,并写入 `im.audit.event`。
7. 群消息消费成功后继续写入 Redis 热消息缓存,便于 24 小时内热点拉取。
事件示例:
File diff suppressed because it is too large Load Diff
+3 -10
View File
@@ -1,7 +1,7 @@
# IM Kafka 接入范围识别
> 日期:2026-08-26
> 依据:[IM 前后端逐图对接协议](./IM-前后端对接文档.md)
> 日期:2026-08-26
> 依据:[IM 前后端逐图对接协议](./IM-前后端对接文档.md)
> 目标:从前后端对接文档逐章识别适合接入 Kafka 的链路,明确 Kafka 与 WebSocket、Redis、MySQL、OSS 的边界。
## 1. 总体边界
@@ -73,14 +73,7 @@ Kafka 消息统一携带 `event_id`、`trace_id`、`tenant_code`、`biz_type`、
3. 总经理信箱提交生产 `im.workbench.event` 和 `im.audit.event`,后台处理结果再回推 C 端。
4. 工作台模块配置变更生产 `im.cache.invalidate`,IM 节点刷新进程内配置缓存。
## 7. 已补充:缓存失效与 Admin 配置查询
1. `im.cache.invalidate` 已接入 IM 消费端,支持租户、注册短信、第三方配置、字典配置、Redis 对象缓存、群热消息缓存失效。
2. IM 已增加 Admin 内部配置查询客户端,开启 `admin_internal_api.enabled=true` 后优先通过 Admin 查询 hotgo 侧配置,旧数据库读取作为灰度兜底。
3. Admin 侧需要补齐 `/internal/im-config/*` 只读接口,并与 IM 使用同一套 HMAC 签名密钥。
4. Kafka 死信已补齐查询、忽略、解决、重放闭环:查询走 IM 内部 API,处理走 `im.admin.command`。
## 8. 首批建议实现
## 7. 首批建议实现
1. 先实现 Kafka 基础生产者、消费者、配置、健康检查和消费幂等表。
2. 接入 `im.group.admin.event`,解决 Admin 后台群操作通过 Kafka 投递到 C 端。
@@ -0,0 +1,570 @@
# IM 后台全量需求文档
> 版本:V1.0 草案
> 编写日期:2026-08-21
> 适用范围:HotGo 管理后台、IM 服务端、IM 客户端动态配置、控制库、租户业务库
> 输入来源:`docs/im/IM-前后端对接文档.md`、`docs/im/IM-注册、邀请码与认证数据模型改造需求文档.md`、`docs/im/IM-HotGo注册短信开关与邀请码管理改造交接文档.md`、`docs/backstage/IM-后台需求文档.md`
## 1. 文档目标
本文从前后端对接协议、注册/邀请码/认证改造文档中,完整识别 IM 项目需要由后台管理系统承载的能力,包括:
1. 后台页面和业务管理模块。
2. 平台级、租户级开关和配置。
3. 后端统一维护、前端动态加载的字典枚举。
4. 定时任务、Outbox、异步投递、重试和补偿任务。
5. 客户端通过 `bootstrap`、配置接口、字典接口动态渲染的 UI 能力。
6. 权限、审计、日志脱敏、导出和安全边界。
本文是后台全量需求盘点,不替代具体接口设计和数据库迁移 SQL。后续开发时,应按 HotGo 项目规范拆成独立 CRUD 模块、配置模块、任务模块和权限菜单。
## 2. 基础原则
### 2.1 多租户边界
当前多租户采用“一个 HotGo 控制库 + 每租户一个 IM 独立业务库”方案。
| 数据类型 | 存储位置 | 是否带 `tenant_id` | 说明 |
| --- | --- | --- | --- |
| 租户元数据 | HotGo 控制库 | 是 | `im_tenant`、租户配置、平台审计、邀请码主数据 |
| 平台全局配置 | HotGo 控制库 | 否或按配置域区分 | 例如 `hg_sys_config.group=im` |
| 邀请码批次和主表 | HotGo 控制库 | 是 | 后台生成、查询、撤销、导出 |
| 租户用户、群、消息、文件、组织、工单 | 租户 IM 业务库 | 否 | 通过登录态租户上下文选择库,不在业务表里保存 `tenant_id` |
| 租户内使用事实和业务 Outbox | 租户 IM 业务库 | 否 | 例如 `im_registration_invite_uses`、`im_business_events_outbox` |
浏览器不能传入可信 `tenant_id`、`db_name`、`db_dsn`。平台管理员跨租户操作时,由后端基于登录身份和权限选择租户;租户管理员只能使用自身绑定的租户上下文。
### 2.2 HotGo 与 IM 服务职责
| 系统 | 职责 |
| --- | --- |
| HotGo 后台 | 菜单权限、后台页面、平台控制库、租户元数据、平台配置、邀请码管理、运维诊断、后台审计 |
| IM 服务端 | 客户端注册登录、实时消息、业务状态机、租户业务库事务、WebSocket、Outbox 消费、客户端动态配置下发 |
| 客户端 | 根据服务端返回的配置、权限、字典和策略动态渲染,不硬编码业务开关和枚举 |
原则上,租户内用户、群、文件、实名、举报、反馈等业务状态变更优先通过 IM 服务端管理 API 或受控业务通道完成,避免绕过 IM 的状态机和审计。当前注册邀请码和密码重置存在明确的 HotGo 直连数据库边界,必须严格按交接文档执行。
### 2.3 配置分层
| 层级 | 示例 | 管理入口 |
| --- | --- | --- |
| 平台全局配置 | 注册短信发送开关、短信服务商、对象存储默认配置、通用风控模板 | 平台后台 |
| 租户基础配置 | 租户状态、企业码、名称、数据库、配额、品牌、终端地址 | 平台后台,部分租户只读 |
| 租户业务策略 | 消息策略、文件策略、群治理、隐私默认值、工作台模块 | 企业后台 |
| 用户个人配置 | 隐私开关、青少年模式状态、设备推送 Token | 客户端为主,后台只读或安全处置 |
| 前端动态 UI 配置 | `bootstrap.features`、`settings_menu`、`workbench.modules`、原因字典 | IM 服务端返回,来源于后台配置和权限 |
## 3. 后台全量模块总览
| 模块 | 平台后台 | 企业后台 | 类型 | 优先级 |
| --- | --- | --- | --- | --- |
| 租户生命周期 | 是 | 否 | 管理页面、任务、审计 | P0 |
| 租户配置与配额 | 是 | 部分只读或申请变更 | 配置、开关、动态 UI | P0 |
| 注册短信开关 | 是 | 否 | 全局开关、审计 | P0 |
| 邀请码管理 | 平台可代管 | 是 | CRUD、导出、审计 | P0 |
| 管理员、角色、权限 | 是 | 是 | 权限、字典、审计 | P0 |
| 用户与账号安全 | 平台可运维 | 是 | 管理页面、Outbox | P0 |
| 组织架构 | 否 | 是 | CRUD、导入导出、动态 UI | P1 |
| 群治理 | 平台可审计 | 是 | 配置、管理页面、字典 | P1 |
| 消息策略与系统消息 | 平台模板 | 租户配置 | 配置、模板、任务 | P1 |
| 文件、收藏、搜索治理 | 平台运维 | 是 | 配置、任务、审计 | P1 |
| Push、短信、TURN、对象存储 | 是 | 租户专属配置 | 敏感配置、状态检查 | P1 |
| 工作台应用 | 平台模板 | 是 | 动态 UI 配置 | P2 |
| 会议 | 平台审计 | 是 | 管理页面、提醒任务 | P2 |
| 日报、月报 | 平台审计 | 是 | 模板、统计、提醒任务 | P2 |
| 总经理信箱 | 平台审计 | 是 | 配置、工单、动态文案 | P2 |
| 实名认证 | 平台审计 | 是 | 审核、策略、字典 | P2 |
| 青少年模式 | 平台模板 | 是 | 策略、动态文案、限制 | P2 |
| 反诈与内容审核 | 平台策略 | 租户策略与处置 | 风控、字典、审计 | P2 |
| 举报与反馈 | 平台可审计 | 是 | 原因字典、工单、导出 | P2 |
| App 版本更新 | 是 | 可按租户覆盖 | 配置、动态 UI | P2 |
| Outbox 与后台任务 | 是 | 只读本租户 | 运维、重试、死信 | P0 |
| 审计与日志脱敏 | 是 | 只读本租户 | 安全基础能力 | P0 |
## 4. 开关与配置清单
### 4.1 平台全局开关
| 配置键 | 类型 | 默认 | 管理权限 | 使用方 | 说明 |
| --- | --- | --- | --- | --- | --- |
| `im.register_sms_enabled` | bool | true | 平台超级管理员 | IM 注册发码接口 | 控制注册验证码是否真实发送;关闭时仍返回倒计时,不暴露给客户端 |
| `im.invite_batch_max_quantity` | int | 1000 | 平台管理员 | HotGo 邀请码管理 | 单批邀请码生成数量上限 |
| `im.invite_export_max_rows` | int | 待定 | 平台管理员 | HotGo 导出 | 防止明文邀请码大批量泄露 |
| `im.sms_provider_enabled` | bool | true | 平台管理员 | IM 发码服务 | 是否启用短信服务商,不等同于注册短信业务开关 |
| `im.audit_retention_days` | int | 待定 | 平台管理员 | 审计任务 | 平台审计保留期 |
| `im.outbox_retry_enabled` | bool | true | 平台运维 | Outbox 任务 | 是否自动重试平台/租户 Outbox |
`register_sms_enabled` 存在 HotGo `hg_sys_config`,真实值必须保存为 `1/0`。前端复用 `sys_switch` 时要显式映射:UI `1=开启`、`2=关闭`,不能把 `2` 直接写成 bool。
### 4.2 租户基础配置
| 配置 | 类型 | 是否动态下发 | 管理入口 | 说明 |
| --- | --- | --- | --- | --- |
| 租户状态 | enum | 是 | 平台后台 | `provisioning/active/suspended/deleting/deleted` |
| 企业码 | string | 登录前解析 | 平台后台 | 客户端 `tenants/resolve` 使用 |
| 租户名称、Logo、品牌色 | string/file | 是 | 平台后台 | 登录页、工作台、关于页展示 |
| `db_name/db_dsn` | string | 否 | 平台后台 | 只允许后端使用,不给浏览器 |
| 用户配额 | int | 是 | 平台后台 | 注册、导入用户、邀请加入时强校验 |
| 群数量配额 | int | 是 | 平台后台 | 建群时校验 |
| 群成员上限 | int | 是 | 平台/企业后台 | 建群、邀请、审批通过时校验 |
| 文件存储配额 | int64 | 是 | 平台/企业后台 | 上传、转发、收藏、群文件共用 |
| 终端 API 地址 | url | 登录前解析 | 平台后台 | HTTP、WebSocket、文件服务地址 |
| 可用登录方式 | array enum | 登录前解析 | 平台后台 | 密码、手机号、企业码等 |
| 密码策略 | json | 登录/注册/改密 | 平台/企业后台 | 长度、复杂度、历史密码、过期策略 |
| App 更新策略 | json | 是 | 平台后台 | platform/channel 维度版本策略 |
### 4.3 客户端 Bootstrap 动态配置
`GET /v1/im/bootstrap` 是客户端登录后的 UI 和能力裁剪总入口,后台必须为以下字段提供配置来源。
| 字段 | 后台配置项 | 客户端影响 |
| --- | --- | --- |
| `features.create_group` | 是否允许用户建群 | 隐藏或禁用建群入口 |
| `features.user_search` | 是否允许搜索用户 | 隐藏添加好友搜索入口 |
| `features.scan_qr` | 是否启用扫码 | 隐藏二维码/扫码入口 |
| `features.system_notifications` | 系统通知开关 | 展示系统通知 Topic |
| `features.message_recall` | 消息撤回开关 | 长按菜单是否显示撤回 |
| `features.message_edit` | 消息编辑开关 | 长按菜单是否显示编辑 |
| `features.organization` | 组织架构开关 | 工作台/通讯录组织入口 |
| `features.meetings` | 会议开关 | 工作台会议入口 |
| `features.daily_report/monthly_report` | 日报/月报开关 | 工作台报表入口 |
| `features.executive_mailbox` | 总经理信箱开关 | 工作台信箱入口 |
| `message_policy.recall_window_seconds` | 撤回时间窗口 | 超时隐藏操作 |
| `message_policy.edit_window_seconds` | 编辑时间窗口 | 超时隐藏操作 |
| `message_policy.supported_actions` | 消息操作集合 | 转发、收藏、复制、删除等 |
| `limits.max_file_size` | 单文件大小 | 上传前校验 |
| `limits.max_text_length` | 文本最大长度 | 输入框限制 |
| `limits.max_forward_targets` | 转发目标上限 | 多选转发限制 |
| `settings_menu` | 设置菜单配置 | iOS/Android 设置页动态入口 |
| `workbench.modules` | 工作台模块配置 | 工作台排序、图标、路由、角标 |
### 4.4 注册、认证与邀请码配置
| 配置 | 作用 | 后台入口 | 备注 |
| --- | --- | --- | --- |
| 注册短信发送 | 是否真实调用短信服务商 | 平台后台“IM 注册” | 全局开关 |
| 用户名正则 | 普通用户名规则 | 平台只读配置或代码常量 | 当前固定 `^[a-z0-9]{1,10}$` |
| 固定国家号 | 手机号补 `+86` | 平台只读配置 | 本期固定中国 |
| 邀请码长度和字符集 | 生成规则 | 平台只读配置 | 当前固定 5 位 `[A-Za-z]` |
| 邀请码批量上限 | 批量生成限制 | 平台后台 | 默认 1000 |
| 邀请码有效期 | 单批/单码字段 | 企业后台 | 创建时必填,必须晚于当前时间 |
| 邀请码明文查看权限 | 控制列表/详情是否显示明文 | 菜单权限 | 查看、查询、导出需独立审计 |
| 密码重置策略 | 管理员重置密码规则 | 企业后台 | 写 `security.password_reset` Outbox |
| 登录 Token 失效策略 | `auth_version` 校验 | 后台只读/运维 | 重置密码后清退旧会话 |
### 4.5 消息与会话策略
| 配置 | 类型 | 说明 |
| --- | --- | --- |
| 文本最大长度 | int | 输入框、服务端发送校验 |
| 消息撤回开关和时间窗 | bool/int | P2P、群聊均需服务端校验 |
| 消息编辑开关和时间窗 | bool/int | 编辑后产生版本或审计 |
| 多选转发目标上限 | int | 单选、多选、合并转发共用 |
| 支持消息操作 | array enum | copy、forward、favorite、recall、edit、delete |
| 已读回执策略 | enum/bool | 可受隐私和企业强制策略影响 |
| 链接识别与风险检测 | bool/json | URL 发送前后检查,触发反诈挑战 |
| 系统通知 Topic | string/json | 系统消息列表和角标 |
| 消息频控 | json | 防刷、风控、举报联动 |
### 4.6 通讯录、好友与隐私策略
| 配置 | 类型 | 客户端影响 |
| --- | --- | --- |
| 是否允许添加好友 | bool | 添加好友入口 |
| 是否允许精准账号搜索 | bool | 用户 ID/用户名/手机号搜索 |
| 添加我的方式 | enum array | 账号、手机号、二维码、群聊 |
| 好友申请验证策略 | enum | 直接添加、需要验证、禁止 |
| 黑名单能力 | bool | 黑名单入口和服务端拦截 |
| 资料可见性默认值 | enum | 主页字段裁剪 |
| 最近在线可见性 | enum | 会话状态展示 |
| 已读回执默认值 | bool | 会话回执 |
| 企业强制隐私字段 | json | `editable=false` 时前端禁用 |
### 4.7 群治理配置
| 配置 | 类型 | 说明 |
| --- | --- | --- |
| 是否允许建群 | bool | bootstrap 和建群接口 |
| 群成员上限 | int | 加群、邀请、审批事务内校验 |
| 入群策略 | enum | free、approval、invite_only |
| 成员邀请权限 | enum/bool | 群成员是否可邀请别人 |
| 成员上传文件 | bool | 群文件上传入口 |
| 成员私聊 | bool | 群成员主页是否允许发起会话 |
| 成员互加好友 | bool | 群资料页添加好友入口 |
| 全员禁言 | bool | 输入框禁用 |
| 成员禁言时长 | enum/int | 后台和群管理员使用 |
| 管理员数量上限 | int | 添加管理员时校验 |
| 群二维码有效期 | int | 二维码页和失效任务 |
| 群公告策略 | json | 发布、置顶、已读统计 |
### 4.8 文件、收藏与搜索策略
| 配置 | 类型 | 说明 |
| --- | --- | --- |
| 单文件大小上限 | int64 | 上传前后校验 |
| 允许 MIME/扩展名 | array | 图片、视频、语音、普通文件按 purpose 区分 |
| 文件 purpose 策略 | json | avatar、chat、group_file、report、abuse_evidence、feedback |
| 文件保留期 | int | 过期清理任务 |
| 孤儿文件清理时间窗 | int | 上传未绑定业务时清理 |
| 文件病毒扫描 | bool/json | 同步拦截或异步隔离 |
| 收藏总量/容量上限 | int/int64 | 收藏入口和服务端校验 |
| 搜索开关 | bool | 聊天记录、文件、链接搜索入口 |
| 搜索索引策略 | json | 重建、增量、失败重试 |
### 4.9 通话、Push 和外部服务配置
| 配置 | 类型 | 说明 |
| --- | --- | --- |
| 音视频通话开关 | bool | 呼叫入口 |
| 通话超时 | int | invite 超时时间 |
| TURN/STUN 服务器 | secret/json | WebRTC ICE 配置 |
| 通话记录保留期 | int | 后台查询和清理 |
| APNS/FCM 配置 | secret/json | 推送服务 |
| Push 锁屏隐私 | enum/bool | 通知内容是否显示明文 |
| Push 模板 | json | 系统消息、好友申请、会议提醒等 |
| 短信服务商配置 | secret/json | 验证码、通知短信 |
| 对象存储配置 | secret/json | 文件服务 |
| 外部 REST 插件配置 | secret/json | 扩展业务调用 |
### 4.10 办公协同配置
| 模块 | 配置 | 说明 |
| --- | --- | --- |
| 工作台 | 模块 code、名称、图标、路由、排序、启用、权限 | `GET /v1/im/workbench` 动态返回 |
| 组织架构 | 组织可见范围、是否允许跨部门搜索 | 通讯录和工作台组织入口 |
| 会议 | 是否启用、提醒提前量、可见范围、取消策略 | 会议通知和提醒任务 |
| 日报 | 模板、必填规则、最大长度、补交天数、提醒时间 | 普通用户和管理员统计 |
| 月报 | 模板、必填规则、最大长度、重开流程 | 月报统计和详情 |
| 总经理信箱 | 是否启用、匿名、必填字段、最大长度、附件策略、提示文案 | 文案必须服务端下发,不由客户端硬编码 |
### 4.11 安全、审核、举报与反馈配置
| 模块 | 配置 | 说明 |
| --- | --- | --- |
| 实名认证 | 是否强制、允许跳过、提醒周期、重复身份策略、审核字段 | 影响 `bootstrap.security.realname` |
| 青少年模式 | 策略版本、限制集合、提示文案、是否可启用 | 客户端确认时携带 `policy_version` |
| 反诈风控 | 高风险动作、提示文案、二次确认、阻断策略、Token TTL | 返回 `RISK_CONFIRMATION_REQUIRED` |
| 内容审核 | 审核模式、词库、插件、异步处置 | sync_block、async_hold、async_post_audit |
| 举报 | target_type 原因字典、证据规则、合并策略、限频 | `GET /v1/im/reports/abuse/reasons` |
| 反馈 | 类别、最大长度、联系方式、附件策略、诊断授权文案 | `GET /v1/im/feedback/config` |
### 4.12 App 更新配置
| 配置 | 类型 | 说明 |
| --- | --- | --- |
| platform | enum | ios、android、desktop 等 |
| channel | string/enum | stable、beta、enterprise |
| latest_version | string | 最新版本 |
| min_supported | string | 最低支持版本 |
| update_type | enum | none、optional、recommended、force |
| package_url/store_url | url | 安装来源 |
| release_notes | text | 更新说明 |
| remind_after_seconds | int | 非强制更新再次提醒间隔 |
## 5. 字典与枚举清单
后台新增枚举必须由后端统一注册字典,前端通过字典接口加载,不能在页面硬编码文本。
### 5.1 平台与租户
| 字典键建议 | 枚举 |
| --- | --- |
| `im_tenant_status` | provisioning、active、suspended、deleting、deleted |
| `im_tenant_plan` | free、standard、enterprise、custom |
| `im_tenant_config_scope` | platform、tenant、user |
| `im_operation_result` | success、failed、partial |
### 5.2 注册、认证、邀请码
| 字典键建议 | 枚举 |
| --- | --- |
| `im_register_sms_status` | enabled、disabled |
| `im_invite_status` | active、expired、revoked |
| `im_invite_action` | create、batch_create、view、query_exact、revoke、batch_revoke、export |
| `im_auth_scene` | register、login、forgot_password、bind_phone、close_youth_mode |
| `im_auth_channel` | sms、password、independent_password |
| `im_challenge_status` | pending、verified、expired、consumed、failed |
| `im_user_status` | normal、disabled、locked、deleted |
| `im_auth_level` | anonymous、authenticated、admin |
### 5.3 好友、群与消息
| 字典键建议 | 枚举 |
| --- | --- |
| `im_friend_request_status` | pending、accepted、rejected、expired、cancelled |
| `im_friend_add_method` | account、phone、qr、group |
| `im_profile_visibility` | public、friends、none |
| `im_group_role` | owner、admin、member |
| `im_group_join_policy` | free、approval、invite_only |
| `im_group_request_status` | pending、approved、rejected、expired |
| `im_group_mute_type` | all、member |
| `im_message_action` | copy、forward、favorite、recall、edit、delete、multi_select |
| `im_message_content_type` | text、image、audio、video、file、card、link、system |
| `im_read_state` | unread、read |
### 5.4 文件、搜索、通话
| 字典键建议 | 枚举 |
| --- | --- |
| `im_file_purpose` | chat、user_avatar、group_avatar、group_file、daily_report、monthly_report、abuse_evidence、feedback |
| `im_file_status` | uploaded、bound、quarantined、deleted、expired |
| `im_file_scan_status` | pending、clean、risky、failed |
| `im_search_index_status` | pending、indexed、failed、deleted |
| `im_call_type` | audio、video |
| `im_call_status` | ringing、accepted、declined、missed、cancelled、ended、timeout |
### 5.5 办公协同
| 字典键建议 | 枚举 |
| --- | --- |
| `im_workbench_module_status` | enabled、disabled |
| `im_meeting_status` | scheduled、cancelled、finished |
| `im_meeting_response` | pending、accepted、declined、tentative |
| `im_report_type` | daily、monthly |
| `im_report_status` | draft、submitted、reopened、archived |
| `im_mailbox_submission_status` | received、assigned、replied、closed |
### 5.6 安全、审核、举报、反馈
| 字典键建议 | 枚举 |
| --- | --- |
| `im_realname_status` | unverified、pending、approved、rejected |
| `im_youth_restriction` | night_quiet、content_filter、payment_guard、contact_limit |
| `im_risk_level` | low、medium、high |
| `im_risk_decision` | continue、cancel |
| `im_moderation_mode` | off、sync_block、async_hold、async_post_audit |
| `im_moderation_status` | pending、passed、blocked、removed |
| `im_abuse_target_type` | user、group、message |
| `im_abuse_report_status` | received、processing、resolved、rejected、merged |
| `im_abuse_reason` | fraud、harassment、pornography、violence、spam、other |
| `im_feedback_category` | bug、suggestion、complaint、other |
| `im_feedback_status` | received、processing、resolved、closed |
### 5.7 终端、外部服务与错误
| 字典键建议 | 枚举 |
| --- | --- |
| `im_app_platform` | ios、android、desktop、web |
| `im_app_channel` | stable、beta、enterprise |
| `im_update_type` | none、optional、recommended、force |
| `im_push_provider` | apns、fcm、vendor |
| `im_outbox_status` | pending、processing、done、failed、dead |
| `im_error_reason` | TENANT_UNAVAILABLE、TENANT_MISMATCH、AUTH_FAILED、TOKEN_EXPIRED、ACCOUNT_DISABLED、RATE_LIMITED、VALIDATION_FAILED、IDEMPOTENCY_CONFLICT、RESOURCE_NOT_FOUND、FORBIDDEN、FRIEND_RELATION_REQUIRED、MESSAGE_NOT_ALLOWED、GROUP_MUTED、GROUP_MEMBER_LIMIT_EXCEEDED、TENANT_USER_QUOTA_EXCEEDED、TENANT_GROUP_QUOTA_EXCEEDED、FILE_TOO_LARGE、FILE_FORBIDDEN、FILE_UNAVAILABLE、STORAGE_QUOTA_EXCEEDED、REALNAME_REQUIRED、YOUTH_MODE_RESTRICTED、RISK_CONFIRMATION_REQUIRED、RISK_ACTION_BLOCKED、REPORT_RATE_LIMITED |
## 6. 定时任务与异步任务
### 6.1 P0 基础任务
| 任务 | 触发方式 | 作用 | 后台能力 |
| --- | --- | --- | --- |
| 租户业务库初始化 | 创建租户后异步执行 | 创建库、执行 schema、初始化系统数据 | 进度、失败原因、重试 |
| 租户健康检查 | 定时 | 检查 DB 连接、schema version、基础 Topic | 状态看板、告警 |
| Outbox 消费状态巡检 | 定时 | 检查积压、失败、死信 | 列表、重试、死信 |
| `security.password_reset` 消费 | IM 轮询或 Kafka | 清退会话、拒绝旧 Token | 事件查询、重试 |
| 注册邀请码使用投影 | IM Outbox 或投影任务 | 将租户库 `invite_uses` 汇总到控制库主表 | 使用次数、最后使用时间、幂等补偿 |
| 审计日志脱敏与落库 | 同步/异步 | 保存操作审计,避免明文泄露 | 查询、导出、保留期 |
### 6.2 业务任务
| 任务 | 说明 |
| --- | --- |
| 邀请码过期状态刷新 | 邀请码可查询时实时判断过期;可增加定时任务做状态冗余和统计,不作为注册校验唯一依据 |
| 邀请码批量生成 | 大批量生成应可异步化,支持进度、失败重试、幂等键 |
| 文件孤儿清理 | 上传后未绑定业务的文件定期清理 |
| 文件过期清理 | 按 purpose 和保留期清理 |
| 病毒扫描和隔离处理 | 异步扫描结果更新文件状态并通知用户 |
| 搜索索引重建 | 消息、文件、链接索引的全量/增量重建 |
| 系统通知投递 | 系统消息、公告、风控提醒、会议提醒的 Push 和站内信投递 |
| Push 重试和死信 | 第三方推送失败后的重试、死信、凭证异常告警 |
| 会议提醒 | 按会议配置提醒参会人,支持提醒未响应者 |
| 日报/月报提醒 | 按租户配置提醒未提交者,生成统计 |
| 总经理信箱通知 | 新提交、分派、回复、关闭通知 |
| 实名审核超时提醒 | 提醒审核员处理 |
| 风控 challenge 过期 | 清理过期 challenge,记录取消/过期审计 |
| 举报和反馈 SLA 提醒 | 超时未处理提醒,支持工单状态流转 |
| App 更新配置缓存刷新 | 版本策略变更后刷新缓存 |
### 6.3 任务后台需求
1. 任务列表:任务名、租户、状态、进度、最近执行时间、耗时、错误原因。
2. 任务详情:输入参数、幂等键、关联审计、关联 Outbox、重试次数。
3. 操作:手动重试、暂停、恢复、标记死信、导出错误明细。
4. 安全:租户管理员只能看本租户任务;平台运维可跨租户查看但必须审计。
## 7. 前端 UI 动态配置需求
### 7.1 客户端动态 UI
客户端不能硬编码以下 UI 能力,必须由服务端返回:
1. 登录前:租户品牌、企业码解析结果、终端地址、认证方式、密码策略、App 更新策略。
2. 登录后:首页入口、`features`、`permissions`、`settings_menu`、工作台模块、系统通知 Topic。
3. 聊天页:可执行消息操作、撤回/编辑时间窗、上传限制、转发上限、已读回执策略。
4. 通讯录:组织架构入口、用户搜索入口、添加我的方式、黑名单入口。
5. 群聊:入群策略、群文件入口、群成员私聊、成员邀请、禁言状态、管理员入口。
6. 工作台:模块名称、图标、路由、排序、角标、启用状态和权限。
7. 日报/月报:模板、校验、补交天数、是否展示管理员统计。
8. 总经理信箱:提示文案、匿名说明、必填字段、附件规则。
9. 实名、青少年模式、反诈:策略版本、提示文案、限制集合、确认 Token。
10. 举报与反馈:原因、类别、最大长度、附件规则、诊断授权文案。
### 7.2 HotGo 管理后台动态 UI
HotGo 后台自身也需要动态化:
1. 平台管理员显示租户选择器;租户管理员隐藏租户选择器并固定当前租户。
2. 按权限显示按钮:邀请码明文查看、导出、撤销、密码重置、跨租户查看必须独立权限。
3. 表格状态、筛选下拉、表单选项必须从后端字典加载。
4. 敏感配置字段只允许写入,不默认回显明文;回显使用脱敏值和“已配置”状态。
5. 导出前必须展示范围、数量、敏感字段提示,二次确认并写审计。
6. 表单提交不接受浏览器传入 `tenant_id/db_name/db_dsn` 作为可信路由依据。
## 8. 后台页面需求
### 8.1 平台后台
| 页面 | 核心能力 |
| --- | --- |
| 租户管理 | 创建、初始化、启停、删除流程、配置 DB、查看用量 |
| 租户配置 | 配额、品牌、终端地址、登录方式、密码策略、功能开关 |
| IM 注册设置 | 注册短信发送开关、批量邀请码上限、导出限制 |
| 平台管理员 | 平台账号、角色、权限、二次认证 |
| 外部服务配置 | SMS、Push、TURN、对象存储、审核插件 |
| App 版本 | 按 platform/channel 配置版本更新策略 |
| 运维看板 | 租户健康、Outbox、任务、缓存、连接状态 |
| 平台审计 | 跨租户操作、敏感配置变更、导出记录 |
### 8.2 企业后台
| 页面 | 核心能力 |
| --- | --- |
| 邀请码管理 | 单个生成、批量生成、列表、精确查询、批次详情、撤销、导出、使用记录 |
| 企业管理员 | 添加管理员、设置角色、移除管理员、权限范围 |
| 用户管理 | 用户列表、状态、资料、重置密码、撤销会话、设备、安全事件 |
| 组织架构 | 部门树、成员、移动、导入导出、组织权限 |
| 群管理 | 群列表、详情、成员、管理员、禁言、转让群主、解散、群文件 |
| 消息与系统通知 | 模板、公告、系统通知、投递记录 |
| 文件治理 | 文件列表、用途、大小、状态、隔离、删除、下载审计 |
| 搜索治理 | 索引状态、重建、失败明细 |
| 工作台 | 模块配置、排序、图标、角标来源、权限绑定 |
| 会议 | 列表、详情、取消、提醒未响应者、统计 |
| 日报/月报 | 模板、统计、明细、提醒、重开流程 |
| 总经理信箱 | 配置、提交列表、分派、回复、关闭 |
| 实名审核 | 提交列表、详情、通过、拒绝、策略 |
| 青少年模式 | 策略、文案、用户状态、独立密码重置 |
| 反诈与审核 | 风控策略、challenge、内容审核队列、处置 |
| 举报工单 | 原因配置、举报列表、证据、处置、合并 |
| 意见反馈 | 类别配置、反馈列表、诊断附件、处理状态 |
| 租户审计 | 本租户后台操作、敏感查看、导出、业务处置 |
## 9. 数据与接口边界
### 9.1 控制库建议承载
| 表或配置 | 说明 |
| --- | --- |
| `im_tenant` | 租户元数据、状态、企业码、DB 路由 |
| `im_tenant_config` | 租户配置、配额、功能开关、品牌 |
| `hg_sys_config` | 平台全局配置,例如注册短信发送 |
| `im_registration_invite_batches` | 邀请码批次 |
| `im_registration_invite_codes` | 邀请码主表、明文码、状态、使用统计投影 |
| `im_admin_operation_audit` | IM 后台业务审计 |
| `hg_admin_member.tenant_id` | 后台管理员绑定 IM 租户 |
| 平台 Outbox/任务表 | 平台任务、投递、重试、死信 |
### 9.2 租户业务库建议承载
| 表 | 说明 |
| --- | --- |
| `im_users` | 用户主数据,目标包含 `username` 非空唯一、`phone` 可空唯一 |
| `im_auth` | 只按 `userid` 保存 Basic 密码凭据,删除 `scheme/uname` |
| `im_user_security` | `auth_version`、安全状态 |
| `im_registration_invite_uses` | 邀请码使用事实,只由 IM 注册事务写 |
| `im_business_events_outbox` | 租户内业务事件,例如密码重置、邀请码使用 |
| 用户、组织、群、文件、会议、报表、信箱、实名、举报、反馈业务表 | 不带 `tenant_id`,由选库隔离 |
### 9.3 注册认证迁移后台需求
1. 增加迁移状态看板:每个租户当前 schema version、迁移批次、成功/失败数。
2. 存量用户 username 回填报告:冲突、非法字符、保留账号、自动生成结果。
3. phone 规范化报告:可转为 `+86`、重复冲突、格式异常。
4. `im_auth` 双读/切换/清理阶段状态展示。
5. 迁移前后登录回归数据:用户名登录、手机号登录、忘记密码、管理员重置密码。
6. 失败租户支持重试和导出错误明细。
## 10. 权限、审计与日志脱敏
### 10.1 权限点
P0 必须独立拆分以下权限:
1. 注册短信开关读取、修改。
2. 邀请码列表、创建、批量创建、明文查看、精确查询、撤销、批量撤销、导出。
3. 租户创建、启停、DB 配置查看、DB 配置修改。
4. 用户查看、状态修改、重置密码、撤销会话、设备查看。
5. 审计查看、审计导出。
6. Outbox 查看、重试、标记死信。
7. 外部服务敏感配置查看状态、修改。
### 10.2 审计范围
必须审计:
1. 注册短信开关变更。
2. 邀请码生成、批量生成、明文查询/查看、导出、撤销。
3. 管理员重置密码、撤销会话、禁用用户。
4. 租户创建、启停、删除流程、DB 路由配置变更。
5. 群管理员变更、禁言、转让群主、解散群。
6. 文件下载敏感证据、隔离、删除。
7. 实名审核、举报处置、反馈处理。
8. 风控挑战确认、取消、阻断。
9. 任何跨租户查看、导出和代操作。
审计字段至少包含:`request_id`、`tenant_id`、`operator_id`、`operator_role`、`action`、`target_type`、`target_id`、`result`、`ip`、`user_agent`、`detail_json`、`created_at`。
### 10.3 日志脱敏
访问日志、错误日志、审计摘要、导出任务日志不得记录以下明文:
1. 邀请码、验证码、密码、独立密码。
2. `Authorization`、`serviceSecret`、短信/Push/对象存储密钥。
3. `db_dsn`、数据库密码。
4. 身份证件、实名原始图片、敏感证据文件临时 URL。
字段名至少覆盖:`inviteCode`、`invite_code`、`code`、`verification_code`、`password`、`secret`、`serviceSecret`、`service_secret`、`authorization`、`db_dsn`。
## 11. 验收清单
### 11.1 P0
1. 平台管理员可管理租户生命周期、配置和配额,租户管理员无法越权。
2. 每个租户业务库独立,租户业务表不依赖 `tenant_id` 过滤。
3. 注册短信开关只有平台超级管理员可改,值保存为 `1/0`,IM 服务缓存期内生效。
4. 邀请码支持单个、批量、列表、精确查询、撤销、导出和使用统计。
5. 邀请码明文查看、查询、导出都有独立权限和审计。
6. 管理员重置密码写入 `im_auth`、递增 `auth_version`、写 `security.password_reset` Outbox。
7. `bootstrap` 能返回功能开关、消息策略、限制、设置菜单、工作台基础配置。
8. `hg_sys_log` 和业务日志不会泄露邀请码、密码、验证码、Token、密钥和 DSN。
### 11.2 P1
1. 用户、组织、群治理、文件治理、系统通知、Push/SMS/TURN/对象存储配置可后台管理。
2. 群成员上限、用户配额、文件配额、消息策略在并发事务中强校验。
3. 文件清理、Outbox 重试、Push 重试、搜索索引任务可观察、可重试。
4. 前端所有状态、下拉、按钮权限从字典和权限接口动态加载。
### 11.3 P2
1. 工作台、会议、日报、月报、总经理信箱配置能动态影响客户端。
2. 实名、青少年模式、反诈、举报、反馈具备策略配置、工单处理、审计。
3. App 更新策略按 platform/channel 下发,异常时不误触发强制更新。
4. 敏感证据、实名资料、诊断包具备权限、保留期和下载审计。
## 12. 已发现冲突与待确认项
1. 注册规则存在文档冲突:`IM-注册、邀请码与认证数据模型改造需求文档.md` 定义用户名和手机号是独立字段,二者同时填写时都保存;`IM-HotGo注册短信开关与邀请码管理改造交接文档.md` 的 3.1 仍写“手机号非空时忽略用户名”。建议以新的注册模型文档为准,并同步修正交接文档。
2. 邀请码批次和主表目前按交接文档放在 HotGo 控制库,带 `tenant_id`;邀请码使用事实放在租户业务库。该设计不违反“租户业务表不带 tenant_id”,但需要在实现中明确控制库和业务库边界。
3. 邀请码过期是否需要物理状态刷新任务待定。注册校验必须实时判断 `expires_at`,定时任务只能做冗余状态和统计。
4. 注册短信开关当前是平台全局配置;未来若需要企业级差异,需迁移到 `im_tenant_config` 并定义优先级。
5. 当前交接文档允许 HotGo 对部分租户库直连操作;长期是否统一收敛到 IM 管理 API 或 Kafka,需要在运维网络条件稳定后再评审。
6. 开发/测试环境固定验证码 `123456` 不能进入生产;生产必须使用强随机验证码和真实短信服务商响应。
@@ -0,0 +1,418 @@
# IM 后台菜单覆盖测试文档
## 1. 测试范围
本文档用于覆盖 IM 后台菜单从「组织架构」开始,沿左侧菜单向下逐个页面点击验证。
当前覆盖菜单顺序:
1. 组织架构
2. 文件治理
3. 注册邀请码
4. 文件策略
5. 通知模板
6. 工作台配置
7. 外部服务配置
8. 版本更新
9. 举报原因
10. 总经理信箱配置
11. 意见反馈配置
12. 任务看板
13. Kafka 命令
14. Kafka 死信
15. Kafka 审计事件
## 2. 测试环境
| 项目 | 地址 |
| --- | --- |
| 前端 | `http://127.0.0.1:8001/` |
| 后端 | `http://127.0.0.1:8000/admin` |
| 登录账号 | 使用具备 IM 管理权限的后台账号 |
| 浏览器 | Chrome,打开 DevTools Network 和 Console |
## 3. 通用检查项
每进入一个菜单页面,都先执行以下检查:
| 编号 | 检查项 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| COMMON-001 | 点击左侧菜单 | URL、页签、面包屑、页面标题正确变化 | 待测 |
| COMMON-002 | 页面首次加载 | 无白屏,无永久 loading,无明显样式错乱 | 待测 |
| COMMON-003 | Console | 无新的 JS Error,例如组件方法不存在、字典方法不存在 | 待测 |
| COMMON-004 | Network | 页面主列表接口 HTTP 200,业务响应 `code=0` | 待测 |
| COMMON-005 | 查询按钮 | 点击后列表刷新,请求参数包含搜索表单值 | 待测 |
| COMMON-006 | 重置按钮 | 搜索条件清空,列表回到默认查询 | 待测 |
| COMMON-007 | 展开/收起 | 搜索区域展开、收起正常,不遮挡表格 | 待测 |
| COMMON-008 | 表格刷新图标 | 点击后重新请求列表接口 | 待测 |
| COMMON-009 | 表格密度/列设置 | 可打开设置,切换后表格仍正常显示 | 待测 |
| COMMON-010 | 分页 | 切换页码和每页数量,请求参数正确,页面不报错 | 待测 |
说明:涉及删除、撤销、重放、清理等副作用操作时,优先使用测试数据。没有测试数据时,至少验证弹窗、参数校验、取消流程;正式执行前确认不会影响线上有效数据。
## 4. 组织架构
菜单路径:`IM管理 / 组织架构`
主要区域:部门列表、成员列表。
### 4.1 搜索与列表
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| ORG-001 | 打开「组织架构」 | 页面标题显示「组织架构」,部门列表和成员列表正常渲染 | 待测 |
| ORG-002 | 选择租户后查询部门 | 部门列表请求成功,显示部门 ID、部门名称、状态、成员数等字段 | 待测 |
| ORG-003 | 输入部门名称、状态后点击查询 | 部门列表按条件刷新 | 待测 |
| ORG-004 | 输入成员关键词、在职状态后点击查询 | 成员列表按条件刷新 | 待测 |
| ORG-005 | 点击部门行「成员」 | 成员列表切换到当前部门范围,添加成员按钮可用 | 待测 |
### 4.2 部门按钮
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| ORG-010 | 点击「新增部门」 | 打开部门编辑弹窗 | 待测 |
| ORG-011 | 新增部门弹窗点击「取消」 | 弹窗关闭,列表不刷新或数据不变化 | 待测 |
| ORG-012 | 新增部门弹窗直接点击「保存」 | 必填项校验正常展示 | 待测 |
| ORG-013 | 填写部门名称、上级部门、排序、状态后点击「保存」 | 保存成功,部门列表刷新,新部门出现 | 待测 |
| ORG-014 | 点击部门行「编辑」 | 打开编辑弹窗,已有数据正确回填 | 待测 |
| ORG-015 | 修改部门信息后点击「保存」 | 保存成功,列表展示新值 | 待测 |
| ORG-016 | 点击部门行「删除」再点取消 | 弹窗关闭,部门不删除 | 待测 |
| ORG-017 | 点击部门行「删除」并确认 | 删除成功,列表刷新;有子部门或成员时应给出清晰错误 | 待测 |
| ORG-018 | 点击「导出部门」 | 触发文件下载,导出条件与当前查询一致 | 待测 |
### 4.3 成员按钮
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| ORG-020 | 未选择部门时观察「添加成员」 | 按钮禁用或提示先选择部门 | 待测 |
| ORG-021 | 选择部门后点击「添加成员」 | 打开成员编辑弹窗,部门信息默认带入 | 待测 |
| ORG-022 | 成员弹窗直接点击「保存」 | 必填项校验正常展示 | 待测 |
| ORG-023 | 填写用户 ID、岗位、排序、可联系等信息后保存 | 保存成功,成员列表刷新 | 待测 |
| ORG-024 | 点击成员行「编辑」 | 打开编辑弹窗,成员信息正确回填 | 待测 |
| ORG-025 | 修改成员信息后保存 | 保存成功,列表展示新值 | 待测 |
| ORG-026 | 点击成员行「移除」再取消 | 弹窗关闭,成员仍保留 | 待测 |
| ORG-027 | 点击成员行「移除」并确认 | 移除成功,成员列表刷新 | 待测 |
| ORG-028 | 点击「导出成员」 | 触发文件下载,导出条件与当前查询一致 | 待测 |
## 5. 文件治理
菜单路径:`IM管理 / 文件治理`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| FILE-001 | 打开页面 | 文件列表正常展示,包含文件 ID、状态、用途、上传用户、MIME、大小、引用数等字段 | 待测 |
| FILE-002 | 按租户、文件 ID、上传用户、状态、用途、MIME、Topic、更新时间查询 | 列表按条件刷新 | 待测 |
| FILE-003 | 点击行操作「详情」 | 打开详情弹窗,展示文件基础信息和引用列表 | 待测 |
| FILE-004 | 详情弹窗关闭 | 弹窗正常关闭,列表状态不丢失 | 待测 |
| FILE-005 | 点击行操作「隔离」再取消 | 确认框正常,取消后文件状态不变 | 待测 |
| FILE-006 | 点击行操作「隔离」并确认 | 文件状态变为隔离或列表刷新后状态正确 | 待测 |
| FILE-007 | 点击行操作「删除」再取消 | 确认框正常,取消后文件不删除 | 待测 |
| FILE-008 | 点击行操作「删除」并确认 | 删除成功,列表刷新 | 待测 |
| FILE-009 | 点击「清理孤儿文件」再取消 | 确认框正常,取消后不触发清理 | 待测 |
| FILE-010 | 点击「清理孤儿文件」并确认 | 清理请求成功,页面给出成功或结果提示 | 待测 |
## 6. 注册邀请码
菜单路径:`IM管理 / 注册邀请码`
实际菜单路由:`/#/im/registrationInvite`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| INVITE-001 | 打开页面 | 邀请码列表正常展示,包含租户、邀请码、状态、批次 ID、到期时间、使用次数等字段 | 待测 |
| INVITE-002 | 平台管理员不选择租户点击「添加」 | 提示先选择租户 | 待测 |
| INVITE-003 | 选择租户后点击「添加」 | 打开生成邀请码弹窗 | 待测 |
| INVITE-004 | 生成弹窗点击「取消」 | 弹窗关闭,列表不变化 | 待测 |
| INVITE-005 | 生成弹窗清空到期时间后点击「确认」 | 表单校验提示请选择到期时间 | 待测 |
| INVITE-006 | 填写到期时间、备注后点击「确认」 | 生成成功,列表刷新并出现新邀请码 | 待测 |
| INVITE-007 | 点击「批量生成」 | 打开批量生成弹窗,包含生成数量、到期时间、备注 | 待测 |
| INVITE-008 | 批量生成数量小于 1 或大于 1000 | 表单校验拦截 | 待测 |
| INVITE-009 | 输入合法数量后确认 | 批量生成成功,列表出现同一批次的邀请码 | 待测 |
| INVITE-010 | 按邀请码、状态、批次 ID、创建人、创建时间、到期时间查询 | 列表按条件刷新 | 待测 |
| INVITE-011 | 点击行操作「批次」 | 打开批次详情弹窗,展示总数、有效、撤销、过期、使用次数和明细 | 待测 |
| INVITE-012 | 点击有效邀请码行操作「撤销」再取消 | 确认框正常,取消后状态不变 | 待测 |
| INVITE-013 | 点击有效邀请码行操作「撤销」并确认 | 撤销成功,状态变为已撤销 | 待测 |
| INVITE-014 | 点击有效批次行操作「撤销批次」再取消 | 确认框正常,取消后批次状态不变 | 待测 |
| INVITE-015 | 点击有效批次行操作「撤销批次」并确认 | 批次内有效邀请码变为已撤销 | 待测 |
| INVITE-016 | 点击「导出」 | 下载 CSV 文件,内容与当前筛选条件一致 | 待测 |
## 7. 文件策略
菜单路径:`IM管理 / 文件策略`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| FILE-POLICY-001 | 打开页面 | 策略列表正常展示,包含用途、大小上限、保留天数、自动清理、病毒扫描、MIME 白名单等字段 | 待测 |
| FILE-POLICY-002 | 按租户、用途、自动清理、病毒扫描查询 | 列表按条件刷新 | 待测 |
| FILE-POLICY-003 | 点击行操作「编辑」 | 打开编辑弹窗,字段正确回填 | 待测 |
| FILE-POLICY-004 | 编辑弹窗点击「取消」 | 弹窗关闭,数据不变化 | 待测 |
| FILE-POLICY-005 | 修改大小上限、保留天数、开关、MIME 白名单后保存 | 保存成功,列表刷新并展示新值 | 待测 |
| FILE-POLICY-006 | 输入非法大小或保留天数后保存 | 表单校验或后端错误提示清晰 | 待测 |
## 8. 通知模板
菜单路径:`IM管理 / 通知模板`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| NOTIFY-001 | 打开页面 | 模板列表正常展示,包含场景、状态、站内、Push、短信、标题模板、内容模板等字段 | 待测 |
| NOTIFY-002 | 按租户、场景、状态查询 | 列表按条件刷新 | 待测 |
| NOTIFY-003 | 点击行操作「编辑」 | 打开编辑弹窗,模板字段正确回填 | 待测 |
| NOTIFY-004 | 编辑弹窗点击「取消」 | 弹窗关闭,数据不变化 | 待测 |
| NOTIFY-005 | 修改标题模板、内容模板、站内/Push/短信开关后保存 | 保存成功,列表刷新并展示新值 | 待测 |
| NOTIFY-006 | 清空必填字段后保存 | 表单校验或后端错误提示清晰 | 待测 |
## 9. 工作台配置
菜单路径:`IM管理 / 工作台配置`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| WORKBENCH-001 | 打开页面 | 工作台模块列表正常展示,包含编码、名称、路由、排序、图标 ID、角标 Key、状态、权限等字段 | 待测 |
| WORKBENCH-002 | 按租户、编码、名称、状态查询 | 列表按条件刷新 | 待测 |
| WORKBENCH-003 | 点击「新增模块」 | 打开模块编辑弹窗 | 待测 |
| WORKBENCH-004 | 新增弹窗点击「取消」 | 弹窗关闭,列表不变化 | 待测 |
| WORKBENCH-005 | 新增弹窗直接保存 | 必填项校验正常展示 | 待测 |
| WORKBENCH-006 | 填写编码、名称、路由、排序、状态等信息后保存 | 保存成功,列表刷新 | 待测 |
| WORKBENCH-007 | 点击行操作「编辑」 | 打开编辑弹窗,字段正确回填 | 待测 |
| WORKBENCH-008 | 修改模块信息后保存 | 保存成功,列表展示新值 | 待测 |
| WORKBENCH-009 | 点击行操作「禁用」再取消 | 确认框正常,取消后状态不变 | 待测 |
| WORKBENCH-010 | 点击行操作「禁用」并确认 | 禁用成功,列表刷新后状态正确 | 待测 |
| WORKBENCH-011 | 点击「保存排序」 | 排序保存成功;列表刷新后顺序保持一致 | 待测 |
## 10. 外部服务配置
菜单路径:`IM管理 / 外部服务配置`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| EXT-CONFIG-001 | 打开页面 | 配置列表正常展示,包含服务、服务商、端点、启用、配置状态、敏感配置、检查结果等字段 | 待测 |
| EXT-CONFIG-002 | 按租户、服务、服务商、启用状态查询 | 列表按条件刷新 | 待测 |
| EXT-CONFIG-003 | 点击行操作「编辑」 | 打开编辑弹窗,字段正确回填 | 待测 |
| EXT-CONFIG-004 | 编辑弹窗切换「启用」开关 | 开关状态可变更,保存前不应提前提交 | 待测 |
| EXT-CONFIG-005 | 修改服务商、端点、配置内容后保存 | 保存成功,列表刷新并展示新值 | 待测 |
| EXT-CONFIG-006 | 点击行操作「检查」再取消 | 确认框正常,取消后不触发检查 | 待测 |
| EXT-CONFIG-007 | 点击行操作「检查」并确认 | 检查请求成功,检查结果和检查时间刷新 | 待测 |
## 11. 版本更新
菜单路径:`IM管理 / 版本更新`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| RELEASE-001 | 打开页面 | 版本策略列表正常展示,包含平台、渠道、最新版本、最低支持、更新类型、下载地址等字段 | 待测 |
| RELEASE-002 | 按租户、平台、渠道查询 | 列表按条件刷新 | 待测 |
| RELEASE-003 | 点击「新增策略」 | 打开策略编辑弹窗 | 待测 |
| RELEASE-004 | 新增弹窗直接保存 | 必填项校验正常展示 | 待测 |
| RELEASE-005 | 填写平台、渠道、版本号、更新类型、地址、发布时间后保存 | 保存成功,列表刷新 | 待测 |
| RELEASE-006 | 点击行操作「编辑」 | 打开编辑弹窗,字段正确回填 | 待测 |
| RELEASE-007 | 修改版本策略后保存 | 保存成功,列表展示新值 | 待测 |
| RELEASE-008 | 输入非法版本号或 URL 后保存 | 表单校验或后端错误提示清晰 | 待测 |
## 12. 举报原因
菜单路径:`IM管理 / 举报原因`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| ABUSE-001 | 打开页面 | 原因列表正常展示,包含对象、编码、文案、要求描述、允许证据、排序、状态等字段 | 待测 |
| ABUSE-002 | 按租户、对象、编码、状态查询 | 列表按条件刷新 | 待测 |
| ABUSE-003 | 点击「新增原因」 | 打开原因编辑弹窗 | 待测 |
| ABUSE-004 | 新增弹窗直接保存 | 必填项校验正常展示 | 待测 |
| ABUSE-005 | 填写对象、编码、文案、排序、状态等信息后保存 | 保存成功,列表刷新 | 待测 |
| ABUSE-006 | 点击行操作「编辑」 | 打开编辑弹窗,字段正确回填 | 待测 |
| ABUSE-007 | 修改原因配置后保存 | 保存成功,列表展示新值 | 待测 |
| ABUSE-008 | 点击行操作「禁用」再取消 | 确认框正常,取消后状态不变 | 待测 |
| ABUSE-009 | 点击行操作「禁用」并确认 | 禁用成功,列表刷新后状态正确 | 待测 |
## 13. 总经理信箱配置
菜单路径:`IM管理 / 总经理信箱配置`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| MAILBOX-001 | 打开页面 | 配置表单正常展示,租户选择框可用 | 待测 |
| MAILBOX-002 | 选择租户 | 自动加载该租户信箱配置 | 待测 |
| MAILBOX-003 | 点击「刷新」 | 当前租户配置重新加载,表单无异常 | 待测 |
| MAILBOX-004 | 未选择租户时观察「保存」 | 按钮禁用或提示先选择租户 | 待测 |
| MAILBOX-005 | 修改启用状态、接收人、处理策略等配置后点击「保存」 | 保存成功,再次刷新后配置保持一致 | 待测 |
| MAILBOX-006 | 输入非法配置后保存 | 表单校验或后端错误提示清晰 | 待测 |
## 14. 意见反馈配置
菜单路径:`IM管理 / 意见反馈配置`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| FEEDBACK-001 | 打开页面 | 配置表单正常展示,租户选择框可用 | 待测 |
| FEEDBACK-002 | 选择租户 | 自动加载该租户反馈配置 | 待测 |
| FEEDBACK-003 | 点击「刷新」 | 当前租户配置重新加载,表单无异常 | 待测 |
| FEEDBACK-004 | 未选择租户时观察「保存」 | 按钮禁用或提示先选择租户 | 待测 |
| FEEDBACK-005 | 修改启用状态、分类、处理人、限制策略等配置后点击「保存」 | 保存成功,再次刷新后配置保持一致 | 待测 |
| FEEDBACK-006 | 输入非法配置后保存 | 表单校验或后端错误提示清晰 | 待测 |
## 15. 任务看板
菜单路径:`IM管理 / 任务看板`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| TASK-001 | 打开页面 | 看板列表正常展示,包含租户、企业码、状态、DB、业务待投递、业务失败、群待投递、群失败、孤儿文件、待物理清理等字段 | 待测 |
| TASK-002 | 按租户、状态查询 | 列表按条件刷新 | 待测 |
| TASK-003 | 查看建议动作字段 | 异常租户展示可理解的建议动作;正常租户展示正常或为空 | 待测 |
| TASK-004 | 刷新表格 | 统计数据重新加载,无接口报错 | 待测 |
## 16. Kafka 命令
菜单路径:`IM管理 / Kafka命令`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| KAFKA-CMD-001 | 打开页面 | 命令列表正常展示,包含租户、命令 ID、事件类型、业务类型、业务 ID、状态、错误码、消息等字段 | 待测 |
| KAFKA-CMD-002 | 按租户、命令 ID、事件 ID、事件类型、业务 ID、状态、创建时间查询 | 列表按条件刷新 | 待测 |
| KAFKA-CMD-003 | 点击「发送缓存失效测试」 | 请求成功,新增一条缓存失效命令或页面提示成功 | 待测 |
| KAFKA-CMD-004 | 点击「发送工作台测试事件」 | 请求成功,新增一条工作台测试事件命令或页面提示成功 | 待测 |
| KAFKA-CMD-005 | 点击行操作「详情」 | 打开详情弹窗,展示命令 payload、结果、错误原因等信息 | 待测 |
| KAFKA-CMD-006 | 详情弹窗点击「关闭」 | 弹窗正常关闭 | 待测 |
| KAFKA-CMD-007 | 发送测试事件后等待消费结果 | 状态从待发送/已发送变为成功或失败,失败时错误信息可读 | 待测 |
## 17. Kafka 死信
菜单路径:`IM管理 / Kafka死信`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| DEAD-001 | 打开页面 | 死信列表正常展示,包含死信 ID、事件 ID、来源 Topic、消费组、事件类型、业务类型、业务 ID、状态等字段 | 待测 |
| DEAD-002 | 按租户、状态、事件类型、业务类型、业务 ID 查询 | 列表按条件刷新 | 待测 |
| DEAD-003 | 点击行操作「详情」 | 打开详情弹窗,展示死信 payload、错误原因、处理信息 | 待测 |
| DEAD-004 | 详情弹窗点击「关闭」 | 弹窗正常关闭 | 待测 |
| DEAD-005 | 详情弹窗点击「忽略」 | 操作成功,状态变为已忽略或列表刷新后状态正确 | 待测 |
| DEAD-006 | 详情弹窗点击「标记已处理」 | 操作成功,状态变为已处理 | 待测 |
| DEAD-007 | 详情弹窗点击「重放」 | 操作成功,重放次数增加,状态或处理结果正确更新 | 待测 |
| DEAD-008 | 对已处理、已忽略死信重复操作 | 系统应拦截或给出清晰提示,不出现脏状态 | 待测 |
## 18. Kafka 审计事件
菜单路径:`IM管理 / Kafka审计事件`
| 编号 | 操作 | 预期结果 | 结果 |
| --- | --- | --- | --- |
| KAFKA-AUDIT-001 | 打开页面 | 审计事件列表正常展示,包含租户、事件 ID、事件类型、动作、资源、资源 ID、操作者、结果等字段 | 待测 |
| KAFKA-AUDIT-002 | 按租户编码、事件 ID、事件类型、动作、资源、资源 ID、结果、接收时间查询 | 列表按条件刷新 | 待测 |
| KAFKA-AUDIT-003 | 点击行操作「详情」 | 打开详情弹窗,展示事件详情、请求上下文、结果信息 | 待测 |
| KAFKA-AUDIT-004 | 详情弹窗点击「关闭」 | 弹窗正常关闭 | 待测 |
## 19. 测试数据建议
| 数据类型 | 建议准备 |
| --- | --- |
| 租户 | 至少 1 个正常租户,具备独立业务库 |
| 组织架构 | 至少 1 个根部门、1 个子部门、2 个用户成员 |
| 文件治理 | 至少 1 个正常文件、1 个可隔离文件、1 个孤儿文件 |
| 注册邀请码 | 至少 1 个有效邀请码、1 个过期邀请码、1 个批次邀请码 |
| Kafka | Kafka、消费者、IM Server 正常运行;准备 1 条可产生成功结果的命令和 1 条可产生死信的命令 |
| 配置类 | 文件策略、通知模板、工作台、外部服务、版本更新、举报原因均有初始数据 |
## 20. 缺陷记录模板
| 缺陷编号 | 菜单 | 操作步骤 | 实际结果 | 预期结果 | Console/Network 信息 | 严重级别 | 状态 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| BUG-001 | | | | | | | 待处理 |
## 21. 第一轮执行记录
执行时间:2026-09-03 15:16 - 15:22
执行环境:
| 项目 | 值 |
| --- | --- |
| 前端 | `http://127.0.0.1:8001/` |
| 后端 | Docker 容器 `im-local-admin`,端口 `8000` |
| MySQL | Docker 容器 `relaxed_cori`,库 `hotgo`、`tinode`、`tinode_test` |
| Redis | Docker 容器 `brave_blackburn` |
| Kafka | Docker 容器 `kafka` |
本轮说明:
1. 从左侧菜单「组织架构」开始,按菜单顺序逐页点击。
2. 对查询、重置、展开、刷新、新增、编辑、详情等非破坏性按钮进行了点击验证。
3. 对删除、隔离、撤销、禁用、重放等有副作用的按钮,本轮只验证入口和取消流程,不确认执行。
4. 「收起」按钮在自动化脚本里先于「展开」检测,产生过误报,本轮不作为缺陷记录。
5. 「注册邀请码」菜单点击正常,真实路由为 `/#/im/registrationInvite`;直接访问 `/#/im/registrationInvite/index` 会进入 ErrorPage,测试文档已修正。
### 21.1 页面通过项
| 菜单 | 本轮结果 |
| --- | --- |
| 组织架构 | 页面可打开,查询/重置/展开/新增部门可点击;添加成员因未先选择部门处于禁用,符合预期 |
| 文件治理 | 页面可打开,查询/重置/展开/详情可点击;列表有数据 |
| 注册邀请码 | 通过左侧菜单可打开,查询/重置/展开/添加/批量生成/导出可见;列表有 2 条历史数据 |
| 工作台配置 | 页面可打开,查询/重置/展开/新增模块/编辑可点击;禁用弹窗可打开并取消 |
| 版本更新 | 页面可打开,查询/重置/展开/新增策略/编辑可点击;列表有数据 |
| 举报原因 | 页面可打开,查询/重置/展开/新增原因/编辑可点击;禁用弹窗可打开并取消 |
| 总经理信箱配置 | 页面可打开,保存/刷新按钮可见,刷新可点击 |
| 意见反馈配置 | 页面可打开,保存/刷新按钮可见,刷新可点击 |
| 任务看板 | 页面可打开,查询/重置/展开可点击 |
### 21.2 本轮发现的问题
| 缺陷编号 | 菜单 | 操作步骤 | 实际结果 | 预期结果 | 证据 | 严重级别 | 状态 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| BUG-IM-001 | 文件策略 | 点击「IM管理 / 文件策略」,或点击查询/重置 | 页面 Console 报 `检查IM文件策略失败`,列表无法加载,行操作「编辑」不可测 | 文件策略列表应正常加载,缺省策略应自动初始化 | 后端日志:`SELECT COUNT(1) FROM im_file_policy ... Error 1146: Table 'hotgo.im_file_policy' doesn't exist`;代码位置:`server/internal/logic/sys/im_file_policy.go:128` | P0 | 待处理 |
| BUG-IM-002 | 通知模板 | 点击「IM管理 / 通知模板」,或点击查询/重置 | 页面 Console 报 `检查IM通知模板失败,请稍后重试!`,列表无法加载,行操作「编辑」不可测 | 通知模板列表应正常加载,缺省模板应自动初始化 | 后端日志:`SELECT COUNT(1) FROM im_notification_template ... Error 1146: Table 'hotgo.im_notification_template' doesn't exist`;代码位置:`server/internal/logic/sys/im_notification.go:123` | P0 | 待处理 |
| BUG-IM-003 | 外部服务配置 | 点击「IM管理 / 外部服务配置」,或点击查询/重置 | 页面 Console 报 `初始化IM外部服务配置失败`,列表无法加载,行操作「编辑」「检查」不可测 | 外部服务配置列表应正常加载,缺省服务配置应自动初始化 | 后端日志:`SELECT COUNT(1) FROM im_external_config ... Error 1146: Table 'hotgo.im_external_config' doesn't exist`;代码位置:`server/internal/logic/sys/im_external_config.go:194` | P0 | 待处理 |
| BUG-IM-004 | Kafka命令 | 点击「IM管理 / Kafka命令」后点击「发送缓存失效测试」 | 页面 toast 显示 `读取IM命令Outbox失败`,Kafka 命令列表仍为 0 条 | 应写入 Outbox 并投递 Kafka,页面能看到新事件或明确状态 | 后端日志:`读取IM命令Outbox失败: element of parameter "pointer" for function Scan should type of struct/*struct/[]struct/[]*struct`;代码位置:`server/internal/library/imkafka/command.go:103` | P0 | 待处理 |
| BUG-IM-005 | Kafka命令 | 点击「发送缓存失效测试」「发送工作台测试事件」后查看数据库 | 正确表 `hotgo.im_admin_outbox` 为 0 条;带前缀表 `hotgo.hg_im_admin_outbox` 新增 3 条 pending 记录 | Admin 新增 IM 表和代码查询应统一使用无前缀 `im_*` 表,避免 `hg_im_*` 和 `im_*` 双表分裂 | MySQL:`SHOW TABLES LIKE 'hg_im_%'` 可见 `hg_im_admin_outbox`、`hg_im_admin_commands`、`hg_im_audit_events`;`hg_im_admin_outbox` 有 pending 数据,`im_admin_outbox` 为空 | P0 | 待处理 |
| BUG-IM-006 | Kafka死信 | 点击「IM管理 / Kafka死信」 | 页面可打开,但列表无数据,行操作「详情」「忽略」「标记已处理」「重放」无法覆盖 | 联调测试库应准备至少 1 条死信数据,才能覆盖处置按钮 | MySQL:`hotgo.im_kafka_dead_letter_events` 计数为 0 | P1 | 待补测试数据 |
| BUG-IM-007 | Kafka审计事件 | 点击「IM管理 / Kafka审计事件」 | 页面可打开,但列表无数据,行操作「详情」无法覆盖 | 联调测试库应准备至少 1 条审计事件数据,才能覆盖详情按钮 | MySQL:`hotgo.im_audit_events` 计数为 0 | P1 | 待补测试数据 |
### 21.3 数据库核查结果
| 库 | 表 | 结果 |
| --- | --- | --- |
| `hotgo` | `im_file_policy` | 不存在 |
| `hotgo` | `im_notification_template` | 不存在 |
| `hotgo` | `im_external_config` | 不存在 |
| `hotgo` | `im_admin_commands` | 存在,0 条 |
| `hotgo` | `im_admin_outbox` | 存在,0 条 |
| `hotgo` | `hg_im_admin_outbox` | 存在,3 条 pending |
| `hotgo` | `im_kafka_dead_letter_events` | 存在,0 条 |
| `hotgo` | `im_audit_events` | 存在,0 条 |
### 21.4 下一步建议
1. 先导入缺失表 SQL:`server/storage/data/generate/im_file_policy_menu.sql`、`server/storage/data/generate/im_notification_menu.sql`、`server/storage/data/generate/im_external_config_menu.sql`。
2. 修复 Kafka Outbox 表名前缀分裂问题,确保所有 Kafka Admin 表统一访问 `im_*`,不要落到 `hg_im_*`。
3. 修复 `DispatchOutboxByEvent` 中 `g.DB().GetOne` 触发的 `Scan` 参数类型错误。
4. 准备 Kafka 死信和 Kafka 审计事件测试数据后,重新覆盖「详情」「忽略」「标记已处理」「重放」等按钮。
5. 上述 P0 处理后,重新执行本测试文档第 4 到第 18 章。
## 22. 第二轮修复记录
执行时间:2026-09-03 15:37 - 15:50
### 22.1 已处理项
| 缺陷编号 | 处理结果 | 复测结果 |
| --- | --- | --- |
| BUG-IM-001 | 已补齐 `hotgo.im_file_policy` 表;访问文件策略列表时可自动初始化默认策略 | `/admin/imFilePolicy/list?tenantId=1` 返回 `code=0`,列表 6 条 |
| BUG-IM-002 | 已补齐 `hotgo.im_notification_template` 表;访问通知模板列表时可自动初始化默认模板 | `/admin/imNotification/templateList` 返回 `code=0`,列表 7 条 |
| BUG-IM-003 | 已补齐 `hotgo.im_external_config` 表;访问外部服务配置列表时可自动初始化默认配置 | `/admin/imExternalConfig/list?tenantId=1` 返回 `code=0`,列表 5 条 |
| BUG-IM-004 | 已修复 Kafka Admin 命令/Outbox 读写 DB,统一改为无前缀 `imRegistry` 连接 | 点击等价接口 `/admin/imKafkaCommand/cacheInvalidate` 返回 `code=0`,不再报 `读取IM命令Outbox失败` |
| BUG-IM-005 | 已修复 `im_admin_commands`、`im_admin_outbox`、`im_audit_events`、`im_kafka_dead_letter_events` 读写前缀分裂 | 新增 Kafka 测试事件写入 `hotgo.im_admin_outbox`,状态为 `sent`;没有继续写入 `hg_im_admin_outbox` |
### 22.2 额外修复
| 问题 | 处理结果 | 复测结果 |
| --- | --- | --- |
| Docker 本地 admin 缺少运行时 MySQL/Redis 环境变量导致启动 panic | 本地容器启动时显式传入 `MYSQL_DSN`、`REDIS_ADDR`、`TOKEN_SECRET_KEY` 等变量 | `im-local-admin` 正常启动,`http://127.0.0.1:8000/admin/` 可访问 |
| admin 容器内 `im-server:6060` 解析到不可用地址,死信内部查询 EOF | `config.template`、`entrypoint.sh` 增加 IM API 环境变量;本地容器使用 `IM_INTERNAL_API_BASE_URL=http://im-local-im:6060` | `/admin/imDeadLetter/list?tenantId=1` 返回 `code=0`,列表 18 条 |
| 测试库 IM 菜单中文标题乱码 | 已在测试库用 UTF-8 连接修复 `hg_admin_menu.title`;新增 `im_menu_title_repair.sql` 可重复修复 | `imManager`、`imOrgIndex`、`imFilePolicyIndex`、`imKafkaCommandIndex` 等标题查询显示正常中文 |
| 部分新增菜单 SQL 使用旧版 `hg_admin_menu` 字段 `route/is_cache/created_by/updated_by` | 已转换相关 `*_menu.sql` 菜单插入字段为当前 HotGo 表结构 | 相关 SQL 在本地 `hotgo` 库重复执行通过,无 `Unknown column 'route'` |
### 22.3 当前剩余注意项
1. 当前浏览器页签仍保存旧 token,页面上可能继续弹「登录身份已失效」;刷新并重新登录后再继续 UI 点击测试。
2. `hg_im_admin_outbox` 里还保留第一轮测试误写入的 3 条旧数据,仅用于证明旧问题,不影响新链路;后续可按运维需要清理。
3. Kafka 审计事件列表当前仍无数据,需要 IM Server 产生或导入审计事件后才能覆盖详情弹窗。
+358
View File
@@ -0,0 +1,358 @@
# IM 未实现增强项
> 更新时间:2026-08-22
> 范围:HotGo IM 后台、IM 服务端配合项、客户端动态配置来源
本文用于记录当前后台已识别但尚未完成的增强项,作为后续开发排期和验收依据。当前多租户方案为“一个 HotGo 控制库 + 每租户一个独立 IM 业务库”,租户业务表不带 `tenant_id`,后台必须通过登录态解析租户并选择租户库。
## 已完成基线
1. 邀请码管理:单个生成、批量生成、列表、批次详情、撤销、批量撤销、导出、注册短信开关。
2. 租户管理:列表、详情、编辑、状态、用量/健康、删除前检查、进入删除流程、恢复。
3. 用户管理:列表、详情、设备、重置密码、撤销会话、账号状态停用/恢复。
4. 审计日志:列表、详情、导出。
5. Outbox 运维:列表、详情、重试、标记死信,覆盖 `im_business_events_outbox` 和 `im_group_events_outbox`。
6. 租户边界:用户、审计、群、实名、工单、Outbox 等后台接口已按登录态解析租户范围。
7. 群治理处置:治理配置更新、解散群、成员禁言、解除禁言、入群审批、管理员设置/取消、群主转让、群文件删除。
## 剩余 P0
### P0-1 Bootstrap 动态配置来源
状态:未实现。
目标:
1. 后台提供 `bootstrap` 所需配置来源,包括 `features`、`message_policy`、`limits`、`settings_menu`、`workbench.modules`。
2. IM 服务端从后台配置或控制库读取后,下发给客户端。
3. 客户端不硬编码功能开关、消息策略、设置菜单和工作台基础入口。
建议实现:
1. 扩展 `im_tenant_config` 或新增独立配置表。
2. 使用 GoFrame CLI 重新生成 DAO/entity/do,禁止手改生成文件。
3. 后台提供租户配置页面,敏感字段脱敏回显。
4. IM 服务端实现配置读取和缓存,缓存失效策略单独定义。
依赖:
1. 控制库迁移 SQL。
2. IM 服务端 `bootstrap` 接口对接。
3. 客户端按返回配置裁剪 UI。
### P0-2 完整租户配置与配额
状态:部分实现。
已实现:
1. `max_users`
2. `max_groups`
3. `max_group_members`
4. 租户状态、企业码、名称、DB 路由
未实现:
1. 品牌 Logo、品牌色、登录页展示配置。
2. HTTP API、WebSocket、文件服务等终端地址。
3. 可用登录方式。
4. 密码策略。
5. 文件存储配额。
6. 功能开关配置。
7. App 更新策略基础配置。
建议实现:
1. 将基础配额、品牌、终端地址、登录方式、密码策略拆成清晰字段或 JSON 配置。
2. 对 `db_dsn`、密钥类字段只保存不明文回显。
3. 平台管理员可编辑,租户管理员按权限只读或申请变更。
### P0-3 日志脱敏统一治理
状态:未实现。
目标:
1. 访问日志、错误日志、审计摘要、导出任务日志不得泄露敏感字段。
2. 覆盖邀请码、验证码、密码、Token、服务密钥、数据库 DSN、实名证件信息。
至少覆盖字段:
1. `inviteCode`
2. `invite_code`
3. `code`
4. `verification_code`
5. `password`
6. `secret`
7. `serviceSecret`
8. `service_secret`
9. `authorization`
10. `db_dsn`
11. `id_number`
12. `token`
建议实现:
1. 在统一响应/日志中间件或日志写入前增加脱敏工具。
2. 对业务审计 `detail_json` 做字段级脱敏。
3. 补充单元测试,覆盖嵌套 JSON、大小写字段、数组对象。
### P0-4 邀请码明文查看与精确查询审计拆分
状态:部分实现。
已实现:
1. 邀请码生成、批量生成、列表、批次详情、撤销、批量撤销、导出。
2. 基础菜单权限。
待确认/增强:
1. 明文查看是否需要独立权限点。
2. 精确查询邀请码是否需要独立权限点。
3. 导出明文邀请码是否需要二次确认和独立审计。
4. 查询/查看/导出操作是否全部写入业务审计。
建议实现:
1. 增加 `imInvitePlainView` 或等价权限。
2. 列表默认脱敏,只有具备权限时显示明文。
3. 精确查询、明文查看、导出记录审计,审计中不写邀请码明文。
## 后续 P1
### P1-0 组织架构管理
状态:部分实现。
已实现:
1. 部门列表。
2. 部门新增、编辑、移动。
3. 部门删除前校验子部门和成员。
4. 部门移动防环校验。
5. 部门成员列表。
6. 成员添加、编辑、调岗、移除。
7. 组织搜索基础接口,覆盖部门和成员。
8. 组织操作写入 IM 后台审计。
9. 部门导出和成员导出。
待实现/配合:
1. 组织数据权限:当前后台按租户选库隔离,部门级 `org_admin` 数据范围尚未建模。
2. 组织导入:需要定义 Excel 字段、冲突覆盖规则、失败明细和幂等更新策略。
3. 离职用户处理策略:当前支持 `employment_state` 和 `contactable`,客户端/IM 服务端需按该状态裁剪通讯录和可联系入口。
4. 组织版本增量同步:当前维护部门/成员版本号,客户端增量同步协议需由 IM 服务端继续承接。
### P1-1 群治理处置动作
状态:已完成基础后台闭环。
已实现:
1. 群列表、详情。
2. 成员列表。
3. 治理配置查看。
4. 入群申请、邀请、公告、禁言、管理日志列表。
5. 治理配置更新。
6. 全员禁言开关。
7. 单人禁言/解禁。
8. 解散群。
9. 入群审批通过/拒绝。
10. 设置/取消管理员。
11. 转让群主。
12. 群文件列表和删除处置。
待 IM 服务端配合确认:
1. 后台写入 `im_group_events_outbox` 后,IM 服务端消费者需同步在线会话、群资料缓存和成员 ACL。
2. 群文件删除当前为业务索引软删除,底层对象文件物理清理归入 P1-2 文件治理。
### P1-2 文件治理
状态:部分实现。
已实现:
1. 文件列表。
2. 文件用途、大小、状态查询。
3. 隔离、删除。
4. 文件详情查看审计。
5. 孤儿文件手动清理。
6. 孤儿文件定时任务 `imFileCleanupOrphan`,参数为保留天数、单租户处理上限。
7. 租户级文件用途策略配置:大小上限、保留天数、MIME 白名单、自动清理开关、病毒扫描开关。
待实现/配合:
1. 下载审计:需要 IM 服务端文件下载链路或后台下载代理在实际下载时写入审计。
2. 底层对象物理删除:当前后台将 `im_fileuploads.status` 标记为已删除,物理对象清理需由文件服务或 IM 服务端清理器消费 `location`。
3. 过期文件执行:后台已提供策略配置,IM 服务端/文件清理器需读取 `im_file_policy` 后执行过期扫描、状态更新和物理清理。
4. 文件用途识别目前基于 `im_filemsglinks`/群文件索引做基础归类,头像、举报证据、反馈附件等用途需要 IM 服务端补齐绑定关系或独立索引。
5. 病毒扫描执行:后台已提供开关,实际扫描任务、扫描结果回写和用户通知需要 IM 服务端或文件服务承接。
### P1-3 外部服务配置
状态:部分实现。
已实现:
1. SMS 服务商配置。
2. Push/APNS/FCM 配置。
3. TURN/STUN 配置。
4. 对象存储配置。
5. 审核插件配置。
6. 敏感字段 `secret_json` 只写入,不明文回显,仅返回 `hasSecret`。
7. 基础配置完整性检查,写入 `status`、`checked_at`、`check_result`。
8. 配置变更和检查写入 IM 后台审计。
待实现/配合:
1. 真实连通性检查:需要各服务商 SDK 或 IM 服务端提供探测接口。
2. IM 服务端 `bootstrap`、短信、Push、TURN、对象存储、审核链路需要读取 `im_external_config`。
3. `im_external_config` 为新增控制库表,需要执行 `server/storage/data/generate/im_external_config_menu.sql`。
### P1-4 任务看板
状态:部分实现。
已实现:
1. 只读任务看板,按租户汇总 IM 业务库健康状态。
2. Outbox 巡检:统计业务事件和群事件的待投递、失败数量。
3. 文件清理巡检:统计孤儿文件和待物理清理文件数量。
4. 展示最新业务事件、最新群事件、最新文件更新时间。
5. 根据失败/待处理/待清理数量给出基础建议动作。
待实现/配合:
1. 主动执行任务:当前看板只读,不直接触发修复或调度。
2. Push 重试:需要 IM 服务端暴露 Push 失败队列、重试入口或统一任务表。
3. 搜索索引重建:需要 IM 服务端定义索引任务入口、进度查询和失败重试模型。
4. 邀请码投影补偿:需要 IM 服务端或后台定义投影任务表、幂等键和重放策略。
5. 文件物理清理任务详情:需要文件服务或 IM 服务端回写物理删除结果。
6. 跨租户任务执行审计:执行型任务落地后需补充独立权限点和审计明细。
### P1-5 系统通知模板
状态:部分实现。
已实现:
1. 租户级通知模板列表。
2. 账号安全、好友申请、群事件、文件事件、会议提醒、日报月报提醒、工单处理等默认场景初始化。
3. 站内通知、Push、短信渠道开关。
4. 标题模板、内容模板、扩展 JSON 配置。
5. 模板启用/禁用。
6. 模板变更写入 IM 后台审计。
待实现/配合:
1. 实际投递:需要 IM 服务端 Outbox 消费器或系统 Topic 桥接读取模板并发送。
2. 投递记录:需要 IM 服务端回写站内、Push、短信投递状态和失败原因。
3. 模板变量校验:当前仅校验非空和 JSON 格式,变量白名单、缺失变量检查需要按具体场景定义。
4. 多语言模板:当前为单语言模板,后续如客户端支持多语言需扩展语言维度。
## 后续 P2
### P2-1 工作台模块配置
状态:已完成基础后台闭环。
已实现:
1. 租户工作台模块列表、搜索。
2. 模块新增、编辑、启用/禁用。
3. 模块排序接口和后台入口。
4. 默认初始化组织架构、会议通知、日报、月报、总经理信箱五个入口。
5. 路由选项对齐 IM 服务端当前白名单,避免配置后客户端接口丢弃。
6. 工作台配置变更写入 IM 后台审计。
待 IM 服务端/客户端配合确认:
1. `GET /v1/im/workbench` 当前读取 `im_workbench_apps` 已具备,需在实际环境确认租户库连接与后台修改后的缓存刷新策略。
2. 用户或角色级工作台权限表 `im_workbench_user_apps` 尚未接入后台,当前只覆盖租户级模块配置。
3. 未读角标实时事件 `workbench.badge.updated` 的聚合和增量推送仍由 IM 服务端承接。
4. 新增自定义入口若超出当前五个路由,需要先扩展 IM 服务端路由白名单和客户端页面。
### P2-2 App 版本更新策略
状态:已完成基础后台闭环。
已实现:
1. 租户级客户端版本策略列表、搜索。
2. 按 `platform + channel` 配置最新版本、最低支持版本、更新类型、商店地址、安装包地址、校验值和再次提醒时间。
3. 默认初始化 iOS/Android stable 策略。
4. 配置变更写入 IM 后台审计。
待 IM 服务端/客户端配合确认:
1. `GET /v1/im/config/app-version` 已有 IM 服务端读取链路,需确认实际环境租户库数据和客户端版本比较规则。
2. 灰度比例、分渠道灰度名单、强更弹窗样式尚未建模。
### P2-3 举报原因配置
状态:已完成基础后台闭环。
已实现:
1. 按用户、群组、消息三类对象配置举报原因。
2. 支持原因新增、编辑、禁用、排序、是否要求描述、是否允许上传证据。
3. 默认初始化诈骗、骚扰、垃圾广告、违法违规等基础原因。
4. 配置变更写入 IM 后台审计。
待实现/配合:
1. 举报工单 SLA、合并策略、批量导出仍未实现。
2. 举报工单处置结果与客户端通知联动仍由后续工单模块增强。
### P2-4 总经理信箱配置
状态:已完成基础后台闭环。
已实现:
1. 租户级总经理信箱启用/禁用。
2. 匿名提交开关。
3. 必填字段、最大字数、附件策略配置。
4. 告知标题和告知内容配置,更新时同步刷新 `notice_updated_at`。
5. 配置变更写入 IM 后台审计。
待实现/配合:
1. 信箱提交列表、处理状态、回复闭环尚未实现。
2. 受理人/部门、SLA、匿名身份展示范围尚未建模。
### P2-5 意见反馈配置
状态:已完成基础后台闭环。
已实现:
1. 租户级意见反馈分类配置。
2. 最大字数、附件策略、联系方式开关、诊断信息提示配置。
3. 配置变更写入 IM 后台审计。
待实现/配合:
1. 反馈提交列表、合并、回复、导出尚未实现。
2. 客户端分类文案如需多语言,需要扩展分类结构。
### P2 待实现
1. 会议。
2. 日报/月报。
3. 总经理信箱提交处理闭环。
4. 青少年模式。
5. 反诈与内容审核策略。
6. 举报/反馈 SLA、合并、导出。
7. 实名审核策略配置。
## 验收提示
1. 所有后台租户业务操作必须通过登录态解析租户,不能信任浏览器传入的 `tenantId`。
2. 平台管理员跨租户操作必须选择租户并写审计。
3. 租户管理员隐藏租户选择器,后端使用绑定租户。
4. 所有新增按钮必须有独立菜单权限。
5. 前端字典、状态、下拉选项必须从后端字典加载。
6. 敏感字段不得在日志、审计、导出文件中明文出现。

Some files were not shown because too many files have changed in this diff Show More