Compare commits

..
155 Commits
Author SHA1 Message Date
Your Name ba837f4cae fix: use production logging profile
Deploy im-manager production / deploy (push) Successful in 4m33s
2026-10-19 01:58:31 +08:00
Your Name 0553424ef9 ci: deploy pro with docker compose
Deploy im-manager production / deploy (push) Successful in 10m56s
2026-10-18 20:11:07 +08:00
Austin 64037863ef feat: 添加毛隅管理模块的后台接口与菜单权限 2026-10-15 18:20:26 +08:00
Austin db2eb62277 fix: 删除过时的im_filemsglinks_msgid_bigint.sql文件
该文件是用于修复msgid字段类型从INT改为BIGINT的遗留SQL脚本,在完成数据库字段变更后已不再需要,删除后可避免后续误用该过时脚本。
2026-10-13 17:07:30 +08:00
Austin a3527a6d22 fix: 将 im_filemsglinks.msgid 字段类型从 INT 修改为 BIGINT
该字段原类型为 INT,最大值仅为 2147483647,而消息 ID 是雪花算法生成的 int64 类型值,插入时会超出范围导致失败。失败的 Kafka 消息持久化消费者会无限重试,阻塞分区及后续消息的持久化。此修改适用于 2026-10-13 之前部署的所有环境,全局非租户分片表仅需执行一次。
2026-10-13 15:39:49 +08:00
Austin 8d6618dac1 feat: 新增毛隅手记服务内部管理接口客户端及配置
新增 maoyuAdminApi 配置段到模板和示例配置,实现 maoyuadmin 客户端,包含请求签名、响应解析等功能,配套单元测试
2026-10-12 17:40:59 +08:00
Austin 2c5995073f feat: 新增禾圃集应用版本更新管理功能
新增禾圃集应用(appCode=garden)的版本策略管理功能,包括后端接口、前端API、页面路由、数据库迁移及相关常量配置,支持对不同平台和渠道的版本策略进行增删改查操作,与现有讯工捷使(appCode=im)版本管理功能保持一致的架构和交互逻辑。
2026-10-12 15:31:11 +08:00
Austin 315d97895b docs(cultivate/flowers): 更新阶段规则相关文本描述
修正了非 bloom 阶段规则的校验注释和错误提示文本,同时更新了编辑页的输入标签和占位符文本,使表述更准确对应后端校验逻辑。
2026-10-10 11:57:03 +08:00
Austin 3834af70b5 style(flowers): update modal selector to fix style priority issue
修改弹窗样式选择器,提升优先级以覆盖 naive-ui 运行时注入的固定宽度样式,解决 Teleport 弹窗的样式覆盖问题。
2026-10-10 10:26:02 +08:00
Austin 667e62e97e feat(cultivate): 添加花朵阶段中文标题并优化编辑页展示
新增阶段中文名映射及转换函数,用于显示更友好的阶段名称
在编辑页中应用阶段标题,同时调整下一阶段输入框为只读展示
优化弹窗宽度样式,适配不同屏幕尺寸
2026-10-10 10:08:22 +08:00
Austin 55ae8b2acd feat(cultivate-flower): 新增等待时长字段并更新阶段规则校验逻辑 2026-10-08 12:47:17 +08:00
Austin 5a7d8407bc refactor(web): 注释通知相关代码并添加临时注释说明
注释右上角通知铃铛及实时推送相关代码,添加说明后续恢复时取消注释
2026-09-30 15:37:33 +08:00
richjohnrambo 837a03a87e fix: cap mysql connection pool 2026-09-27 18:36:00 +08:00
richjohnrambo 0edbc63bc1 fix: keep tenant option list request 2026-09-25 23:31:33 +08:00
richjohnrambo 9e01f95a61 fix: allow bound tenant option loading 2026-09-25 23:27:11 +08:00
Austin fce8dbbe32 fix: backfill tenant via useForm channel and skip empty-tenant watch on search reset
- imSearchReset now receives setFieldsValue from useForm() instead of the
  template ref instance method, which was unreliable for backfilling tenantId
- imUser/imSensitiveWord watches skip tenantId<=0 so reset no longer fires
  policy-group/group requests that get rejected with empty tenant
- loadImTenantOptions wraps platform-branch TenantList in try/catch so a
  failed request no longer aborts page initialization
2026-09-24 20:32:32 +08:00
Austin 43f3581b1c fix: restore tenant filter on search reset in IM pages
BasicForm resetFields clears tenantId along with other fields, so the
auto-query after reset hit the empty-list guard (or was rejected by the
backend). Add a shared imSearchReset handler in imTenantOptions.ts that
refills the tenant select with its first option (bound users keep their
own tenant) before reloading, and wire it into the 9 IM list pages whose
reset did not already refill the tenant condition.
2026-09-24 17:02:08 +08:00
richjohnrambo 9924c1e522 fix: restore lockscreen timeout to 72 hours 2026-09-24 01:31:41 +08:00
richjohnrambo e87ac92cd1 Revert "refactor: 移除锁屏功能相关代码"
This reverts commit e002e45f82.
2026-09-24 01:30:49 +08:00
richjohnrambo e24a04a920 fix: prevent admin bootstrap merge helper race 2026-09-24 01:20:59 +08:00
richjohnrambo 32017a2fbf fix: avoid stale admin asset cache 2026-09-24 01:01:57 +08:00
richjohnrambo 369898bd7c feat: integrate cultivate admin internal APIs 2026-09-24 00:02:27 +08:00
Austin e002e45f82 refactor: 移除锁屏功能相关代码 2026-09-23 17:39:18 +08:00
richjohnrambo abadcceb19 feat: initialize shared tenant workbench data 2026-09-23 17:04:30 +08:00
Austin 5a07f63716 feat: 添加实时锁定状态控制解锁按钮显示
添加实时锁定状态字段到登录失败日志模型
并修改解锁按钮仅在账户当前仍锁定时显示
2026-09-23 13:18:35 +08:00
richjohnrambo 6c338626d6 fix: validate im registration platform role 2026-09-23 02:51:58 +08:00
richjohnrambo 22adc13acf feat: shard tenant topic and membership tables 2026-09-23 02:14:13 +08:00
richjohnrambo 432e7ac51a fix: keep only realname review policy on tenant 2026-09-23 02:06:52 +08:00
richjohnrambo dd12dbfe07 fix: cache admin permissions in redis 2026-09-23 00:30:40 +08:00
richjohnrambo 4fbc896d75 fix: sync tenant entry menu casbin rules 2026-09-23 00:07:20 +08:00
richjohnrambo 603e89e204 fix: query tenant entry tables without prefix 2026-09-22 23:52:08 +08:00
richjohnrambo abd5eb162a feat: add tenant entry application pages 2026-09-22 23:17:55 +08:00
Austin 935bfe17a0 fix(admin/member): 允许校验租户时包含初始化状态
修改租户校验逻辑,将允许的租户状态从仅活跃态扩展至包含初始化态,确保新建商户创建管理员操作正常完成
2026-09-22 19:38:54 +08:00
Austin 671ac2e1f7 fix(security-policy): 修复策略保存时租户行更新时间与事件投递阻塞问题
调整策略保存时的租户表更新逻辑,显式赋值updated_at避免自动填充干扰
修改策略变更事件投递逻辑,失败时仅告警审计不阻塞主流程
新增租户无策略时加载模板初始化配置的逻辑,提升默认策略灵活性
2026-09-22 19:08:53 +08:00
Austin dc3c45cd53 feat: 将安全策略模板化,新建商户时自动复制初始策略 2026-09-22 18:32:08 +08:00
richjohnrambo 9a9e30f94e feat: add tenant list id sorting 2026-09-22 17:32:08 +08:00
richjohnrambo 85b564e2ac refactor: merge tenant configuration into tenant registry 2026-09-22 16:47:40 +08:00
richjohnrambo 851eca97ed feat: support shared tenant database sharding 2026-09-22 15:21:34 +08:00
Austin 0596599db9 feat: add tenant bound restriction for IM dashboard and management pages
1. extract isTenantBound utility from user store logic
2. add disabled state to tenant select components across all IM pages
3. update policy tenant options logic to hide global option for bound tenants
4. add backend scope validation for IM admin operations
2026-09-22 11:34:40 +08:00
Austin 40ac52923e feat(imTenant): 新增商户管理员账号查看与密码修改功能
1.  新增编辑页管理员账号展示与密码修改字段,仅编辑态可见
2.  新增商户管理员密码重置逻辑,完善编辑场景密码校验
3.  补充超管可见控制:业务库、连接串、管理员信息非超管脱敏
4.  修复schema未使用响应式值的问题
2026-09-21 18:44:37 +08:00
Austin c9673ca871 feat: 新增商户管理员账号自动创建功能,优化租户权限控制
1.  后端实现租户创建时自动绑定创建管理员账号,支持超管/普通管理员权限区分
2.  前端适配超管权限控制业务库名、连接串等敏感字段展示与编辑
3.  优化租户数据库回退逻辑,未配置独立库时自动回落默认库
4.  调整租户列表与详情页的权限数据脱敏逻辑
2026-09-21 17:50:38 +08:00
Austin 197c6c86f4 fix: 修复多页面租户未就绪时表格提前请求问题,优化登录审计相关文案与列宽
1. 为imSensitiveWord、imUserPolicyGroup、imSecurityPolicy页面添加租户ID校验,避免表格挂载时因租户默认值未就绪发起无效请求
2. 更新imLoginFailureLog、imUserLoginHistory的搜索占位符与账号列宽
3. 修正后端登录审计相关结构体的字段注释描述
2026-09-21 16:25:12 +08:00
Austin a2f328f97e style(im/registrationInvite): 调整绑定用户提示的插槽封装与样式
将绑定用户未验证提示调整为插槽形式,并移除冗余的样式配置
2026-09-21 14:57:49 +08:00
Austin c4ab970fb7 fix(im/registrationInvite): add input verification for bound user search
1. add computed state to check if bind user input is unverified
2. disable confirm button and show warning tip when input is unverified
3. track last searched keyword to validate user input
4. add submission guard to prevent unvalidated submissions
2026-09-21 14:45:10 +08:00
Austin 8fad32e396 docs: update IM login lock error prompt copy and description
修正登录锁定错误提示文案,补充完整的时长格式化规则,更新示例文案匹配新规则
2026-09-21 13:57:53 +08:00
Austin 6f331a07fc style(imTenant): adjust action column width to 320px
widen the operation column to fit longer action buttons properly
2026-09-21 13:27:52 +08:00
Austin b4174a6412 feat: 新增IM联系我们字典配置,优化用户搜索与邀请绑定功能
1. 优化IM用户列表搜索逻辑,支持长数字用户ID精确匹配
2. 修复前端雪花ID精度丢失问题,改用字符串传输用户ID
3. 新增邀请码批量绑定用户功能,支持搜索绑定指定用户
4. 新增字典配置接口,新增联系我们字典SQL脚本
5. 完善字典数据变更事件推送,优化弹窗关闭与页面刷新逻辑
2026-09-21 13:11:08 +08:00
Austin 0381984939 Merge branch 'dev' of https://git.118im.com/im_team/im_manager into dev 2026-09-21 09:28:09 +08:00
Austin 6348ace38e feat(im security): add login failure lockout configuration feature
1. add APIs for login lockout config get and save
2. add backend service and controller for login lockout config
3. refactor frontend to separate login lockout config from main policy
4. update default login lockout policy values
5. add permission sql and docking documentation
2026-09-21 09:28:03 +08:00
richjohnrambo e8dd1eaa7d feat: 增加商户消息加密密钥管理 2026-09-20 23:43:33 +08:00
richjohnrambo 42cfe232fe fix: IM设置菜单更名为平台设置 2026-09-20 22:43:57 +08:00
richjohnrambo 4ae69706a5 fix: 区分IM系统设置菜单标题 2026-09-20 22:31:54 +08:00
richjohnrambo 3ef328a5a3 fix: 隐藏内置超管时保留角色树 2026-09-20 22:03:51 +08:00
richjohnrambo 2b93d27a4e fix: 角色列表隐藏内置超管 2026-09-20 21:53:38 +08:00
richjohnrambo 97990785cb fix: 平台超管权限走角色配置 2026-09-20 21:50:43 +08:00
richjohnrambo b68c048383 fix: 平台超管可管理后台角色 2026-09-20 21:48:10 +08:00
richjohnrambo f154b9e3c3 fix: 按角色规范后台用户商户绑定 2026-09-20 21:03:54 +08:00
richjohnrambo e2ca0607af fix: 平台超管拥有全部商户数据范围 2026-09-20 20:05:08 +08:00
richjohnrambo 6afeb65b91 feat: add tenant realname review policy 2026-09-20 18:31:46 +08:00
richjohnrambo e5ab79c40d fix: 拆分商户新增和编辑权限 2026-09-20 17:08:50 +08:00
Austin 8675fe333d feat(imLoginFailureLog): 用户登录密码错误次数锁定与解锁能力(管理端)
- 安全策略登录限频由 JSON 文本改为结构化表单(次数/锁定分钟/窗口分钟),编辑校验同步扩展
- 失败日志新增解锁入口:/imLoginFailureLog/unlock 经 internal 通道调用 im-server 释放锁定
- 失败日志模型补充 accountHash 字段,解锁按钮按 risk_action=account_locked 展示
- 租户业务库初始化模板补充 im_login_lockouts 等表;新增解锁按钮菜单权限 SQL
2026-09-20 17:04:19 +08:00
richjohnrambo fd4ff85191 fix: 限制IM租户列表为当前租户 2026-09-20 16:53:38 +08:00
richjohnrambo 6bf364ec0b fix: 修复养成管理接口参数过滤 2026-09-20 16:53:38 +08:00
Austin 47ed097923 refactor(imWorkOrder): 优化工单分类和目标类型的展示逻辑
将工单分类和目标类型的格式化逻辑抽离到model文件中,统一处理字典映射展示,替换原有的硬编码空值占位,同时修复列表页和详情页的展示不一致问题
2026-09-20 10:53:41 +08:00
Austin e5cfc80916 chore(imFile): 临时隐藏用途相关功能
注释掉页面、表单和列表中的用途相关代码,后续需要时可恢复
2026-09-20 10:44:27 +08:00
richjohnrambo c7265252d8 feat: 完善养成管理后台功能 2026-09-19 17:07:47 +08:00
richjohnrambo f6766d3dbd feat: 新增养成管理后台 2026-09-19 14:36:30 +08:00
richjohnrambo 7c8b4d9b7c feat: add friend add and curfew controls 2026-09-18 10:55:33 +08:00
richjohnrambo 348cbcaf96 fix: allow multiple config api service names 2026-09-17 22:55:21 +08:00
richjohnrambo 2a35cb4358 fix: update uid encryption key seed 2026-09-17 16:59:47 +08:00
richjohnrambo be6c5b525c fix: seed uid encryption config 2026-09-17 15:56:23 +08:00
richjohnrambo 9ed8aaf142 refactor: reuse global aliyun moderation config 2026-09-17 00:25:04 +08:00
richjohnrambo 4ed54ca392 test: initialize admin member service in scope tests 2026-09-16 23:45:21 +08:00
richjohnrambo 5173bc0aad fix: return default moderation policy when unpublished 2026-09-16 23:30:10 +08:00
richjohnrambo fec2a43b9c feat: add tenant content moderation controls 2026-09-16 22:21:42 +08:00
Austin 32491e9cdd fix: repair healthz mysql check and im tenant creation
- healthz: Scan(new(int)) is not supported by GoFrame, use .One() so
  the mysql check no longer reports a false failure
- im_tenant: set updated_by on insert for both im_tenant and
  im_tenant_config, otherwise new tenant creation fails with
  "Field 'updated_by' doesn't have a default value"
- gitignore: exclude .trae/ local debugging artifacts
2026-09-16 18:45:37 +08:00
richjohnrambo fe5c7af7e2 fix: show bound tenant in im menus 2026-09-16 16:54:12 +08:00
richjohnrambo 156e8b9c9f fix: align workbench default secret 2026-09-16 16:05:39 +08:00
richjohnrambo f8aaae42ff fix: use workbench service dns 2026-09-16 15:41:32 +08:00
richjohnrambo 82379b8823 fix: configure workbench service secret fallback 2026-09-16 15:21:28 +08:00
richjohnrambo 3e40554229 fix: fallback workbench admin secret 2026-09-16 14:28:42 +08:00
richjohnrambo e159f9a88e feat: proxy workbench admin through service 2026-09-16 14:03:58 +08:00
richjohnrambo c258e0473b fix: resolve bound im tenant for company admins 2026-09-16 13:19:55 +08:00
richjohnrambo 5dbbf39cce chore: update test environment defaults 2026-09-15 11:59:14 +08:00
richjohnrambo 50a2781b23 fix: avoid gf cli install in docker build 2026-09-14 14:27:23 +08:00
richjohnrambo a6f7ac41b0 fix: remove duplicate IM frontend API exports 2026-09-14 14:13:39 +08:00
richjohnrambo c1a6a41575 fix: update admin test environment config 2026-09-14 14:06:41 +08:00
Your NameandClaude Sonnet 5 f44e8adb72 ci: bring k3s deploy workflow over from main
This commit only ever landed on main (from when main was still the
default branch); dev's copy was still the stale blue-green version.
Bringing main's file over as-is before making dev the default branch,
so neither branch ends up missing it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 12:28:55 +08:00
richjohnrambo bd19a594ae 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
2026-09-13 23:43:51 +08:00
richjohnrambo 7aee265721 im 管理后台 2026-09-13 23:39:18 +08:00
richjohnrambo 8b30b8241c feat: refine IM admin management workflows 2026-09-13 23:25:53 +08:00
richjohnrambo 6e65935e3c feat: manage global app release policies 2026-09-12 17:08:10 +08:00
richjohnrambo 2fb5102add feat: add invite user binding 2026-09-11 10:46:20 +08:00
richjohnrambo 68d9bafbdd fix: show sensitive word action column 2026-09-11 01:20:41 +08:00
richjohnrambo d3d2235ca3 fix: refine sensitive word table actions 2026-09-11 01:11:35 +08:00
richjohnrambo 26e511113b feat: add short IM user numbers 2026-09-10 21:51:23 +08:00
richjohnrambo 047df08f84 feat: separate workbench admin features 2026-09-10 01:02:35 +08:00
richjohnrambo 27a368b386 feat: 统一IM后台商户文案和默认选择 2026-09-09 14:28:44 +08:00
richjohnrambo da60b52be2 feat: improve group owner and status display 2026-09-09 11:10:59 +08:00
richjohnrambo 964746c19b feat: add tenant login IP whitelist management 2026-09-09 09:39:55 +08:00
richjohnrambo 99307f1c98 fix: 完善IM用户状态与分组展示 2026-09-08 22:16:51 +08:00
richjohnrambo 7d54bf785a feat: 完善IM用户管理与治理操作 2026-09-08 10:54:01 +08:00
richjohnrambo 5f3f6913b8 feat: 优化群聊消息手动查询 2026-09-07 21:52:00 +08:00
richjohnrambo af2803ef1f data: 增加中文敏感词测试词库 2026-09-07 21:43:26 +08:00
richjohnrambo fdc86ea6bf fix: 允许IM平台管理员选择租户 2026-09-07 21:11:20 +08:00
richjohnrambo 5fac9a0669 feat: 重组IM后台菜单层级 2026-09-07 20:33:09 +08:00
richjohnrambo e5b8db41ea fix: 兼容租户标准MySQL连接串 2026-09-07 18:47:50 +08:00
richjohnrambo 845f19fd1c fix: 修复多级菜单页面无法打开 2026-09-07 17:25:36 +08:00
richjohnrambo ed9cc03514 test: 记录IM后台发布复测结果 2026-09-07 17:03:00 +08:00
richjohnrambo 7839dce0e6 fix: 修正IM策略页面字典选项调用 2026-09-07 16:47:09 +08:00
richjohnrambo 37d708c5a1 test: 补充IM后台测试用例与问题记录 2026-09-07 16:31:50 +08:00
richjohnrambo 16cc4567da fix: 修正IM内部接口启动示例配置 2026-09-07 12:06:10 +08:00
richjohnrambo bb87d01644 feat: 完成鸽达达后台安全与审计改造 2026-09-07 11:50:29 +08:00
richjohnrambo 0a21d7289e Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	server/api/admin/imgroup/im_group.go
#	server/api/admin/imuser/im_user.go
#	server/internal/controller/admin/sys/im_group.go
#	server/internal/controller/admin/sys/im_user.go
#	server/internal/library/imtenant/db.go
#	server/internal/logic/admin/member.go
#	server/internal/logic/sys/im_admin_scope.go
#	server/internal/logic/sys/im_group.go
#	server/internal/logic/sys/im_invite.go
#	server/internal/logic/sys/im_registration.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/router/genrouter/im_invite.go
#	server/internal/router/genrouter/im_registration.go
#	server/internal/service/sys.go
#	server/manifest/config/config.example.yaml
#	server/storage/data/generate/im_audit_log_menu.sql
#	server/storage/data/generate/im_group_menu.sql
#	server/storage/data/generate/im_realname_menu.sql
#	server/storage/data/generate/im_tenant_menu.sql
#	server/storage/data/generate/im_user_menu.sql
#	server/storage/data/generate/im_work_order_menu.sql
#	server/storage/data/im_registration_invite_hotgo.sql
#	web/src/api/imGroup/index.ts
#	web/src/api/imUser/index.ts
#	web/src/views/im/registrationInvite/BatchViewModal.vue
#	web/src/views/im/registrationInvite/CreateModal.vue
#	web/src/views/im/registrationInvite/index.vue
#	web/src/views/im/registrationInvite/model.ts
#	web/src/views/imGroup/governance.vue
#	web/src/views/imGroup/members.vue
#	web/src/views/imUser/index.vue
#	web/src/views/imUser/model.ts
#	web/src/views/imUser/view.vue
#	web/src/views/system/config/ImRegistrationSetting.vue
2026-09-04 12:01:22 +08:00
richjohnrambo f7f8cc0ef0 feat: add im kafka admin integration 2026-09-04 11:57:38 +08:00
Your NameandClaude Sonnet 5 ab2b84c9fa ci: 部署时透传 imAdminApi/imInternalApi/imConfigApi secret
commit 25abba8 把 config.template 里 imAdminApi/imInternalApi/imConfigApi
三段的 baseUrl/serviceId/serviceName/secret 从硬编码改成了环境变量占位符,
但 deploy workflow 没有跟着传这几个 secret。这次改动和 im-server 那边
commit 7193ba2 是同一批,im-server 侧已经因为漏传导致容器反复重启(本次
故障);这几个 secret 已经在 Gitea 里配好,提前补上避免下次部署 im-manager
时重演同样的问题。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 22:37:09 +08:00
richjohnrambo 25abba8eeb feat: 完善IM后台Kafka与组织架构管理 2026-09-03 17:11:29 +08:00
richjohnrambo 5a2ed4b2a4 fix: use existing dict option API in im views 2026-09-03 14:53:26 +08:00
richjohnrambo 82675c7500 fix: add im missing menu patch 2026-08-28 16:30:02 +08:00
richjohnrambo 1b86314c5f test: guard im tables from default db prefix 2026-08-28 16:19:32 +08:00
richjohnrambo eff2f54641 fix: use unprefixed kafka audit tables 2026-08-28 16:16:25 +08:00
richjohnrambo ed4f186d8e fix: normalize kafka version config 2026-08-28 15:36:56 +08:00
richjohnrambo a938c6cb23 fix: enable kafka in root docker image 2026-08-28 15:19:50 +08:00
richjohnrambo cd354e9e1b fix: enable kafka in admin docker defaults 2026-08-28 15:09:36 +08:00
richjohnrambo 6b5ef0a911 fix: query im kafka tables with gf raw helpers 2026-08-28 14:56:02 +08:00
richjohnrambo f98f3e146b fix: use unprefixed im kafka tables 2026-08-28 14:44:00 +08:00
richjohnrambo cde4480b51 feat: publish password reset via kafka 2026-08-28 14:18:27 +08:00
richjohnrambo 6e08013fd1 fix: stringify im admin json fields 2026-08-28 13:34:40 +08:00
richjohnrambo d7578efa2b fix: return im user ids as strings 2026-08-28 13:07:17 +08:00
richjohnrambo da880bc6d6 chore: log im internal query transport errors 2026-08-28 11:55:27 +08:00
richjohnrambo 673806ba2a fix: keep im api settings in config 2026-08-28 01:56:51 +08:00
richjohnrambo 1a515ef183 fix: configure im internal api from env 2026-08-28 01:44:30 +08:00
richjohnrambo a04abb0cc8 fix: set admin frontend cache headers 2026-08-28 01:42:14 +08:00
richjohnrambo 296c9a22a8 fix: stabilize kafka queue startup 2026-08-28 01:23:36 +08:00
richjohnrambo f93b935ea5 chore: use internal im service url 2026-08-28 01:08:51 +08:00
richjohnrambo e3751d9fa6 chore: use public im internal api 2026-08-28 00:48:42 +08:00
richjohnrambo a6f6f23422 chore: configure im internal api auth 2026-08-28 00:40:40 +08:00
richjohnrambo 8c54914f9a chore: configure test kafka queue 2026-08-28 00:35:10 +08:00
richjohnrambo 33bf0f8625 feat: support kafka scram queue config 2026-08-28 00:31:08 +08:00
richjohnrambo 4779214591 feat: add im kafka admin integration 2026-08-28 00:05:54 +08:00
richjohnrambo d53b0c1b62 feat: add IM admin P2 configuration modules 2026-08-22 00:36:38 +08:00
richjohnrambo 5c3f6ae58d feat: add IM file policy and org exports 2026-08-21 23:07:06 +08:00
richjohnrambo 204f96e07a feat: add IM org and notification admin 2026-08-21 17:54:54 +08:00
richjohnrambo c89e35d0e0 feat: add IM admin P1 operations 2026-08-21 17:25:19 +08:00
richjohnrambo 77b19a8157 feat: complete IM group governance actions 2026-08-21 16:34:55 +08:00
richjohnrambo 21a5b97ac8 feat: add IM governance admin enhancements 2026-08-21 16:06:29 +08:00
richjohnrambo 543c7b624e feat: expand IM admin controls 2026-08-21 15:09:39 +08:00
richjohnrambo a2abaef3a6 feat: add IM admin management 2026-08-21 01:06:19 +08:00
richjohnrambo c3676f9744 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	web/pnpm-workspace.yaml
2026-08-19 00:50:10 +08:00
richjohnrambo 89ba6bcff8 feat: add tenant-aware IM management console 2026-08-19 00:31:09 +08:00
richjohnrambo 642b7ec5da im 管理后台 2026-08-17 09:52:02 +08:00
Your Name 5ac3a5b96c 修复容器崩溃: hggen初始化需要hack/config.yaml, 运行时镜像未拷贝
hotgo/internal/library/hggen.InIt()在HTTP命令初始化时会读取hack/config.yaml
(在线代码生成功能依赖), 之前只拷贝了manifest目录导致运行时找不到该文件而致命退出
2026-08-12 11:40:13 +08:00
Your Name 616d11202e 修复容器崩溃循环: alpine运行时镜像缺少tzdata
现象:容器起来后一直重启,日志显示
"time.LoadLocation failed for zone Asia/Shanghai: unknown time zone"。
alpine默认不带IANA时区数据库,补上tzdata包解决。
2026-08-12 11:32:37 +08:00
Your Name b315d20197 新增Gitea Actions蓝绿发布workflow
手动触发(workflow_dispatch),构建镜像后调用宿主机上的
/opt/apps/im-manager/deploy.sh完成蓝绿切换。
2026-08-12 11:26:48 +08:00
Your Name f711fec00b 接入Docker蓝绿部署:自包含多阶段构建、配置外部化
- 新增根目录Dockerfile:多阶段构建(Vue前端 + Go后端),包含main.go依赖的
  internal/packed资源打包步骤(原来依赖gf build外部产出,未提交进仓库)
- 新增server/manifest/config/config.template:数据库/Redis连接串、
  token密钥等敏感配置改为占位符,运行时通过entrypoint.sh用envsubst生成
  真正的config.yaml,不再需要在仓库里维护包含真实密码的config.yaml
- entrypoint.sh改为先渲染配置再启动
- 修复web/pnpm-lock.yaml与package.json不同步的问题(有依赖被加进
  package.json但没重新生成锁文件)
- 新增web/pnpm-workspace.yaml:较新版本pnpm默认禁止依赖包运行构建脚本
  (供应链安全特性),需要显式在这里批准esbuild/@swc/core等的构建脚本,
  否则CI环境下frozen-lockfile安装会直接失败
2026-08-12 11:26:13 +08:00
602 changed files with 64206 additions and 1713 deletions
Vendored
BIN
View File
Binary file not shown.
+68
View File
@@ -0,0 +1,68 @@
name: Deploy im-manager production
on:
push:
branches:
- pro
workflow_dispatch:
concurrency:
group: production-im-manager
cancel-in-progress: false
permissions:
contents: read
env:
REGISTRY: git.olns.shop
IMAGE: git.olns.shop/pro/im_manager
SERVICE: im-manager
DEPLOY_HOST: 145.223.69.9
DEPLOY_USER: olns-deploy
jobs:
deploy:
runs-on: deploy-host
timeout-minutes: 60
steps:
- uses: actions/checkout@v4
- name: Log in to Gitea Container Registry
env:
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
printf '%s' "$REGISTRY_TOKEN" | docker login "$REGISTRY" -u admin --password-stdin
- name: Build and push image
env:
GITHUB_SHA: ${{ github.sha }}
run: |
set -euo pipefail
SHORT_SHA="$(printf '%.12s' "$GITHUB_SHA")"
docker build -t "${IMAGE}:pro" -t "${IMAGE}:${SHORT_SHA}" .
docker push "$IMAGE:pro"
docker push "$IMAGE:$SHORT_SHA"
- name: Deploy Compose service
env:
DEPLOY_SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
DEPLOY_KNOWN_HOSTS: ${{ secrets.DEPLOY_KNOWN_HOSTS }}
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
run: |
set -euo pipefail
install -d -m 700 "$HOME/.ssh"
KEY_FILE="$(mktemp "$HOME/.ssh/olns-deploy.XXXXXX")"
cleanup() {
shred -u "$KEY_FILE" 2>/dev/null || true
}
trap cleanup EXIT
printf '%s\n' "$DEPLOY_SSH_KEY" > "$KEY_FILE"
chmod 600 "$KEY_FILE"
printf '%s\n' "$DEPLOY_KNOWN_HOSTS" > "$HOME/.ssh/known_hosts"
chmod 600 "$HOME/.ssh/known_hosts"
SSH_ARGS=(-i "$KEY_FILE" -o BatchMode=yes -o StrictHostKeyChecking=yes)
printf '%s' "$REGISTRY_TOKEN" | ssh "${SSH_ARGS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"docker login git.olns.shop -u admin --password-stdin"
ssh "${SSH_ARGS[@]}" "$DEPLOY_USER@$DEPLOY_HOST" \
"cd /opt/olns && docker compose pull im-manager && docker compose up -d --no-deps --wait im-manager"
+3
View File
@@ -163,3 +163,6 @@ dist
.yarn/install-state.gz
.pnp.*
# Trae local debugging artifacts
.trae/
+80
View File
@@ -0,0 +1,80 @@
# ---- Stage 1: build the Vue frontend ----
FROM node:22-alpine AS web-build
WORKDIR /web
ENV CI=true
RUN npm install -g pnpm
COPY web/package.json web/pnpm-lock.yaml web/pnpm-workspace.yaml ./
RUN pnpm install --frozen-lockfile
COPY web/ .
RUN pnpm run build
# ---- Stage 2: build the Go backend, embedding the frontend build ----
FROM golang:1.24-alpine AS server-build
RUN apk add --no-cache git
WORKDIR /src
COPY server/go.mod server/go.sum ./
RUN go mod download
COPY server/ .
# Same layout the project's own Makefile produces: built frontend goes into
# resource/public/admin/ before packing/building the backend.
RUN rm -rf resource/public/admin && mkdir -p resource/public/admin
COPY --from=web-build /web/dist/ ./resource/public/admin/
# main.go imports internal/packed. Generate the packed resource file directly
# through GoFrame's library API instead of installing the full gf CLI in CI.
RUN printf '%s\n' \
'package main' \
'import "github.com/gogf/gf/v2/os/gres"' \
'func main() {' \
' if err := gres.PackToGoFileWithOption("resource", "internal/packed/packed.go", "packed", gres.Option{}); err != nil { panic(err) }' \
'}' > /tmp/pack.go \
&& go run /tmp/pack.go \
&& rm /tmp/pack.go
RUN CGO_ENABLED=0 go build -o /out/hotgo main.go
# ---- Stage 3: runtime ----
FROM alpine:3.22
RUN apk add --no-cache ca-certificates gettext bash tzdata
WORKDIR /app
ENV QUEUE_DRIVER=kafka
ENV KAFKA_ADDRESS=kafka.118im.com:30835
ENV KAFKA_VERSION=2.4.0
ENV KAFKA_USERNAME=
ENV KAFKA_PASSWORD=
ENV KAFKA_SASL_MECHANISM=
ENV MYSQL_DSN=
ENV REDIS_ADDR=redis.118im.com:30637
ENV REDIS_DB=2
ENV REDIS_PASSWORD=
ENV TOKEN_SECRET_KEY=hotgo123
ENV IM_ADMIN_API_BASE_URL=https://api.118im.com
ENV IM_ADMIN_API_SERVICE_ID=hotgo-admin
ENV IM_ADMIN_API_SECRET=
ENV IM_INTERNAL_API_BASE_URL=https://api.118im.com
ENV IM_INTERNAL_API_SERVICE_NAME=hotgo-admin
ENV IM_INTERNAL_API_SECRET=
ENV IM_WORKBENCH_API_BASE_URL=http://im-workbench.workbench-system.svc.cluster.local:6061
ENV IM_WORKBENCH_API_SERVICE_NAME=hotgo-admin
ENV IM_WORKBENCH_API_SECRET=hotgo-workbench-internal
ENV IM_CONFIG_API_SERVICE_NAME=im-server
ENV IM_CONFIG_API_SECRET=
COPY --from=server-build /src/manifest ./manifest
COPY --from=server-build /src/hack ./hack
COPY --from=server-build /out/hotgo .
COPY server/manifest/docker/entrypoint.sh .
RUN mkdir -p storage/logs storage/data storage/cache storage/diskqueue \
&& chmod +x entrypoint.sh hotgo
HEALTHCHECK --interval=1m --timeout=3s --start-period=20s \
CMD wget -q -O /dev/null http://localhost:8000/healthz || exit 1
EXPOSE 8000
ENTRYPOINT ["./entrypoint.sh"]
@@ -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,30 @@
-- 商户级内容审核开关。
-- 可重复执行;新商户默认关闭,现有 default 商户保持当前审核行为。
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'aliyun_moderation_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `aliyun_moderation_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否启用阿里云内容审核'' 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 = 'local_moderation_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `local_moderation_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否启用本地内容审核'' AFTER `aliyun_moderation_enabled`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
UPDATE `im_tenant_config` c
JOIN `im_tenant` t ON t.`id` = c.`tenant_id`
SET c.`aliyun_moderation_enabled` = 1,
c.`local_moderation_enabled` = 1
WHERE t.`code` = 'default';
@@ -0,0 +1,137 @@
-- 消息加密密钥管理。
-- 可重复执行。根 KEK 只在首次执行时生成,重复执行不会覆盖已有值。
SET NAMES utf8mb4;
SET @now := NOW();
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'message_encryption_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `message_encryption_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''是否启用消息加密'' AFTER `local_moderation_enabled`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
CREATE TABLE IF NOT EXISTS `im_message_encryption_keys` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL COMMENT '商户ID',
`key_version` int unsigned NOT NULL COMMENT '密钥版本',
`algorithm` varchar(32) NOT NULL DEFAULT 'AES-256-GCM' COMMENT '数据加密算法',
`encrypted_dek` blob NOT NULL COMMENT '由根KEK包裹后的商户DEK',
`dek_nonce` varbinary(12) NOT NULL COMMENT '包裹DEK使用的随机nonce',
`status` varchar(16) NOT NULL DEFAULT 'pending' COMMENT 'pending/active/retired/disabled',
`activated_at` datetime DEFAULT NULL,
`retired_at` datetime DEFAULT NULL,
`remark` varchar(255) NOT NULL DEFAULT '',
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`updated_by` bigint NOT NULL DEFAULT 0,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_message_key_tenant_version` (`tenant_id`, `key_version`),
KEY `idx_im_message_key_tenant_status` (`tenant_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='商户消息加密密钥版本';
INSERT INTO `hg_sys_dict_type`
(`pid`, `name`, `type`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
SELECT 0, 'IM内部安全材料', 'im_internal_secret', 9999,
'仅供服务端读取,通用字典接口禁止查询和维护', 1, @now, @now
WHERE NOT EXISTS (
SELECT 1 FROM `hg_sys_dict_type` WHERE `type` = 'im_internal_secret'
);
INSERT INTO `hg_sys_dict_data`
(`label`, `value`, `value_type`, `type`, `list_class`, `is_default`, `sort`, `remark`, `status`, `created_at`, `updated_at`)
SELECT 'core_material_01', TO_BASE64(RANDOM_BYTES(32)), 'string', 'im_internal_secret',
'', 2, 1, '消息DEK包裹根密钥KEK,请勿通过后台展示或修改', 1, @now, @now
WHERE NOT EXISTS (
SELECT 1 FROM `hg_sys_dict_data`
WHERE `type` = 'im_internal_secret' AND `label` = 'core_material_01'
);
SET @imRootId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imManager' LIMIT 1);
SET @settingsId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imSystemSettings' LIMIT 1);
INSERT INTO `hg_admin_menu`
(`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`,
`permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`,
`frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`,
`status`, `created_at`, `updated_at`)
SELECT @settingsId, '消息密钥管理', 'imMessageEncryptionIndex', 'message-encryption/index',
'KeyOutlined', 2, '', '/imMessageEncryption/list', '', '/imMessageEncryption/index',
1, 'imManager', 0, 0, '', 0, 0, 0, 3,
CONCAT('tr_', @imRootId, ' tr_', @settingsId, ' '), 80,
'商户消息加密开关及密钥版本管理', 1, @now, @now
WHERE @settingsId IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imMessageEncryptionIndex');
SET @keyMenuId := (SELECT `id` FROM `hg_admin_menu` WHERE `name` = 'imMessageEncryptionIndex' LIMIT 1);
UPDATE `hg_admin_menu`
SET `pid` = @settingsId,
`title` = '消息密钥管理',
`path` = 'message-encryption/index',
`icon` = 'KeyOutlined',
`permissions` = '/imMessageEncryption/list',
`component` = '/imMessageEncryption/index',
`active_menu` = 'imManager',
`level` = 3,
`tree` = CONCAT('tr_', @imRootId, ' tr_', @settingsId, ' '),
`sort` = 80,
`hidden` = 0,
`status` = 1,
`updated_at` = @now
WHERE `id` = @keyMenuId;
INSERT INTO `hg_admin_menu`
(`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`,
`permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`,
`frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`,
`status`, `created_at`, `updated_at`)
SELECT @keyMenuId, '初始化密钥', 'imMessageEncryptionInitialize', '', '', 3, '',
'/imMessageEncryption/initialize', '', '', 1, '', 0, 0, '', 0, 1, 0, 4,
CONCAT('tr_', @imRootId, ' tr_', @settingsId, ' tr_', @keyMenuId, ' '), 10, '', 1, @now, @now
WHERE @keyMenuId IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imMessageEncryptionInitialize');
INSERT INTO `hg_admin_menu`
(`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`,
`permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`,
`frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`,
`status`, `created_at`, `updated_at`)
SELECT @keyMenuId, '轮换密钥', 'imMessageEncryptionRotate', '', '', 3, '',
'/imMessageEncryption/rotate', '', '', 1, '', 0, 0, '', 0, 1, 0, 4,
CONCAT('tr_', @imRootId, ' tr_', @settingsId, ' tr_', @keyMenuId, ' '), 20, '', 1, @now, @now
WHERE @keyMenuId IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imMessageEncryptionRotate');
INSERT INTO `hg_admin_menu`
(`pid`, `title`, `name`, `path`, `icon`, `type`, `redirect`, `permissions`,
`permission_name`, `component`, `always_show`, `active_menu`, `is_root`, `is_frame`,
`frame_src`, `keep_alive`, `hidden`, `affix`, `level`, `tree`, `sort`, `remark`,
`status`, `created_at`, `updated_at`)
SELECT @keyMenuId, '切换消息加密', 'imMessageEncryptionSwitch', '', '', 3, '',
'/imMessageEncryption/switch', '', '', 1, '', 0, 0, '', 0, 1, 0, 4,
CONCAT('tr_', @imRootId, ' tr_', @settingsId, ' tr_', @keyMenuId, ' '), 30, '', 1, @now, @now
WHERE @keyMenuId IS NOT NULL
AND NOT EXISTS (SELECT 1 FROM `hg_admin_menu` WHERE `name` = 'imMessageEncryptionSwitch');
UPDATE `hg_admin_menu`
SET `level` = 4,
`tree` = CONCAT('tr_', @imRootId, ' tr_', @settingsId, ' tr_', @keyMenuId, ' '),
`updated_at` = @now
WHERE `pid` = @keyMenuId AND `type` = 3;
INSERT IGNORE INTO `hg_admin_role_menu` (`role_id`, `menu_id`)
SELECT 1, `id` FROM `hg_admin_menu`
WHERE `name` IN (
'imManager',
'imSystemSettings',
'imMessageEncryptionIndex',
'imMessageEncryptionInitialize',
'imMessageEncryptionRotate',
'imMessageEncryptionSwitch'
);
@@ -0,0 +1,33 @@
-- 阿里云文本审核统一复用 hg_sys_config.im_third_party 中的公共凭证、地域和端点。
-- 可重复执行;清理 im_external_config 中历史重复保存的密钥和连接参数。
INSERT INTO `im_external_config`
(`tenant_id`, `service`, `provider`, `endpoint`, `enabled`, `status`, `config_json`, `secret_json`, `created_by`, `updated_by`)
SELECT
t.`id`,
'moderation',
'aliyun_green',
'',
1,
'unconfigured',
JSON_OBJECT(
'nickname_service', 'nickname_detection_pro',
'chat_service', 'chat_detection_pro',
'timeout_ms', 1500,
'failure_mode', 'allow'
),
JSON_OBJECT(),
0,
0
FROM `im_tenant` t
WHERE t.`code` = 'default'
ON DUPLICATE KEY UPDATE
`provider` = VALUES(`provider`),
`endpoint` = VALUES(`endpoint`),
`enabled` = VALUES(`enabled`),
`status` = VALUES(`status`),
`config_json` = VALUES(`config_json`),
`secret_json` = VALUES(`secret_json`),
`checked_at` = NULL,
`check_result` = '',
`updated_at` = CURRENT_TIMESTAMP;
@@ -0,0 +1,708 @@
# 服务端内容审核与消息加密改造文档
> 文档状态:分阶段实施
>
> 更新日期:2026-09-16
>
> 适用项目:`im_manager`(Admin Server)、`im_server`(IM Server)
## 实施顺序
本项目拆成两个相互独立的实施部分,并统一保存在本文档中:
1. 第一部分先完成内容审核,独立开发、灰度、验收和稳定观察;本阶段不修改消息存储格式。
2. 第二部分在内容审核稳定后实施消息加密;该阶段单独立项、单独发布,并对消息表和历史数据进行分批迁移。
两部分禁止同批切换。第二部分只接管“内容审核通过后的消息存储”,不得反向修改第一部分已经验收的审核决策。
```text
第一部分:
客户端 -> TLS -> IM Server -> 内容审核 -> 现有明文存储
第二部分:
客户端 -> TLS -> IM Server -> 内容审核 -> AES-256-GCM -> 密文存储
```
## 第二部分启动门禁
消息加密开发必须同时满足:
1. 内容审核已全量上线并通过业务验收。
2. 误拦截率、阿里云耗时、超时降级和熔断策略达到生产要求。
3. 消息发送、编辑、资料修改和多 Pod 配置同步已经稳定。
4. IM 数据库全量备份及恢复演练通过。
5. 历史消息规模、迁移时间和数据库负载完成评估。
6. 加密双读、密钥轮换和回滚方案已在测试环境演练通过。
## 第一部分:服务端内容审核(优先实施)
> 文档状态:第一阶段,优先开发
>
> 编制日期:2026-09-16
>
> 适用项目:`im_manager`(Admin Server)、`im_server`(IM Server)
>
> 本阶段不修改消息存储格式,不实施消息加密。
### 1. 目标
Admin Server 提供内容审核配置和商户策略管理,IM Server 在内容写入前根据策略执行:
1. 本地敏感词补充检查。
2. 阿里云 `TextModerationPlus` 文本审核。
3. 按风险等级放行、拦截或仅审计。
4. 记录审核结果、请求耗时和处置结果,不保存审核明文。
第一阶段只为 `tenant.code=default` 的上架默认商户开启阿里云审核。其他团队商户默认关闭,不产生阿里云调用和费用。
### 2. 不在本阶段实施的内容
- 不改造 `im_messages.content` 和 `im_group_messages.content`。
- 不建设 AES 加解密模块。
- 不建设密钥管理表、密钥轮换和历史消息迁移。
- 不做客户端加密或端到端加密。
- 不改变现有 HTTPS/WSS 传输协议。
- 图片、视频和文件本体继续走独立的文件审核链路。
### 3. 现有实现和差距
| 能力 | 现状 | 改造目标 |
| --- | --- | --- |
| 阿里云文本审核 | 文档已人工验证,IM Server 无 Go 调用实现 | 实现 `TextModerationPlus` 客户端并接入业务链路 |
| 审核服务配置 | `im_external_config` 已有 `moderation` 占位 | 补齐阿里云参数、真实连通性检查和发布 |
| 商户审核策略 | `im_security_policy.sensitive_word_policy` 可保存 JSON | 定义固定结构并提供后台配置 |
| 本地敏感词 | Admin 已有 CRUD,IM Server 未消费 | 作为可选补充规则接入 IM 运行时 |
| 配置同步 | 已有 internal config、Kafka 配置事件和缓存失效 | 复用现有机制扩展审核策略 |
| 消息审核 | 消息直接保存和广播 | 在保存和广播前审核 |
| 昵称审核 | 无 | 覆盖用户昵称、群昵称和群名称的所有修改入口 |
### 4. 总体链路
```text
客户端
| HTTPS/WSS
v
IM Server
|-- 权限、宵禁、用户状态等现有校验
|-- 根据tenant读取审核策略
|-- 提取用户可见文本
|-- 本地敏感词检查(可选)
|-- 阿里云TextModerationPlus(可选)
|-- 映射风险结果和处置动作
|-- block:返回业务错误,不入库、不广播
`-- allow/audit_only:按原逻辑入库和广播
```
### 5. 审核场景
| 场景 | 阿里云 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` | 发布或修改前 |
系统消息、WebRTC 信令、已签名的服务端消息和空文本不调用文本审核。
### 6. Admin Server 改造
#### 6.1 阿里云服务配置
复用 `im_external_config` 的 `service=moderation`,只允许平台管理员维护审核策略参数。阿里云账号与连接参数统一复用 `hg_sys_config` 的 `im_third_party` 分组,不在商户审核配置中重复保存:
| 配置 | 说明 |
| --- | --- |
| `provider` | 固定 `aliyun_green` |
| `enabled` | 平台总开关 |
| `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` |
公共配置来源如下:
| `hg_sys_config` 键 | 说明 |
| --- | --- |
| `im_third_party.aliyun_access_key_id` | 阿里云 AccessKey ID |
| `im_third_party.aliyun_access_key_secret` | 阿里云 AccessKey Secret |
| `im_third_party.aliyun_green_region_id` | 内容安全地域,默认 `cn-shanghai` |
| `im_third_party.aliyun_green_endpoint` | 内容安全端点,默认 `green-cip.cn-shanghai.aliyuncs.com` |
`im_external_config.secret_json` 不再保存内容审核密钥;升级时清空历史重复密钥。Admin Server 的内部接口负责把平台总开关、场景服务参数和公共阿里云配置合并后下发给 IM Server。
“检查配置”必须真实调用阿里云测试文本,返回成功与否、耗时、RequestID 和脱敏错误,不能只检查字段非空。
#### 6.2 商户审核开关与策略
`im_tenant_config` 增加两个商户级权威开关,并在“商户管理”的新增/编辑页面维护:
| 字段 | 说明 |
| --- | --- |
| `aliyun_moderation_enabled` | 是否对该商户启用阿里云内容审核 |
| `local_moderation_enabled` | 是否对该商户启用本地敏感词审核 |
两个开关默认关闭;现有 `default` 商户迁移时保持开启。只要任一开关开启,该商户即进入内容审核流程。安全策略不再重复维护审核引擎开关,只维护场景、风险动作、第三方失败策略和拦截提示。
复用 `im_security_policy.sensitive_word_policy`,统一 JSON 结构:
```json
{
"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_tenant_config` 的两个商户开关为准。
- 每个商户可以独立开启阿里云审核、本地审核,或同时开启。
- 商户管理员不得查看或修改阿里云 AK/SK。
- 策略发布使用现有 `im.config.event` 通知 IM Server 按商户失效缓存。
#### 6.3 本地敏感词
- 现有 `im_sensitive_words` 和分组管理作为补充规则保留。
- 只有商户配置 `local_moderation_enabled=1` 时才加载。
- 敏感词和分组按商户隔离,平台管理员及当前商户管理员按权限维护。
- 词库更新继续发布 `sensitive_words.updated` 事件。
#### 6.4 内部接口
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| GET | `/internal/im-config/moderation/provider` | 拉取阿里云审核服务配置 |
| GET | `/internal/im-config/tenants/{code}/moderation-policy` | 拉取商户审核策略 |
| GET | `/internal/im-config/tenants/{code}/sensitive-words` | 拉取商户有效敏感词 |
接口复用现有内部服务签名鉴权、时间窗、RequestID 和重放校验,禁止浏览器直接调用。
### 7. IM Server 改造
#### 7.1 内部模块
```text
server/moderation/
provider.go 审核Provider接口
policy.go 商户策略和缓存
extractor.go 文本提取
decision.go 风险结果映射
local/words.go 本地敏感词匹配
aliyun/text.go TextModerationPlus实现
```
统一接口需支持:商户、场景、操作人、业务对象、文本和 TraceID,返回 `allow/block/audit_only`、风险等级、标签和可审计的脱敏结果。
#### 7.2 消息发送接入
在 `Topic.handlePubBroadcast` 中,于 `saveAndBroadcastMessage` 之前:
1. 排除 WebRTC/系统信令。
2. 从 `msg.Pub.Content` 提取审核文本。
3. 执行本地规则和阿里云审核。
4. `block` 时通过 Tinode ctrl 返回业务错误,不调用保存和广播。
5. `allow/audit_only` 时继续现有消息链路。
消息编辑必须重新审核编辑后内容,不复用原消息结果。
#### 7.3 资料修改接入
用户昵称/签名、群名称/公告、群昵称的 HTTP 和 Tinode meta 修改通道必须接入同一 moderation service,防止通过另一个协议入口绕过审核。
#### 7.4 文本提取
- 普通文本:提取完整文本。
- Drafty 富文本:提取 `txt` 及按钮、链接的用户可见标题。
- 图片/文件/语音:提取文件名、说明和附带文本。
- 禁止将任意 JSON 直接字符串化后送审,避免审核协议元数据。
#### 7.5 阿里云结果映射
| 内部结果 | 动作 |
| --- | --- |
| `allow` | 继续保存和广播 |
| `block` | 不入库、不广播,返回统一业务错误 |
| `audit_only` | 允许业务继续,写审核日志 |
| `retryable_error` | 根据 `failure_mode` 放行或拦截 |
阿里云已测试样本表明,部分引流诈骗话术可能只出现在扩展判断中。实现时不能只查看单一 `RiskLevel/Label`,需保留脱敏原始响应以便调试,并使用真实样本建立回归集。
#### 7.6 超时、降级和熔断
- 阿里云请求不得在数据库事务中执行。
- 必须设置独立超时,禁止无限等待。
- 默认 `failure_mode=allow`,第三方异常时放行并告警,避免 IM 全局不可用。
- 连续失败达阈值后短时熔断,熔断期间按 `failure_mode` 处理。
- 本地敏感词检查不受阿里云熔断影响。
#### 7.7 缓存和多 Pod
- 阿里云全局配置、商户策略和敏感词分别缓存。
- 策略缓存键至少包含 `tenant_code + policy_version`。
- Kafka 配置事件和词库事件使所有 IM Pod 同时失效。
- 事件消费必须幂等,丢失事件后仍能通过 TTL 重新加载。
### 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` | 创建时间 |
表中不保存原始明文、AK/SK 或完整第三方响应中的敏感信息。
### 9. 错误码
| 错误 | 说明 | 客户端处理 |
| --- | --- | --- |
| `CONTENT_REJECTED` | 内容审核拒绝 | 保留未发送内容,提示用户修改 |
| `CONTENT_REVIEW_UNAVAILABLE` | 审核不可用且策略为拦截 | 提示稍后重试 |
不向客户端返回阿里云原始错误、命中词详情、AK/SK 或内部堆栈。
### 10. 监控
- 按商户/场景统计请求量、放行数、拦截数、审计数和降级数。
- 阿里云耗时 P50/P95/P99。
- 超时率、错误率、熔断状态和熔断持续时间。
- 按风险等级和标签统计命中数。
- 配置版本、各 Pod 最后刷新时间和缓存命中率。
- 监控标签不得放入原始消息文本。
### 11. 开发任务拆分
#### 11.1 Admin Server
1. 补齐 `moderation` 外部服务配置和真实连通性检查。
2. 实现商户审核策略表单和发布。
3. 限制默认商户以外的敏感词入口和后端权限。
4. 增加阿里云配置、商户策略和词库 internal API。
5. 发布并验证 Kafka 配置失效事件。
#### 11.2 IM Server
1. 实现 moderation 统一接口。
2. 实现阿里云 `TextModerationPlus` 客户端。
3. 实现商户策略、本地敏感词和阿里云配置缓存。
4. 接入消息发送、消息编辑、用户资料和群资料入口。
5. 增加审核日志表、错误映射、监控和告警。
6. 补齐单元测试、集成测试和阿里云沙箱/测试账号回归。
### 12. 测试重点
- 文本、Drafty、链接、表情、中英文混合和空内容提取。
- 同一内容在单聊、群聊和编辑入口处置一致。
- HTTP 和 Tinode meta 入口不能相互绕过。
- `default` 商户开启,其他商户关闭。
- 阿里云超时、500、限流、鉴权失败和熔断恢复。
- 本地词库命中、阿里云命中、双重命中和无命中。
- 多 Pod 收到配置发布后同步生效。
- 拦截消息不入库、不广播,不占用有效消息序号。
- 日志、Kafka、死信和监控中不出现消息明文和 AK/SK。
### 13. 验收标准
1. `default` 商户的用户昵称、群昵称、群名称、单聊和群聊均真实调用对应阿里云 Service。
2. 其他商户关闭审核时不调用阿里云。
3. 拦截内容不进入消息表,不向任何订阅者广播。
4. 本地敏感词与阿里云开关可独立启停。
5. 阿里云超时和异常严格按 `failure_mode` 处理。
6. 策略和词库发布后,所有 IM Pod 在规定时间内生效。
7. 日志能追溯商户、场景、用户、RequestID、风险结果和处置动作,但不保存明文。
8. 审核上线后独立稳定运行和观察,不依赖消息加密功能。
### 14. 发布顺序
1. 先发布 Admin Server 配置表单、internal API 和配置事件。
2. 发布 IM Server 审核模块,但保持所有商户策略关闭。
3. 对阿里云连通性、超时降级和多 Pod 配置同步进行生产前验证。
4. 仅对测试用户或小流量开启 `default` 商户审核。
5. 观察误拦截率、阿里云耗时、错误率和消息总延迟。
6. 完成全量开启和验收,进入独立稳定观察期。
7. 审核功能稳定并通过验收后,才能启动消息加密阶段。
## 第二部分:服务端消息加密存储(审核稳定后实施)
> 文档状态:第二阶段,内容审核稳定后实施
>
> 编制日期:2026-09-16
>
> 适用项目:`im_manager`(Admin Server)、`im_server`(IM Server)
>
> 该改造会影响消息写入、历史同步、后台查询和数据迁移,必须单独发布和验收。
### 1. 实施前提
本阶段必须在《服务端内容审核改造文档》完成开发、全量上线和稳定观察后启动。
启动条件:
1. 内容审核的误拦截率、耗时、超时和降级策略已验收。
2. 审核与消息保存的边界已稳定,加密模块只接管“审核通过后的存储”。
3. 已完成 IM 数据库全量备份和可恢复性演练。
4. 已对 `im_messages`、`im_message_revisions`、`im_group_messages` 的数据量、存储量和迁移耗时完成评估。
### 2. 目标和非目标
#### 2.1 目标
- 审核通过的新消息在 IM Server 使用 AES-256-GCM 加密后落库。
- 国内 IM 业务库不保存可读消息正文。
- HotGo 库保存根密钥和加密后的商户数据密钥。
- 客户端协议不变,IM Server 对历史消息解密后按原协议返回。
- Admin Server 通过 IM internal-query 查看授权范围内的解密消息。
- 支持密钥版本、轮换、退役和历史数据迁移。
#### 2.2 非目标
- 不做客户端 AES 加密。
- 不做端到端加密(E2EE)。
- 不改变 HTTPS/WSS 传输安全方案。
- 不允许 Admin Server 直连 IM 数据库后自行解密。
- 不在日志、Kafka、死信和监控标签中保存消息明文。
### 3. 安全边界和部署
| 区域 | 系统 | 保存数据 |
| --- | --- | --- |
| 国外 | HotGo / Admin Server | 字典根密钥、商户消息密钥、加密开关和版本 |
| 国内 | IM Server | 用户、会话、消息密文和密钥版本 |
防护目标:任意一侧数据库单独泄露时,不能直接恢复消息明文。
已知边界:HotGo 数据库、IM 数据库和完整代码同时泄露不在本方案的主要防护范围内。
Admin Server 和 IM Server 跨公网通信必须使用 HTTPS、现有签名鉴权和重放防护,建议叠加 mTLS 或固定出口 IP 白名单。
### 4. 算法和存储格式
#### 4.1 算法
- 算法:AES-256-GCM。
- DEK:32 字节强随机密钥。
- Nonce:每次加密使用 `crypto/rand` 生成的独立 12 字节随机值。
- GCM 标签:使用 Go 标准库默认 16 字节认证标签。
- 明文:将原 `content` JSON 规范序列化为 UTF-8 字节。
- 禁止固定 Nonce、AES-ECB 和未认证的 AES-CBC/CTR。
#### 4.2 AAD
每条消息绑定:
```text
tenant_id | topic | seqid | from | client_msg_id | key_version | content
```
`content` 是固定字段标识。AAD 使用固定字段顺序和长度前缀编码,不使用可形成拼接歧义的简单字符串。
#### 4.3 密钥层级
```text
HotGo字典根密钥(KEK)
-> 加密每个商户、每个版本的DEK
-> DEK加密该商户消息
```
### 5. HotGo 密钥管理
#### 5.1 字典根密钥
根密钥使用 `hg_sys_dict_data` 保存,必须满足:
1. 使用独立内部字典类型,不出现在通用字典管理页。
2. 后端字典列表、选项、导出和缓存接口必须服务端拒绝该类型,不得仅隐藏菜单。
3. 根密钥是 32 字节强随机值的 Base64 编码,不使用口令、UUID 或可读文本。
4. 根密钥只由 Admin Server 专用密钥服务读取。
5. 根密钥不回显、不写日志、不进入 Kafka,不通过通用配置接口返回。
#### 5.2 商户密钥表
新增 `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.3 后台管理页
只允许最高权限平台管理员:
- 查看商户加密状态、当前密钥版本和最后轮换时间。
- 初始化商户密钥。
- 启用或暂停消息加密。
- 轮换密钥。
- 查看历史迁移进度。
页面不展示根密钥、DEK、`encrypted_dek` 和任何可恢复密钥的内容。
### 6. IM 数据库改造
#### 6.1 `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=NULL`、`encrypted=1`。
- 旧数据:`content IS NOT NULL`、`encrypted=0`。
- 解密后必须恢复为原 `any` JSON 结构,不修改客户端协议。
- `head` 第一阶段不加密,但不得放置消息正文、联系方式等敏感明文。
#### 6.2 `im_group_messages`
`im_group_messages` 是群消息派生查询表,不得另存一份明文。优先方案:
- 仅保存索引、状态、审核和统计字段。
- 通过 `topic + seqid` 关联 `im_messages` 获取密文并解密。
- 迁移后清空或删除派生表中的明文 `content`。
如果必须保留独立内容列,则必须同步改为密文字段,禁止主表已加密而派生表仍保留明文。
#### 6.3 Schema 一致性
- MySQL 普通库和共享库/多商户 adapter 同时改造。
- 新建库 schema、启动 ensure 迁移和手工 SQL 脚本保持一致。
- `types.Message` 和 `types.MessageRevision` 增加密文元数据。
- Save、GetOne、GetAll、编辑、撤回、删除、修订记录和后台查询全部覆盖。
### 7. IM Server 加解密模块
```text
server/messagecrypto/
cipher.go AES-256-GCM加解密
aad.go AAD稳定编码
key_provider.go 密钥获取接口
key_cache.go tenant/version内存缓存
serializer.go content规范序列化
```
不将 AES 逻辑或 HotGo 调用直接写入 MySQL adapter。adapter 负责存储密文模型,加解密由业务存储服务边界统一执行。
#### 7.1 新消息
```text
完成现有校验
-> 完成内容审核
-> 分配seqid并组装AAD
-> 序列化content
-> 使用active DEK加密
-> 事务写入密文和元数据
-> 向在线订阅者广播内存中的原明文
```
审核必须发生在加密之前。广播使用请求内存中的明文,不执行“加密后立即解密”。
#### 7.2 历史消息和离线同步
```text
读取数据库记录
-> encrypted=0:读原content
-> encrypted=1:按tenant + key_version获取DEK
-> 重建AAD
-> GCM验签并解密
-> JSON反序列化
-> 按原Tinode协议返回
```
GCM 验签失败必须当作数据篡改或密钥错误,不得忽略错误或返回部分内容。
### 8. 密钥下发、缓存和轮换
#### 8.1 内部接口
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| 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 确认新密钥可用 |
这些接口只允许 IM Server 服务身份调用,不得暴露给浏览器或通用 Admin API。
#### 8.2 缓存
- IM Server 不得每条消息都跨境请求 HotGo。
- 按 `tenant_id + key_version` 在内存缓存已解密 DEK。
- 新消息使用 `active` 密钥,历史消息按存储版本取密钥。
- 每个 Pod 持有本地缓存,Kafka 事件使全部 Pod 失效。
- HotGo 短时不可用时,已有内存密钥的 Pod 可继续工作。
- 无可用密钥时禁止写入新消息,不得降级为明文落库。
#### 8.3 轮换
1. Admin Server 生成新 DEK,使用 KEK 加密后以 `pending` 入库。
2. 通知 IM Server 拉取、解包并完成加解密自检。
3. IM Server 回传 ACK 后,在事务中将旧版本设为 `retired`,新版本设为 `active`。
4. 新消息立即使用新版本,旧消息仍按旧版本解密。
5. 历史转密钥作为独立低速任务,不阻塞即时通信。
### 9. Admin 后台查看明文
- Admin Server 调用 IM internal admin-query,不直连 IM 库解密。
- IM Server 完成商户数据权限校验后,只解密当前分页数据。
- 记录管理员 ID、商户、IP、查询条件、返回数量、时间和 TraceID。
- 列表解密、详情解密和导出解密使用不同审计动作。
- 默认不提供明文批量导出。后续开放时必须增加独立权限和审批。
### 10. 关键词查询
加密后 MySQL 无法对密文执行 `LIKE '%keyword%'`。第一阶段采用:
1. 查询必须限定商户和时间范围,并尽量限定用户或群号。
2. IM Server 使用明文元数据缩小候选集。
3. 对有上限的候选集批量解密,在内存中执行关键词匹配。
4. 超过时间范围或候选数限制时返回明确提示。
第一版不建设明文分词索引。如后续必须支持全库高性能检索,再单独评审 HMAC 盲索引。
### 11. 历史数据迁移
#### 11.1 发布顺序
1. 发布 HotGo 密钥结构、密钥服务和管理页。
2. 为默认商户初始化第 1 版 DEK,但不开启加密写入。
3. 发布 IM Server 双读版本,验证密钥获取、加解密和所有读写链路。
4. 小流量开启新消息加密,旧数据仍可明文读取。
5. 全量开启新消息加密后,启动历史数据分批迁移。
6. 数量对账、抽样解密和 GCM 验签通过后,逐批清空明文 `content`。
7. 观察期结束后,再决定是否删除原明文字段。
#### 11.2 迁移任务
- 按主键或 `topic + seqid` 分批,不长时锁表。
- 每批独立事务,记录游标、成功数、失败数和最后错误。
- 重复执行幂等,`encrypted=1` 直接跳过。
- 先写密文并验证可解密,再将明文置空。
- 失败记录保留原数据并进入重试列表,不得丢失数据。
- 限制迁移速率,监控数据库负载和即时消息延迟。
### 12. 错误处理
| 错误 | 说明 | 客户端/后台处理 |
| --- | --- | --- |
| `MESSAGE_ENCRYPTION_UNAVAILABLE` | 未取得当前加密密钥 | 暂停新消息写入,不明文降级 |
| `MESSAGE_DECRYPT_FAILED` | 验签或解密失败 | 显示“消息暂时无法加载”,不返回乱码 |
| `MESSAGE_KEY_VERSION_NOT_FOUND` | 历史密钥版本不存在 | 报警并阻止该记录解密 |
客户端不参与加解密,不返回密钥版本、Nonce、密文、解密堆栈或内部错误。
### 13. 监控和审计
- 加密/解密成功数、失败数和耗时。
- 各商户当前密钥版本。
- 密钥获取成功率、缓存命中率和最后刷新时间。
- 明文历史记录剩余数、迁移速率和迁移失败数。
- GCM 验签失败产生高优先级告警。
- 后台明文查看记录管理员、商户、IP、查询范围、返回数量和 TraceID。
- 监控标签和日志不得保存消息正文、密文、Nonce、DEK 或 KEK。
### 14. 开发任务拆分
#### 14.1 Admin Server
1. 根密钥字典隔离和通用接口防护。
2. `im_message_encryption_keys` 及业务逻辑。
3. 消息加密管理页面和最高权限控制。
4. 密钥下发、ACK 和 Kafka 失效事件。
5. 密钥初始化、轮换、退役和审计。
#### 14.2 IM Server
1. AES-256-GCM、AAD 和规范序列化模块。
2. 密钥获取、多版本缓存和多 Pod 失效。
3. 新消息加密写入和双读解密。
4. 单聊、群聊、修订、离线同步和后台查询覆盖。
5. 历史数据迁移、进度统计和失败重试。
6. 加解密监控、验签告警和后台解密审计。
### 15. 测试重点
- 相同明文连续加密两次,Nonce 和密文不同。
- 修改密文、Nonce、AAD 或 `key_version` 任意一项后解密失败。
- JSON 字符串、数组、对象、Drafty、空值和超长内容往返一致。
- 在线发送、离线同步、多端漫游、编辑、回复、撤回和删除行为不变。
- 新旧消息混合分页和历史同步。
- 新旧密钥版本并存和多 Pod 切换。
- HotGo 暂时不可用、IM Pod 重启且无缓存、密钥版本缺失。
- 历史迁移中断、重试、幂等和数量对账。
- 旧密钥退役后可读旧消息,新消息只使用新密钥。
- 日志、Kafka、错误响应和监控中无明文和密钥。
### 16. 验收标准
1. 开启商户的新消息在数据库中不存在可读正文。
2. 相同内容的 Nonce 和密文不同。
3. 篡改密文、Nonce 或 AAD 后必须验签失败。
4. 所有在线和历史消息功能保持原行为。
5. Admin 有权限账号可查看当页解密明文,无权限账号被拒绝。
6. 后台明文查看形成完整审计记录。
7. 旧密钥退役后仍可解密旧消息,新消息只使用新版本。
8. HotGo 短时断开时,有内存密钥的 Pod 可工作;无密钥时不得明文降级。
9. 历史数据迁移完成后,`im_messages`、`im_message_revisions`、`im_group_messages` 无明文副本。
10. 纯 AES 加解密不得成为消息链路主要瓶颈,密钥缓存命中时不产生跨境请求。
### 17. 上线和回滚
1. 第一次只为测试商户或小流量用户开启新消息加密。
2. 加密开关必须按商户生效,不做全局一次性切换。
3. 观察写入失败、解密失败、同步延迟、数据库负载和密钥缓存。
4. 未开始历史迁移前,可停止新加密写入,但已加密消息仍必须保持可解密读取。
5. 禁止通过删除旧密钥实现回滚。
6. 历史迁移开始后,回滚必须保留双读能力,不允许只回滚数据库结构或只回滚代码。
@@ -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

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