Compare commits

..
69 Commits
Author SHA1 Message Date
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
richjohnrambo 331a55751d im 管理后台 2026-08-11 23:13:51 +08:00
richjohnrambo 0397fe5f22 Initial commit 2026-08-11 15:11:11 +00:00
2066 changed files with 233024 additions and 1 deletions
+42
View File
@@ -0,0 +1,42 @@
name: Deploy im-manager (Blue-Green)
on:
workflow_dispatch:
inputs:
image_tag:
description: '镜像tag,留空则使用commit短哈希'
required: false
jobs:
deploy:
runs-on: deploy-host
steps:
- uses: actions/checkout@v4
- name: 确定镜像tag
id: vars
run: |
TAG="${{ github.event.inputs.image_tag }}"
[ -z "$TAG" ] && TAG=$(git rev-parse --short HEAD)
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
- name: 构建镜像(前端+后端一体,自包含多阶段构建)
run: docker build -t im-manager:${{ steps.vars.outputs.tag }} .
- name: 蓝绿部署
env:
MYSQL_DSN: ${{ secrets.MYSQL_DSN }}
REDIS_ADDR: ${{ secrets.REDIS_ADDR }}
REDIS_DB: ${{ secrets.REDIS_DB }}
REDIS_PASSWORD: ${{ secrets.REDIS_PASSWORD }}
TOKEN_SECRET_KEY: ${{ secrets.TOKEN_SECRET_KEY }}
IM_ADMIN_API_BASE_URL: ${{ secrets.IM_ADMIN_API_BASE_URL }}
IM_ADMIN_API_SERVICE_ID: ${{ secrets.IM_ADMIN_API_SERVICE_ID }}
IM_ADMIN_API_SECRET: ${{ secrets.IM_ADMIN_API_SECRET }}
IM_INTERNAL_API_BASE_URL: ${{ secrets.IM_INTERNAL_API_BASE_URL }}
IM_INTERNAL_API_SERVICE_NAME: ${{ secrets.IM_INTERNAL_API_SERVICE_NAME }}
IM_INTERNAL_API_SECRET: ${{ secrets.IM_INTERNAL_API_SECRET }}
IM_CONFIG_API_SERVICE_NAME: ${{ secrets.IM_CONFIG_API_SERVICE_NAME }}
IM_CONFIG_API_SECRET: ${{ secrets.IM_CONFIG_API_SECRET }}
run: |
IMAGE=im-manager:${{ steps.vars.outputs.tag }} /opt/apps/im-manager/deploy.sh
+8
View File
@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
+9
View File
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
+8
View File
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/im_manager.iml" filepath="$PROJECT_DIR$/.idea/im_manager.iml" />
</modules>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
+960
View File
@@ -0,0 +1,960 @@
# HotGo 项目开发规范
**生成或修改任何代码前,必须完整阅读本文档。**
本规范是项目唯一事实来源,所有新增代码必须严格对齐现有模块风格,不得自创新的代码模式。
---
## 技术栈
| 层 | 技术 |
|----|----------------------------------------------------------------|
| 后端语言 | Go,module 名 `hotgo` |
| 后端框架 | GoFrame v2(gdb、ghttp、gcron、gerror、gctx、gerror、gjson、gvar) |
| 前端框架 | Vue 3 + Vite + TypeScript |
| UI 组件库 | **Naive UI**(优先使用,未经允许不得引入其他 UI 库) |
| 状态管理 | Pinia |
| HTTP 客户端 | 项目封装 `@/utils/http/axios`(`http.request`),禁止直接使用 axios |
---
## 后端目录结构
```
server/
├── addons/ # 插件模块(每个插件独立微架构,见"插件开发"章节)
│ ├── modules/ # 隐式注册文件(每个插件一个 .go,仅含 import)
│ └── <addonName>/ # 插件目录,与主模块结构镜像
├── api/admin/<module>/ # 主模块 HTTP 契约:Req/Res + g.Meta 路由声明
├── internal/
│ ├── consts/ # 全局常量与字典选项(枚举在此定义并注册)
│ ├── global/ # 全局公用变量(谨慎添加,尽量少用)
│ ├── controller/admin/sys/ # 薄控制器,只做参数透传,不含业务逻辑
│ ├── crons/ # 主模块定时任务注册与处理
│ ├── queues/ # 主模块消息队列消费者注册与处理
│ ├── logic/sys/ # 业务实现(sSysXxx),init() 注册到 service
│ ├── library/ # 内部基础库(addons、dict、hgorm、cache、queue、cron 等)
│ ├── service/ # 接口声明(ISysXxx)+ 注册函数
│ ├── dao/ # 对外 DAO;internal/dao/internal/ 为 CLI 生成,禁止手改
│ ├── model/
│ │ ├── entity/ # 表结构体(CLI 生成,禁止手改)
│ │ ├── do/ # ORM 写入结构(CLI 生成,禁止手改)
│ │ └── input/sysin/ # 业务入参、出参、过滤结构
│ └── router/
│ ├── admin.go # 主路由,末尾调用 genrouter.Register()
│ └── genrouter/ # 每个生成模块一个文件,init() 追加到 LoginRequiredRouter
├── resource/
│ ├── generate/default/curd/ # 主模块 CRUD 代码生成模板(参考,禁止直接修改)
│ └── generate/default/addon/ # 插件脚手架代码生成模板(参考,禁止直接修改)
└── utility/ # 通用工具:convert、excel、validate 等
```
---
## 后端分层约定
### 分层职责(禁止跨层调用)
```
api(HTTP契约) → controller(参数透传) → service(接口) → logic(业务实现) → dao(数据访问)
```
- **controller 层**:不写任何业务逻辑,只转调 service
- **logic 层**:不出现 HTTP 相关代码,所有校验通过 `Filter()` 或 `g.Validator()` 完成
- **dao 层**:`internal/dao/internal/` 由 CLI 自动生成,禁止手改
---
### 1. API 层 `api/admin/<module>/<module>.go`
- 每个接口一对 `XxxReq` / `XxxRes`,`g.Meta` 声明路由
- GET 接口用 `method:"get"`,写操作用 `method:"post"`
- 请求体嵌入 `sysin.XxxInp`,响应体嵌入对应 Model 或空 `struct{}`
```go
type ListReq struct {
g.Meta `path:"/xxxModule/list" method:"get" tags:"XXX模块" summary:"获取XXX列表"`
sysin.XxxListInp
}
type ListRes struct {
form.PageRes
List []*sysin.XxxListModel `json:"list" dc:"数据列表"`
}
```
### 2. Controller 层 `internal/controller/admin/sys/<module>.go`
- 变量:`var XxxModule = cXxxModule{}`,类型:`type cXxxModule struct{}`
- 只做参数转发和结果组装,禁止直接操作 dao 或写业务逻辑
```go
func (c *cXxxModule) List(ctx context.Context, req *xxxmodule.ListReq) (res *xxxmodule.ListRes, err error) {
list, totalCount, err := service.SysXxxModule().List(ctx, &req.XxxListInp)
if err != nil {
return
}
if list == nil {
list = []*sysin.XxxListModel{}
}
res = new(xxxmodule.ListRes)
res.List = list
res.PageRes.Pack(req, totalCount)
return
}
```
### 3. Service 层 `internal/service/sys.go`
- 在已有文件中**追加**接口声明和注册函数,禁止新建文件
- 声明 `ISysXxxModule` 接口,提供 `SysXxxModule()` 获取函数和 `RegisterSysXxxModule()` 注册函数
### 4. Logic 层 `internal/logic/sys/<module>.go`
- 结构体:`type sSysXxxModule struct{}`
- `init()` 里调用 `service.RegisterSysXxxModule(NewSysXxxModule())`
- `Model()` 方法:`return handler.Model(dao.XxxTable.Ctx(ctx), option...)`
- 列名引用必须用 `dao.XxxTable.Columns().FieldName`,禁止硬编码字符串字段名
- `Edit` 方法以 `in.Id > 0` 区分更新/新增,更新用 `XxxUpdateFields`,新增用 `XxxInsertFields`
- 字段校验在 `sysin` 的 `Filter(ctx)` 方法完成,logic 层不重复校验
### 5. Model 层 `internal/model/input/sysin/<module>.go`
| 类型 | 用途 |
|------|------|
| `XxxUpdateFields` | 更新时字段白名单 |
| `XxxInsertFields` | 新增时字段白名单 |
| `XxxEditInp` | 嵌入 `entity.XxxTable`,含 `Filter()` 校验方法 |
| `XxxListInp` | 列表查询入参,嵌入 `form.PageInp` |
| `XxxListModel` | 列表返回字段 |
| `XxxViewInp` / `XxxViewModel` | 详情入参/出参 |
| `XxxDeleteInp` | 删除入参(通常含 `Ids []int64`) |
| `XxxStatusInp`、`XxxMaxSortInp` 等 | 其他操作 |
### 6. 路由注册 `internal/router/genrouter/<module>.go`
```go
package genrouter
import "hotgo/internal/controller/admin/sys"
func init() {
LoginRequiredRouter = append(LoginRequiredRouter, sys.XxxModule) // XXX模块
}
```
新建此文件即可,`genrouter/init.go` 通过 `init()` 链自动加载,无需修改 `admin.go`。
---
## 字典与枚举规范
**字典由后端统一维护**,前端通过固定接口拉取,禁止在前端硬编码枚举值。
### 后端:定义与注册(`internal/consts/` 下对应文件)
```go
// 1. 定义常量
const (
XxxStatusEnabled = 1 // 启用
XxxStatusDisable = 2 // 禁用
)
// 2. 定义选项(选择合适的样式函数)
var XxxStatusOptions = []*model.Option{
dict.GenSuccessOption(XxxStatusEnabled, "启用"),
dict.GenWarningOption(XxxStatusDisable, "禁用"),
}
// 3. 在 init() 中注册(同文件)
func init() {
dict.RegisterEnums("XxxStatusOptions", "XXX状态选项", XxxStatusOptions)
}
```
**样式函数选择规则:**
| 函数 | 适用场景 |
|------|---------|
| `dict.GenSuccessOption` | 正常、启用、成功 |
| `dict.GenWarningOption` | 待处理、禁用、警告 |
| `dict.GenErrorOption` | 失败、封禁、危险 |
| `dict.GenInfoOption` | 信息类、中性状态 |
| `dict.GenPrimaryOption` | 主要操作、强调 |
| `dict.GenDefaultOption` | 默认、无特殊含义 |
| `dict.GenHashOption` | 不确定数量的动态枚举 |
### 后端:Logic 层中使用字典
```go
// 获取标签文本
label := dict.GetOptionLabel(consts.XxxStatusOptions, in.Status)
// 校验是否为合法值
if !dict.HasOptionKey(consts.XxxStatusOptions, in.Status) {
return gerror.New("状态值不合法")
}
```
### 前端:加载与使用字典
**加载**(在 `model.ts` 的 `loadOptions()` 中):
```ts
export function loadOptions() {
dict.loadOptions(['XxxStatusOptions', 'sys_normal_disable']);
}
```
**在 `model.ts` 的 columns 中渲染字典标签:**
```ts
import { useDictStore } from '@/store/modules/dict';
const dict = useDictStore();
// columns 中
{
title: '状态',
key: 'status',
render(row) {
return dict.getLabel('XxxStatusOptions', row.status);
}
}
```
**在搜索表单 schemas 中使用字典下拉:**
```ts
{
field: 'status',
component: 'NSelect',
label: '状态',
componentProps: {
options: computed(() => dict.getOptions('XxxStatusOptions')),
placeholder: '请选择状态',
},
}
```
---
## 插件(Addon)开发规范
> 定位:独立、临时性、工具类型的功能推荐插件化开发,例如小游戏、广告管理、文章管理、小程序、微商城等。插件之间完全隔离,方便多项目复用。
### 插件目录结构
每个插件拥有完整的独立微架构,与主模块结构镜像,但 **包路径前缀为 `hotgo/addons/<addonName>/`**:
```
server/addons/<addonName>/
├── main.go # 插件入口:定义 module 结构体,实现 Module 接口,init() 注册
├── global/
│ ├── global.go # 插件级全局变量
│ └── init.go # Init(ctx, skeleton),GetSkeleton() 方法
├── api/
│ ├── admin/ # 后台接口契约(同主模块 api 写法)
│ ├── api/ # 前台 API 接口契约
│ └── home/ # 首页接口契约(如有)
├── controller/
│ ├── admin/sys/ # 后台薄控制器
│ ├── api/ # 前台控制器
│ └── home/ # 首页控制器(如有)
├── logic/
│ ├── logic.go # 匿名导入各 logic 子包触发 init()
│ └── sys/ # 业务实现(同主模块 logic 写法)
├── service/ # 插件内接口声明与注册(独立,不与主模块 service 混用)
├── model/
│ └── input/sysin/ # 插件内入参/出参(调用主模块服务时继承主模块 input 结构)
├── router/
│ ├── admin.go # 注册后台路由,使用 addons.RouterPrefix() 获取路由前缀
│ ├── api.go # 注册前台路由
│ ├── home.go # 注册首页路由(如有)
│ ├── websocket.go # 注册 WebSocket 路由(如有)
│ └── genrouter/
│ ├── init.go # 插件版 genrouter:Register() 使用插件前缀
│ └── *.go # 每个生成模块一个文件,追加到插件 LoginRequiredRouter
├── consts/ # 插件内常量与字典(同主模块 consts 写法)
├── crons/ # 插件定时任务(可选)
├── queues/ # 插件消息队列消费者(可选)
└── resource/ # 插件静态资源与模板(可选)
```
**对应前端目录:**
```
web/src/
├── api/addons/<addonName>/ # 插件前端 API 封装
└── views/addons/<addonName>/ # 插件前端页面
```
### 插件入口 `main.go`
```go
package <addonName>
import (
"context"
"github.com/gogf/gf/v2/net/ghttp"
_ "hotgo/addons/<addonName>/crons"
"hotgo/addons/<addonName>/global"
_ "hotgo/addons/<addonName>/logic"
_ "hotgo/addons/<addonName>/queues"
"hotgo/addons/<addonName>/router"
"hotgo/internal/library/addons"
"hotgo/internal/service"
"sync"
)
type module struct {
skeleton *addons.Skeleton
ctx context.Context
sync.Mutex
}
func init() { newModule() }
func newModule() {
m := &module{
skeleton: &addons.Skeleton{
Label: "插件显示名",
Name: "<addonName>", // 与目录名一致
Group: 1,
Brief: "简介",
Description: "详细描述",
Author: "作者",
Version: "v1.0.0",
},
ctx: gctx.New(),
}
addons.RegisterModule(m)
}
func (m *module) Start(option *addons.Option) (err error) {
global.Init(m.ctx, m.skeleton)
option.Server.Group("/", func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().Addon)
router.Admin(m.ctx, group)
router.Api(m.ctx, group)
})
return
}
// 其余方法:Stop、Ctx、GetSkeleton、Install、Upgrade、UnInstall(默认留空)
```
### 插件路由前缀规则
```
后台:/admin/<addonName>/接口路径 → 对应 controller/admin/sys/
前台:/api/<addonName>/接口路径 → 对应 controller/api/
首页:/home/<addonName>/接口路径 → 对应 controller/home/
WebSocket:/socket/<addonName>/接口路径
```
路由前缀通过 `addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)` 自动生成,**禁止在路由文件里硬编码路径前缀**。
### 插件 genrouter 与主模块的区别
插件 `genrouter/init.go` 的 `Register()` 函数需要传入 `ctx` 和 `group`,并使用插件路由前缀:
```go
// 插件 genrouter/init.go
func Register(ctx context.Context, group *ghttp.RouterGroup) {
prefix := addons.RouterPrefix(ctx, consts.AppAdmin, global.GetSkeleton().Name)
group.Group(prefix, func(group *ghttp.RouterGroup) {
group.Middleware(service.Middleware().AdminAuth)
if len(LoginRequiredRouter) > 0 {
group.Bind(LoginRequiredRouter...)
}
})
}
// 插件 genrouter/<module>.go
func init() {
LoginRequiredRouter = append(LoginRequiredRouter, sys.XxxModule)
}
```
参考实现:`server/addons/hgexample/router/genrouter/`
### 插件调用主模块服务
**在插件 input 层继承主模块 input 结构,解耦参数依赖**,避免 import cycle:
```go
// 插件 model/input/sysin/config.go
package sysin
import "hotgo/internal/model/input/sysin"
type UpdateConfigInp struct {
sysin.UpdateAddonsConfigInp
}
```
```go
// 插件 logic 中调用主模块服务
import isc "hotgo/internal/service"
func (s *sSysConfig) UpdateConfigByGroup(ctx context.Context, in sysin.UpdateConfigInp) error {
in.UpdateAddonsConfigInp.AddonName = global.GetSkeleton().Name
return isc.SysAddonsConfig().UpdateConfigByGroup(ctx, in.UpdateAddonsConfigInp)
}
```
### 插件获取自身信息
```go
// 插件内部
global.GetSkeleton()
// 任意位置判断当前请求是否为插件请求
contexts.IsAddonRequest(ctx)
contexts.GetAddonName(ctx)
```
### 插件隐式注册
在 `server/addons/modules/` 下新建 `<addonName>.go`,内容仅含一行匿名导入:
```go
package modules
import _ "hotgo/addons/<addonName>"
```
### 新增插件检查清单
**后端:**
- [ ] `addons/<addonName>/main.go` — 插件入口,实现 Module 接口
- [ ] `addons/<addonName>/global/` — global.go + init.go
- [ ] `addons/modules/<addonName>.go` — 隐式注册
- [ ] `addons/<addonName>/service/` — 插件内接口声明(独立包)
- [ ] `addons/<addonName>/logic/logic.go` — 匿名导入触发 init()
- [ ] `addons/<addonName>/logic/sys/<module>.go` — 业务实现
- [ ] `addons/<addonName>/model/input/sysin/<module>.go` — 入参/出参
- [ ] `addons/<addonName>/api/admin/<module>/<module>.go` — API 契约
- [ ] `addons/<addonName>/controller/admin/sys/<module>.go` — 薄控制器
- [ ] `addons/<addonName>/router/admin.go` — 路由注册
- [ ] `addons/<addonName>/router/genrouter/init.go` — 插件 genrouter
- [ ] `addons/<addonName>/router/genrouter/<module>.go` — 模块路由追加
**前端:**
- [ ] `web/src/api/addons/<addonName>/index.ts` — API 封装
- [ ] `web/src/views/addons/<addonName>/model.ts` — State、schemas、columns
- [ ] `web/src/views/addons/<addonName>/index.vue` — 列表页
- [ ] `web/src/views/addons/<addonName>/edit.vue` — 编辑页
参考实现:`server/addons/hgexample/`(完整插件示例)、`server/resource/generate/default/addon/`(生成模板)
---
## 标准 CRUD 接口清单
| 接口 | method | 说明 |
|------|--------|------|
| `/xxx/list` | GET | 分页列表 |
| `/xxx/view` | GET | 详情 |
| `/xxx/edit` | POST | 新增/编辑(id>0 为编辑) |
| `/xxx/delete` | POST | 删除(支持批量,传 ids) |
| `/xxx/status` | POST | 启用/禁用状态切换 |
| `/xxx/switch` | POST | 开关字段切换 |
| `/xxx/maxSort` | GET | 获取最大排序值 |
| `/xxx/export` | GET | 导出列表 |
按实际需求裁剪,不强制全部实现。
---
## 前端目录结构
```
web/src/
├── api/<module>/index.ts # HTTP 请求封装
├── views/<module>/
│ ├── index.vue # 列表页
│ ├── edit.vue # 新增/编辑弹窗
│ └── model.ts # State 类、rules、schemas、columns、loadOptions
├── components/ # 通用组件(BasicTable、BasicForm、BasicModal 等)
├── store/modules/dict.ts # 字典 store(loadOptions、getLabel、getOptions)
└── utils/
├── http/axios.ts # HTTP 封装(http.request / jumpExport)
└── dateUtil.ts # 日期工具(defRangeShortcuts 等)
```
---
## 前端编码约定
### API 文件 `api/<module>/index.ts`
- 使用 `http.request`,禁止直接使用 axios
- 函数名与接口语义一致:`List`、`Edit`、`Delete`、`View`、`Status`、`Switch`、`Export`、`MaxSort`
- 导出接口用 `jumpExport('/xxx/export', params)`
```ts
import { http, jumpExport } from '@/utils/http/axios';
export function List(params) {
return http.request({ url: '/xxxModule/list', method: 'get', params });
}
export function Edit(params) {
return http.request({ url: '/xxxModule/edit', method: 'POST', params });
}
export function Export(params) {
jumpExport('/xxxModule/export', params);
}
```
### model.ts 结构
- `class State`:属性与后端 entity 字段对应,提供合理默认值
- `newState()`:工厂函数,支持深拷贝(`cloneDeep`)
- `rules`:Naive UI 表单校验规则
- `schemas`:搜索栏 `FormSchema[]`(`ref` 包装)
- `columns`:表格列定义,含权限守卫的操作用 `hasPermission(['/xxx/edit'])`
- `loadOptions()`:调用 `dict.loadOptions([...])`,预加载字典数据
### 组件使用规范
**必须优先使用 Naive UI 原生组件**,能用 Naive UI 实现的禁止自行封装替代组件:
| 场景 | 必须使用的组件 |
|------|--------------|
| 表格 | 项目封装 `BasicTable`(基于 Naive UI) |
| 表单 | 项目封装 `BasicForm`(基于 Naive UI) |
| 弹窗 | `n-modal` 或项目封装 `BasicModal` |
| 按钮 | `n-button`,类型用 `primary`/`default`/`error` 等语义值 |
| 选择器 | `n-select`(字典下拉)、`n-tree-select`(树形) |
| 开关 | `n-switch`,配合 Switch 接口 |
| 输入框 | `n-input` |
| 日期选择 | `n-date-picker`,范围选择配合 `defRangeShortcuts()` |
| 图片渲染 | `renderImage(url)` |
| 文件渲染 | `renderFile(url)` |
| 成员信息 | `renderPopoverMemberSumma(summa)` |
**禁止行为:**
- 禁止引入 Element Plus、Ant Design Vue 等其他 UI 库
- 禁止使用内联 style(使用 Naive UI 的 props 或 class)
- 禁止在模板里硬编码字典值文本,必须通过 `dict.getLabel()` 渲染
### 权限控制
```ts
import { usePermission } from '@/hooks/web/usePermission';
const { hasPermission } = usePermission();
// 模板中
v-if="hasPermission(['/xxxModule/edit'])"
// columns 中开关操作
disabled: !hasPermission(['/xxxModule/switch'])
```
权限标识与后端路由路径保持一致,**路由由后端菜单数据驱动,禁止在源码里硬编码路由路径**。
### Vue 3 代码规范
- 使用 Composition API(`<script setup>`)
- 响应式数据用 `ref` / `reactive`,计算属性用 `computed`
- 字典选项传给组件时用 `computed(() => dict.getOptions('XxxOptions'))`,保持响应式
---
## 新增 CRUD 模块检查清单
**后端(必须全部完成,顺序建议从下往上):**
- [ ] `internal/consts/<module>.go` — 常量定义、选项变量、`init()` 注册字典
- [ ] 使用 goframe CLI 生成:`entity`、`do`、`dao/internal`、`dao`(禁止手写)
- [ ] `internal/model/input/sysin/<module>.go` — 入参/出参/过滤模型
- [ ] `internal/service/sys.go` — 追加接口声明与注册函数
- [ ] `internal/logic/sys/<module>.go` — 业务实现
- [ ] `internal/controller/admin/sys/<module>.go` — 薄控制器
- [ ] `api/admin/<module>/<module>.go` — API 契约
- [ ] `internal/router/genrouter/<module>.go` — 路由注册(`LoginRequiredRouter` 追加)
**前端(必须全部完成):**
- [ ] `web/src/api/<module>/index.ts` — HTTP 封装
- [ ] `web/src/views/<module>/model.ts` — State、schemas、columns、rules、loadOptions
- [ ] `web/src/views/<module>/index.vue` — 列表页
- [ ] `web/src/views/<module>/edit.vue` — 编辑页
---
## 参考实现
**所有新模块必须严格对齐以下文件的风格,生成前请逐一阅读:**
**主模块 CRUD 参考:**
| 文件 | 说明 |
|------|------|
| `server/internal/consts/status.go` | 字典常量定义与注册标准写法 |
| `server/api/admin/curddemo/curddemo.go` | API 契约标准写法 |
| `server/internal/controller/admin/sys/curd_demo.go` | 薄控制器标准写法 |
| `server/internal/logic/sys/curd_demo.go` | Logic 标准写法(含关联查询、导出、字段过滤) |
| `server/internal/model/input/sysin/curd_demo.go` | 入参/出参模型标准写法 |
| `server/internal/router/genrouter/curd_demo.go` | 主模块路由注册标准写法 |
| `web/src/api/curdDemo/index.ts` | 前端 API 封装标准写法 |
| `web/src/views/curdDemo/model.ts` | 前端 model 标准写法(含字典加载) |
| `web/src/views/curdDemo/index.vue` | 列表页标准写法 |
| `web/src/views/curdDemo/edit.vue` | 编辑页标准写法 |
**插件参考(以 hgexample 为标准):**
| 文件 | 说明 |
|------|------|
| `server/addons/hgexample/main.go` | 插件入口标准写法 |
| `server/addons/hgexample/global/init.go` | 插件 global 标准写法 |
| `server/addons/hgexample/router/admin.go` | 插件路由注册标准写法 |
| `server/addons/hgexample/router/genrouter/init.go` | 插件 genrouter 标准写法 |
| `server/addons/hgexample/router/genrouter/tenant_order.go` | 插件模块路由追加标准写法 |
| `server/addons/hgexample/logic/sys/tenant_order.go` | 插件业务逻辑标准写法 |
| `server/addons/hgexample/logic/sys/config.go` | 插件调用主模块服务标准写法 |
代码生成模板:
- 主模块 CRUD:`server/resource/generate/default/curd/`
- 插件脚手架:`server/resource/generate/default/addon/`
以上模板可参考逻辑,**禁止直接修改模板文件**。
---
## 错误处理规范
### 基本原则
- 业务错误直接用 `gerror.New("描述清晰的中文信息")` 返回,GoFrame 框架会统一处理响应码
- 错误信息要面向用户,清晰说明**发生了什么**,而不是堆栈路径
### 错误创建方式
```go
import "github.com/gogf/gf/v2/errors/gerror"
// 普通业务错误(最常用)
return gerror.New("用户名已存在")
// 包装底层错误,追加上下文(调用第三方/dao 层出错时使用)
if err := dao.User.Ctx(ctx).Data(data).Insert(); err != nil {
return gerror.Wrap(err, "创建用户失败")
}
// 带格式化参数
return gerror.Newf("订单 %d 状态不合法,当前状态:%d", in.Id, order.Status)
```
### 错误传递原则
- **logic 层**:遇到底层错误用 `gerror.Wrap` 追加上下文后返回;纯业务校验失败用 `gerror.New` 直接返回
- **controller 层**:直接 `return` 透传 logic 的 error,**不再包装**,不打印日志(框架统一处理)
- **禁止**在 controller 层 `fmt.Println(err)` 或 `g.Log().Error` 打印已经会被框架记录的错误
---
## 缓存使用规范
### 统一使用 `cache.Instance()`
**禁止直接调用 `g.Redis().Do()`**,项目统一通过 `cache.Instance()` 操作缓存,底层适配器(Memory / Redis / File)由配置决定,业务层无需关心。
```go
import (
"hotgo/internal/library/cache"
"github.com/gogf/gf/v2/os/gcache"
"time"
)
// 设置缓存(带过期时间)
err := cache.Instance().Set(ctx, "user:info:1", userInfo, 30*time.Minute)
// 获取缓存
val, err := cache.Instance().Get(ctx, "user:info:1")
if err != nil || val.IsNil() {
// 缓存未命中,查数据库
}
// 删除缓存
_, err = cache.Instance().Remove(ctx, "user:info:1")
// GetOrSet:缓存不存在时自动执行函数并写入(推荐用于只读类缓存)
val, err := cache.Instance().GetOrSet(ctx, "user:info:1", func(ctx context.Context) (interface{}, error) {
return dao.AdminMember.Ctx(ctx).Where(...).One()
}, 30*time.Minute)
```
### 缓存 Key 命名规范
- 格式:`模块:实体:唯一标识`,例如:`user:info:1`、`config:site:basic`、`dict:options:XxxStatus`
- 同一模块的 Key 统一在 `internal/consts/cache.go`(或对应模块的 consts 文件)中定义为常量,禁止散落在 logic 里硬编码
- 插件缓存 Key 加插件名前缀:`hgexample:order:1`
### 缓存使用原则
- 更新或删除数据后,**主动清除**相关缓存,不依赖过期自动失效
- 缓存时间:频繁更新的数据(配置、状态)用较短时间(5~30 分钟);基本不变的数据(字典、枚举)可用更长时间
- 缓存穿透场景:查询结果为空时也写入短时缓存(如 1 分钟)防止击穿
---
## 日志规范
### 日志级别选择
| 级别 | 方法 | 适用场景 |
|------|------|---------|
| Info | `g.Log().Info` / `g.Log().Infof` | 正常业务流程的关键节点(登录成功、订单创建、支付回调等) |
| Warning | `g.Log().Warning` / `g.Log().Warningf` | **遇到错误但不影响主流程**(重试、降级、外部服务异常、非致命校验失败) |
| Panic | `g.Log().Panic` / `g.Log().Panicf` | 极其严重、必须立即终止的情况(配置缺失、核心依赖不可用) |
### 使用示例
```go
// Info:记录正常流程关键事件
g.Log().Infof(ctx, "用户登录成功 uid:%d ip:%s", member.Id, ip)
// Info:记录外部调用结果
g.Log().Info(ctx, "支付回调处理完成", g.Map{"orderId": orderId, "status": status})
// Warning:外部接口失败但已降级处理
g.Log().Warningf(ctx, "短信发送失败,已跳过 mobile:%s err:%+v", mobile, err)
// Warning:业务异常但不中断流程
g.Log().Warning(ctx, "用户余额不足,跳过自动扣款", g.Map{"uid": uid, "balance": balance})
// Panic:启动时核心配置缺失
g.Log().Panicf(ctx, "缓存未初始化,无法启动: %+v", err)
```
### 日志上下文原则
- 日志**必须传入 `ctx`**(第一个参数),便于链路追踪
- 关键日志需携带足够的业务字段(uid、orderId、requestId 等),用 `g.Map{}` 附加结构化信息
- **禁止**用 `fmt.Println` 替代日志,禁止在 logic/controller 层裸打印调试信息
---
## 数据库建表约定
建表时遵循以下字段命名约定,代码生成器和 ORM hook 会自动识别并处理:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `id` | bigint(20) NOT NULL AUTO_INCREMENT | 主键,必须 |
| `created_at` | datetime | 创建时间,**自动写入**,禁止手动赋值 |
| `updated_at` | datetime | 更新时间,**自动维护**,禁止手动赋值 |
| `deleted_at` | datetime | 软删除时间,存在时查询自动加 `IS NULL`,删除只更新此字段 |
| `created_by` | bigint(20) | 创建者 ID,**自动写入** |
| `updated_by` | bigint(20) | 更新者 ID,**自动维护** |
| `deleted_by` | bigint(20) | 删除者 ID,**自动写入** |
| `status` | tinyint | 状态字段,默认使用系统字典 `sys_normal_disable` |
| `sort` | int | 排序字段,存在时编辑表单自动获取最大值+1填充 |
| `pid` | bigint(20) | 树表上级ID(树表必须) |
| `level` | int | 树等级(树表必须,自动维护) |
| `tree` | varchar(512) | 关系树(树表必须,自动维护) |
| `tenant_id` | bigint(20) | 租户ID(多租户场景按需加) |
**特殊字段查询方式(代码生成器默认生成):**
- `string` 类型字段 → `LIKE` 模糊查询
- `int/uint/int64` → `=` 精确查询
- `created_by`/`updated_by`/`deleted_by` → 关键词查询(ID/用户名/手机号匹配),调用 `service.AdminMember().GetIdsByKeyword()`
**数据类型 → Go 类型映射(影响生成代码的字段类型):**
- `tinyint/int/smallint` → `int`;加 `unsigned` → `uint`
- `bigint` → `int64`;加 `unsigned` → `uint64`
- `varchar/text/char` → `string`
- `datetime/timestamp` → `*gtime.Time`
- `json/jsonb` → `*gjson.Json`
- `decimal/float/double` → `float64`
---
## 数据权限(权限过滤)
业务查询中如需过滤数据权限(仅自己/所属部门/下级等),在 ORM 链式调用中加入 handler:
```go
import "hotgo/internal/library/hgorm/handler"
// 按登录用户的数据权限范围过滤(表需含 created_by 或 member_id 字段)
dao.XxxTable.Ctx(ctx).Handler(handler.FilterAuth).Scan(&res)
// 表中无标准字段时,指定自定义字段
dao.XxxTable.Ctx(ctx).Handler(handler.FilterAuthWithField("custom_field")).Scan(&res)
// 多租户数据权限过滤(表需含 tenant_id 字段)
// 在 Model() 方法中通过 handler.Option 开启
func (s *sSysXxx) Model(ctx context.Context, option ...*handler.Option) *gdb.Model {
if len(option) == 0 {
option = append(option, &handler.Option{FilterTenant: true})
}
return handler.Model(dao.XxxTable.Ctx(ctx), option...)
}
```
多租户增改时自动维护租户关系,在 ORM 操作后加 `.Hook(hook.SaveTenant)`:
```go
import "hotgo/internal/library/hgorm/hook"
// 新增时自动写入 tenant_id/merchant_id/user_id
dao.XxxTable.Ctx(ctx).Fields(sysin.XxxInsertFields{}).Hook(hook.SaveTenant).Data(in).Insert()
// 更新时自动维护
s.Model(ctx).Fields(sysin.XxxUpdateFields{}).WherePri(in.Id).Data(in).Hook(hook.SaveTenant).Update()
```
---
## 定时任务(Crons)
定时任务通过实现接口 + `init()` 注册,在后台「系统设置 → 定时任务」中配置执行策略:
**主模块**:新建 `server/internal/crons/<name>.go`
**插件**:新建 `server/addons/<addonName>/crons/<name>.go`
```go
package crons
import (
"context"
"hotgo/internal/library/cron"
)
func init() {
cron.Register(MyTask)
}
var MyTask = &cMyTask{name: "myTask"} // name 与后台配置的任务名称一致
type cMyTask struct{ name string }
func (c *cMyTask) GetName() string { return c.name }
func (c *cMyTask) Execute(ctx context.Context, parser *cron.Parser) (err error) {
parser.Logger.Infof(ctx, "任务执行:%v", ...)
return
}
```
---
## 消息队列(Queues)
队列采用接口 + `init()` 注册,topic 统一定义在 `internal/consts/queue.go`(或对应模块 consts 文件)。
**主模块**:新建 `server/internal/queues/<name>.go`
**插件**:新建 `server/addons/<addonName>/queues/<name>.go`
```go
package queues
import (
"context"
"encoding/json"
"hotgo/internal/consts"
"hotgo/internal/library/queue"
)
func init() {
queue.RegisterConsumer(MyConsumer)
}
var MyConsumer = &qMyConsumer{}
type qMyConsumer struct{}
func (q *qMyConsumer) GetTopic() string { return consts.QueueMyTopic }
func (q *qMyConsumer) Handle(ctx context.Context, mqMsg queue.MqMsg) (err error) {
var data MyData
if err = json.Unmarshal(mqMsg.Body, &data); err != nil {
return
}
// 处理消息...
return
}
```
**发送消息:**
```go
// 即时消息
queue.Push(consts.QueueMyTopic, data)
// 延迟消息(仅 redis/rocketmq 驱动支持)
queue.SendDelayMsg(consts.QueueMyTopic, data, 10) // redis: 延迟秒数;rocketmq: 延迟级别
```
---
## 工具库使用规范
**后端(`server/utility/`)优先使用已有工具,禁止重复造轮子:**
| 包 | 用途 |
|----|------|
| `utility/convert` | 数据类型转换 |
| `utility/encrypt` | 加密/解密 |
| `utility/excel` | 电子表格导出/导入(导出接口用此包) |
| `utility/validate` | 数据验证工具 |
| `utility/tree` | 树形结构处理 |
| `utility/simple` | 简捷函数集合 |
| `utility/format` | 数据格式化 |
**前端(`web/src/utils/`)优先使用已有工具:**
| 工具 | 用途 |
|------|------|
| `@/utils/http/axios`(`http.request`) | HTTP 请求,**唯一入口** |
| `@/utils/dateUtil`(`defRangeShortcuts`) | 日期范围快捷选项 |
| `@/utils`(`renderImage`、`renderFile`、`renderPopoverMemberSumma`) | 表格渲染工具函数 |
| `@/store/modules/dict`(`useDictStore`) | 字典数据加载与渲染 |
| `@/hooks/web/usePermission`(`hasPermission`) | 权限判断 |
---
## 树表(Tree CRUD)特殊约定
树表在普通 CRUD 基础上有以下差异:
- 表中必须含 `pid`、`level`、`tree` 字段(参见建表约定)
- `pid`/`level`/`tree` 字段**由系统自动维护**,禁止在编辑表单中手动设置这三个字段
- 前端列表页使用树形表格(`BasicTable` 的 `childrenKey` 或 `treeData` 模式),而非普通分页列表
- 参考实现:`server/internal/logic/sys/tree_demo.go`、`web/src/views/develop/curd/treeDemo/`
---
## 响应格式约定
所有接口统一响应格式(由框架中间件自动处理,**无需在 controller 手动包装**):
```json
{ "code": 0, "message": "操作成功", "timestamp": 1234567890, "traceID": "xxx", "data": {} }
```
- 成功状态码:`0`;失败状态码:`-1`(GoFrame 内置,无需手动设置)
- `gerror.Wrap(err, "用户友好的提示")` → 客户端只看到最外层提示,开发者日志可见完整堆栈
- 禁止在 controller 层手动 `Write`/`WriteJSON` 等直接写响应(破坏统一格式)
---
## 常见错误禁止清单
**后端:**
- 禁止在 controller 层直接调用 dao
- 禁止硬编码字段名字符串(用 `dao.Table.Columns().Field`)
- 禁止手改 `entity/`、`do/`、`dao/internal/` 目录下的文件
- 禁止在 logic 层重复做 `Filter()` 已完成的校验
- 禁止在 `internal/service/sys.go` 之外新建主模块 service 文件(同文件追加)
- 禁止插件 import 另一个插件的包(插件间完全隔离,通过主模块 service 通信)
- 禁止在插件路由文件里硬编码路由前缀(用 `addons.RouterPrefix()` 生成)
- 禁止在 `internal/library/` 下修改基础库(如需扩展,提 issue 或在 utility/ 添加)
**前端:**
- 禁止直接使用 axios,必须用 `http.request`
- 禁止硬编码字典值文本,必须用 `dict.getLabel()` 渲染
- 禁止引入 Naive UI 以外的 UI 框架
- 禁止内联 style
- 禁止在源码里硬编码菜单路由路径
+67
View File
@@ -0,0 +1,67 @@
# ---- 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 does `import _ "hotgo/internal/packed"`, a file normally generated by
# `gf build` (see server/hack/config.yaml: packSrc/packDst) and gitignored —
# generate it explicitly with the same gf CLI version pinned in go.mod.
RUN go install github.com/gogf/gf/cmd/gf/v2@v2.10.0
RUN gf pack resource internal/packed/packed.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=64.118.137.182:9094
ENV KAFKA_VERSION=2.4.0
ENV KAFKA_USERNAME=kafka-external
ENV KAFKA_PASSWORD=gr58tNnlZOVGW3AmMbL2
ENV KAFKA_SASL_MECHANISM=SCRAM-SHA-512
ENV IM_ADMIN_API_BASE_URL=http://im-server:6060
ENV IM_ADMIN_API_SERVICE_ID=hotgo-admin
ENV IM_ADMIN_API_SECRET=8569f5626a54950539eece9f731c9e3bf073daa74af8d23735d19afacac40f4b
ENV IM_INTERNAL_API_BASE_URL=http://im-server:6060
ENV IM_INTERNAL_API_SERVICE_NAME=hotgo-admin
ENV IM_INTERNAL_API_SECRET=8569f5626a54950539eece9f731c9e3bf073daa74af8d23735d19afacac40f4b
ENV IM_CONFIG_API_SERVICE_NAME=im-server
ENV IM_CONFIG_API_SECRET=b96832c04dbfb6dd768930ec9f082ecd90d87a1ffdaa493aa070617c48de9097
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"]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-present HotGo
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+166 -1
View File
@@ -1,2 +1,167 @@
# im_manager
# HotGo-V2
<div align="center">
<img width="140px" src="https://gmycos.facms.cn/hotgo/logo.sig.png">
<p>
<h1>HotGo V2</h1>
</p>
<p align="center">
<a href="https://goframe.org/pages/viewpage.action?pageId=1114119" target="_blank">
<img src="https://img.shields.io/badge/goframe-2.10.0-green" alt="goframe">
</a>
<a href="https://v3.vuejs.org/" target="_blank">
<img src="https://img.shields.io/badge/vue.js-vue3.4-green" alt="vue">
</a>
<a href="https://www.naiveui.com" target="_blank">
<img src="https://img.shields.io/badge/naiveui-%3E2.44.1-blue" alt="naiveui">
</a>
<a href="https://www.tslang.cn/" target="_blank">
<img src="https://img.shields.io/badge/typescript-%3E4.0.0-blue" alt="typescript">
</a>
<a href="https://vitejs.dev/" target="_blank">
<img src="https://img.shields.io/badge/vite-%3E5.4.2-yellow" alt="vite">
</a>
<a href="https://github.com/bufanyun/hotgo/blob/v2.0/LICENSE" target="_blank">
<img src="https://img.shields.io/badge/license-MIT-success" alt="license">
</a>
</p>
</div>
## 平台简介
* 基于 GoFrame2 + Vue3 + NaiveUI + uniapp 的企业级全栈框架,为二次开发而生。
* 前端采用 Naive-Ui-Admin、Vue、Naive UI、uniapp。
* 企业级 AI 赋能基础开发平台,内置 AI 开发规范,适配主流 AI 开发工具,实现人机协同开发,大幅提升开发效率。
## 演示地址
- [https://hotgo.facms.cn/admin](https://hotgo.facms.cn/admin)
> 账号:admin 密码:123456
### 使用文档
[安装文档](docs/guide-zh-CN/start-installation.md) · [本地文档](docs/guide-zh-CN/README.md) · [更新历史](docs/guide-zh-CN/start-update-log.md) · [常见问题](docs/guide-zh-CN/start-issue.md)
## 特征
* 高生产率:极强的可扩展性,应用化、模块化、插件化机制敏捷开发,几分钟即可搭建一个应用开发骨架。
* 多应用入口:多入口分为 Admin (后台)、Home (前台页面)、Api (对外通用接口)、WebSocket (即时通讯接口),不同的业务,进入不同的应用入口。
* 极致的插件化: 微核架构,功能隔离,高可定制性,可以渐进式开发,亦可以多人协同开发。支持一键创建插件模板、一键安装、更新、卸载插件、可以非常方便的将插件迁移到新项目中。
* 快速生成代码:无需编写代码,只需创建表进行简单配置就能生成一个完善的 CURD、树表等常用的开发代码,其中所需表单控件也是勾选即可直接生成。
* 认证机制:采用 JWT 的用户状态认证及 casbin 的权限认证
* 路由模式:得益于 GoFrame 提供了规范化的路由注册方式,无需注解自动生成api文档
* 模块化设计,面向接口开发
## 内置功能
1. 用户管理:用户是系统操作者,该功能主要完成系统用户配置。
2. 部门管理:配置系统组织机构(公司、部门、岗位),树结构展现支持数据权限。
3. 岗位管理:配置系统用户所属担任职务。
4. 菜单管理:配置系统菜单,操作权限,按钮权限标识等。
5. 角色管理:角色菜单权限分配、设置角色按机构或按上下级关系进行数据范围权限划分。
6. 字典管理:对系统中经常使用的一些特定数据进行维护,支持枚举字典和自定义方法字典。
7. 配置管理:对系统动态配置常用参数。
8. 操作日志:系统正常操作日志记录和查询;系统异常信息日志记录和查询。
9. 登录日志:系统登录日志记录查询包含登录异常。
10. 服务日志:服务端运行所产生的警告、异常、崩溃日志的详细数据和堆栈信息。
11. 支付网关:集成支付宝、微信支付、QQ支付等多种支付方式,只需简单配置即可使用。
12. 资金管理:支持在线充值、订单申请/原路退款、资金提现、资金/积分变动明细等通用模块。
13. 在线用户:当前系统中活跃用户状态监控。
14. 定时任务:在线(添加、修改、删除)任务调度包含执行结果日志。
15. 代码生成:支持自动化生成前后端代码。CURD关联表、树表、消息队列、定时任务一键生成等。
16. 插件应用:支持一键生成插件模板,每个插件之间开发隔离,拥有独立多应用入口、独立配置。完美支持多人协同开发、插件插拔不会对原系统产生影响等。
17. 服务监控:监视当前系统CPU、内存、磁盘、网络、堆栈等相关信息。
18. 附件管理:文件图片上传,大文件分片上传、断点续传,支持本地、阿里云oss、腾讯云cos、ucloud对象存储、七牛云对象存储、minio等多种上传驱动,后台一键切换配置,并集成了文件选择器。
19. TCP服务:基于gtcp的服务应用,支持长连接、断线重连、服务认证、路由分发、RPC消息、拦截器和数据绑定等。简化和规范了服务器开发流程。
20. 消息队列:同时兼容 kafka、redis、rocketmq、磁盘队列,一键配置切换到场景适用的MQ。
21. 通知公告:采用WebSocket实时推送在线用户最新通知、公告、私信消息。
22. 地区编码:整合国内通用省市区编码,运用于项目于一身,支持动态省市区选项。
23. 常用工具:集成常用的工具包和命令行工具,可以快速开发自定义命令行,多种启动入口。
> HotGo开源以来得到了大家的很多支持,本项目初衷只为互相学习交流,没有任何盈利性目的!欢迎为HotGo贡献代码或提供建议!
## 演示图
<table>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/1.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/2.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/3.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/4.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/5.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/6.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/7.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/8.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/9.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/10.png"/></td>
</tr>
<tr>
<td><img src="./docs/guide-zh-CN/images/demo/11.png"/></td>
<td><img src="./docs/guide-zh-CN/images/demo/12.png"/></td>
</tr>
</table>
## 感谢(排名不分先后)
> gf框架 [https://github.com/gogf/gf](https://github.com/gogf/gf)
>
> naive-ui [https://www.naiveui.com](https://www.naiveui.com)
>
> naive-ui-admin [https://github.com/jekip/naive-ui-admin](https://github.com/jekip/naive-ui-admin)
>
> websocket [https://github.com/gorilla/websocket](github.com/gorilla/websocket)
>
> casbin [https://github.com/casbin/casbin](https://github.com/casbin/casbin)
>
> gopay [https://github.com/go-pay/gopay](https://github.com/go-pay/gopay)
## 交流QQ群
交流群①:190966648 <a target="_blank" href="https://qm.qq.com/cgi-bin/qm/qr?k=mJafkvme3VNyiQlCFIFNRtY8Xlr7pj9U&jump_from=webapi&authKey=jL10vIESr+vO8wpxwyd6DlChzkrbHpzN9uhAsIHgAinL/Vvd+nvuRyilf2UqUlCy"><img border="0" src="https://gmycos.facms.cn/hotgo/group.png" alt="HotGo框架交流1群" title="HotGo框架交流1群"></a>
> <img src="https://gmycos.facms.cn/hotgo/hotgo1qun.png" width="400px"/>
## 商用说明
> HotGo 是开源免费的,遵循 MIT 开源协议,意味着您无需支付任何费用,也无需授权,即可将它应用到您的产品中。
* 使用本项目必须保留所有版权信息。
* 本项目包含的第三方源码和二进制文件之版权信息另行标注。
* 版权所有Copyright © 2020-2026 by Ms (https://github.com/bufanyun/hotgo)
* All rights reserved。
## 免责声明:
* HotGo为开源学习项目,一切商业行为与HotGo无关。
* 用户不得利用HotGo从事非法行为,用户应当合法合规的使用,发现用户在使用产品时有任何的非法行为,HotGo有权配合有关机关进行调查或向政府部门举报,HotGo不承担用户因非法行为造成的任何法律责任,一切法律责任由用户自行承担,如因用户使用造成第三方损害的,用户应当依法予以赔偿。
* 所有与使用HotGo相关的资源直接风险均由用户承担。
#### 如果对您有帮助,您可以点右上角 💘Star💘支持
## [感谢JetBrains提供的免费GoLand](https://jb.gg/OpenSource)
[![avatar](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://jb.gg/OpenSource)
## License
[MIT © HotGo-2026](./LICENSE)
@@ -0,0 +1,62 @@
{
"label": "数据概览",
"slug": "01-dashboard",
"route": "/dashboard",
"url": "https://admin.adfahi.cn/dashboard",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/01-dashboard.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n总用户数\n16\n活跃用户\n0\n总群组数\n6\n总会话数\n11\n总消息数\n220\n今日消息\n0\n用户增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0\n消息增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0",
"tables": [
{
"headers": [
"日期",
"数量"
],
"idx": 0,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
]
]
},
{
"headers": [
"日期",
"数量"
],
"idx": 1,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,78 @@
{
"label": "数据概览",
"slug": "01-dashboard",
"route": "/dashboard",
"url": "https://admin.adfahi.cn/dashboard",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/01-dashboard.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n总用户数\n16\n活跃用户\n0\n总群组数\n6\n总会话数\n11\n总消息数\n220\n今日消息\n0\n用户增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0\n消息增长趋势\n日期\t数量\n2026-08-28\t0\n2026-08-29\t0\n2026-08-30\t0\n2026-08-31\t0\n2026-09-01\t0\n2026-09-02\t0\n2026-09-03\t0",
"tables": [
{
"headers": [
"日期",
"数量"
],
"idx": 0,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
],
[
"2026-08-31",
"0"
],
[
"2026-09-01",
"0"
]
]
},
{
"headers": [
"日期",
"数量"
],
"idx": 1,
"rows": [
[
"2026-08-28",
"0"
],
[
"2026-08-29",
"0"
],
[
"2026-08-30",
"0"
],
[
"2026-08-31",
"0"
],
[
"2026-09-01",
"0"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,866 @@
{
"label": "用户管理",
"slug": "02-user-management",
"route": "/users",
"url": "https://admin.adfahi.cn/users",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/02-user-management.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户管理\n编号\n用户分组\n全部\n状态\n全部\n关键词\n查 询\n重 置\n创建用户\n批量创建\n导入Excel\n下载模板\n编号\t头像\t用户名\t昵称\t手机号\t注册方式\t状态\t用户分组\t最近登录\t创建时间\t操作\n\n编号\n\t\n头像\n\t\n用户名\n\t\n昵称\n\t\n手机号\n\t\n注册方式\n\t\n状态\n\t\n用户分组\n\t\n最近登录\n\t\n创建时间\n\t\n操作\n\n16\t\n点击查看\n\thy73074162\tpoter001\t\t后台创建\t正常\t默认用户组\t2026-08-01 00:05:30\t2026-08-01 00:04:25\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n15\t\n点击查看\n\thy17716319\t往事随风\t\t后台创建\t正常\t默认用户组\t2026-08-01 21:16:15\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n14\t\n点击查看\n\thy34899108\t坚守的山羊\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n13\t\n点击查看\n\thy45257043\t沉稳的松鼠\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n12\t\n点击查看\n\thy69598195\t专注的白兔\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n11\t\n点击查看\n\thy20144467\t沉稳的黑猫\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n10\t\n点击查看\n\thy70299474\t坚韧的河马\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n9\t\n点击查看\n\thy23799134\t求真的猫头\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n8\t\n点击查看\n\thy18959373\t进取的北狐\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n7\t\n点击查看\n\thy80424632\t温润的乌鸦\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n6\t\n点击查看\n\thy98400466\t乐观的野马\t\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:44:06\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n5\t\n点击查看\n\thy99111080\tF\t13800001115\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:43:53\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n4\t\n点击查看\n\thy92120233\tF\t13800001114\t后台创建\t正常\t默认用户组\t--\t2026-07-31 22:43:37\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n3\t\n点击查看\n\thy69089908\tF\t13800001113\t后台创建\t正常\t默认用户组\t2026-07-31 23:26:17\t2026-07-31 22:43:12\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n2\t\n点击查看\n\thy91144536\t冯仁飞\t13800001112\t后台创建\t正常\t默认用户组\t2026-08-02 22:27:59\t2026-07-31 22:43:02\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n\n1\t\n点击查看\n\thy50703413\t冯宇昂\t13800001111\t后台创建\t正常\t默认用户组\t2026-07-31 23:37:42\t2026-07-31 22:42:45\t\n详 情\n重置密码\nIP白名单\n调整分组\n封 禁\n强制下线\n强制注销\n1\n20 条/页",
"tables": [
{
"headers": [
"编号",
"头像",
"用户名",
"昵称",
"手机号",
"注册方式",
"状态",
"用户分组",
"最近登录",
"创建时间",
"操作",
"编号",
"头像",
"用户名",
"昵称",
"手机号",
"注册方式",
"状态",
"用户分组",
"最近登录",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"16",
"点击查看",
"hy73074162",
"poter001",
"后台创建",
"正常",
"默认用户组",
"2026-08-01 00:05:30",
"2026-08-01 00:04:25",
"详 情重置密码IP白名单调整分组封 禁强制下线强制注销"
],
[
"15",
"点击查看",
"hy17716319",
"往事随风",
"后台创建",
"正常",
"默认用户组",
"2026-08-01 21:16:15",
"2026-07-31 22:44:06",
"详 情重置密码IP白名单调整分组封 禁强制下线强制注销"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户编号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名/昵称/手机号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "创建用户"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量创建"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导入Excel"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载模板"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重置密码"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "调整分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制下线"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "强制注销"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,102 @@
{
"label": "用户分组",
"slug": "03-user-groups",
"route": "/user-policy-groups",
"url": "https://admin.adfahi.cn/user-policy-groups",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/03-user-groups.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户分组\n添加分组\nIP 白名单\n租户渠道 IP 白名单\nAPP:未配置\nWeb:未配置\n分组名称\t用户数\t允许加好友\t允许建群\t直接加好友\t好友上限\tAPP 登录\tWeb 登录\tIP 白名单\t状态\t权重\t操作\n\n分组名称\n\t\n用户数\n\t\n允许加好友\n\t\n允许建群\n\t\n直接加好友\n\t\n好友上限\n\t\nAPP 登录\n\t\nWeb 登录\n\t\nIP 白名单\n\t\n状态\n\t\n权重\n\t\n操作\n\n\n默认用户组\n默认\n\t16\t允许\t允许\t--\t不限\t允许\t允许\t未启用\t启用\t0\t\n编辑\n删除",
"tables": [
{
"headers": [
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作",
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作"
],
"idx": 0,
"rows": [
[],
[
"默认用户组默认",
"16",
"允许",
"允许",
"--",
"不限",
"允许",
"允许",
"未启用",
"启用",
"0",
"编辑删除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "添加分组"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP 白名单"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "编辑"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "删除"
}
],
"resources": []
}
@@ -0,0 +1,108 @@
{
"label": "用户分组",
"slug": "03-user-groups",
"route": "/user-policy-groups",
"url": "https://admin.adfahi.cn/user-policy-groups",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/03-user-groups.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户分组\n添加分组\nIP 白名单\n租户渠道 IP 白名单\nAPP:未配置\nWeb:未配置\n分组名称\t用户数\t允许加好友\t允许建群\t直接加好友\t好友上限\tAPP 登录\tWeb 登录\tIP 白名单\t状态\t权重\t操作\n\n分组名称\n\t\n用户数\n\t\n允许加好友\n\t\n允许建群\n\t\n直接加好友\n\t\n好友上限\n\t\nAPP 登录\n\t\nWeb 登录\n\t\nIP 白名单\n\t\n状态\n\t\n权重\n\t\n操作\n\n\n默认用户组\n默认\n\t16\t允许\t允许\t--\t不限\t允许\t允许\t未启用\t启用\t0\t\n编辑\n删除",
"tables": [
{
"headers": [
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作",
"分组名称",
"用户数",
"允许加好友",
"允许建群",
"直接加好友",
"好友上限",
"APP 登录",
"Web 登录",
"IP 白名单",
"状态",
"权重",
"操作"
],
"idx": 0,
"rows": [
[],
[
"默认用户组默认",
"16",
"允许",
"允许",
"--",
"不限",
"允许",
"允许",
"未启用",
"启用",
"0",
"编辑删除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "添加分组",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "IP 白名单",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "编辑",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "删除",
"value": ""
}
]
}
@@ -0,0 +1,296 @@
{
"label": "群组管理",
"slug": "04-group-management",
"route": "/groups",
"url": "https://admin.adfahi.cn/groups",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/04-group-management.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群组管理\n群号\n关键词\n状态\n全部\n查 询\n重 置\n群组编号\t群号\t群组名称\t成员数\t群主昵称\t群主用户名\t状态\t创建时间\t操作\n\n群组编号\n\t\n群号\n\t\n群组名称\n\t\n成员数\n\t\n群主昵称\n\t\n群主用户名\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n6\t68127154\t\n冯\n\t1\tF\thy69089908\t正常\t2026-07-31 22:50:52\t\n详 情\n群文件\n封 禁\n解 散\n\n5\t62262588\t\n冯\n\t3\tF\thy69089908\t正常\t2026-07-31 22:50:38\t\n详 情\n群文件\n封 禁\n解 散\n\n4\t75874312\t\n346亿916\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:57\t\n详 情\n群文件\n封 禁\n解 散\n\n3\t92851806\t\n64919\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:49\t\n详 情\n群文件\n封 禁\n解 散\n\n2\t89233971\t\n冯氏家族\n\t3\t冯仁飞\thy91144536\t正常\t2026-07-31 22:46:59\t\n详 情\n群文件\n封 禁\n解 散\n\n1\t17776111\t\n冯氏家族\n\t3\t冯宇昂\thy50703413\t正常\t2026-07-31 22:45:28\t\n详 情\n群文件\n封 禁\n解 散\n1\n20 条/页",
"tables": [
{
"headers": [
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作",
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"6",
"68127154",
"冯",
"1",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:52",
"详 情群文件封 禁解 散"
],
[
"5",
"62262588",
"冯",
"3",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:38",
"详 情群文件封 禁解 散"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "群名称",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,356 @@
{
"label": "群组管理",
"slug": "04-group-management",
"route": "/groups",
"url": "https://admin.adfahi.cn/groups",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/04-group-management.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群组管理\n群号\n关键词\n状态\n全部\n查 询\n重 置\n群组编号\t群号\t群组名称\t成员数\t群主昵称\t群主用户名\t状态\t创建时间\t操作\n\n群组编号\n\t\n群号\n\t\n群组名称\n\t\n成员数\n\t\n群主昵称\n\t\n群主用户名\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n6\t68127154\t\n冯\n\t1\tF\thy69089908\t正常\t2026-07-31 22:50:52\t\n详 情\n群文件\n封 禁\n解 散\n\n5\t62262588\t\n冯\n\t3\tF\thy69089908\t正常\t2026-07-31 22:50:38\t\n详 情\n群文件\n封 禁\n解 散\n\n4\t75874312\t\n346亿916\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:57\t\n详 情\n群文件\n封 禁\n解 散\n\n3\t92851806\t\n64919\n\t1\t冯宇昂\thy50703413\t正常\t2026-07-31 22:47:49\t\n详 情\n群文件\n封 禁\n解 散\n\n2\t89233971\t\n冯氏家族\n\t3\t冯仁飞\thy91144536\t正常\t2026-07-31 22:46:59\t\n详 情\n群文件\n封 禁\n解 散\n\n1\t17776111\t\n冯氏家族\n\t3\t冯宇昂\thy50703413\t正常\t2026-07-31 22:45:28\t\n详 情\n群文件\n封 禁\n解 散\n1\n20 条/页",
"tables": [
{
"headers": [
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作",
"群组编号",
"群号",
"群组名称",
"成员数",
"群主昵称",
"群主用户名",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"6",
"68127154",
"冯",
"1",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:52",
"详 情群文件封 禁解 散"
],
[
"5",
"62262588",
"冯",
"3",
"F",
"hy69089908",
"正常",
"2026-07-31 22:50:38",
"详 情群文件封 禁解 散"
],
[
"4",
"75874312",
"346亿916",
"1",
"冯宇昂",
"hy50703413",
"正常",
"2026-07-31 22:47:57",
"详 情群文件封 禁解 散"
],
[
"3",
"92851806",
"64919",
"1",
"冯宇昂",
"hy50703413",
"正常",
"2026-07-31 22:47:49",
"详 情群文件封 禁解 散"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "群名称",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详 情",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "群文件",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "封 禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解 散",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,113 @@
{
"label": "群成员管理",
"slug": "05-group-members",
"route": "/group-members",
"url": "https://admin.adfahi.cn/group-members",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/05-group-members.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群成员管理\n群号\n角色\n全部\n关键词\n查 询\n重 置\n刷 新\n编号\t用户名\t昵称\t群昵称\t角色\t禁言状态\t加入时间\t操作\n\n编号\n\t\n用户名\n\t\n昵称\n\t\n群昵称\n\t\n角色\n\t\n禁言状态\n\t\n加入时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作",
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "必填",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名/昵称",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
}
],
"resources": []
}
@@ -0,0 +1,173 @@
{
"label": "群成员管理",
"slug": "05-group-members",
"route": "/group-members",
"url": "https://admin.adfahi.cn/group-members",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/05-group-members.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群成员管理\n群号\n角色\n全部\n关键词\n查 询\n重 置\n刷 新\n编号\t用户名\t昵称\t群昵称\t角色\t禁言状态\t加入时间\t操作\n\n编号\n\t\n用户名\n\t\n昵称\n\t\n群昵称\n\t\n角色\n\t\n禁言状态\n\t\n加入时间\n\t\n操作\n\n3\thy69089908\tF\t--\t群主\t正常\t2026-07-31 22:50:52\t\n禁 言\n踢 出\n1\n20 条/页",
"tables": [
{
"headers": [
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作",
"编号",
"用户名",
"昵称",
"群昵称",
"角色",
"禁言状态",
"加入时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"3",
"hy69089908",
"F",
"--",
"群主",
"正常",
"2026-07-31 22:50:52",
"禁 言踢 出"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "必填",
"tag": "INPUT",
"text": "",
"value": "68127154"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名/昵称",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "禁 言",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "踢 出",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,103 @@
{
"label": "邀请码管理",
"slug": "06-invite-codes",
"route": "/invite-codes",
"url": "https://admin.adfahi.cn/invite-codes",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/06-invite-codes.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n邀请码管理\n状态\n全部\n邀请码\n查 询\n重 置\n生成邀请码\n每次生成 1 个邀请码,可多次使用。可指定企业与用户;勾选「不限制时间」则永久有效;开启「自动加好友」后,使用该码注册的用户将直接成为指定用户的好友。点击「查看」可查看使用该邀请码注册的用户列表。\n邀请码\t指定用户\t自动加好友\t使用人数\t创建人\t状态\t过期时间\t创建时间\t操作\n\n邀请码\n\t\n指定用户\n\t\n自动加好友\n\t\n使用人数\n\t\n创建人\n\t\n状态\n\t\n过期时间\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作",
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "6 位数字",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "生成邀请码"
}
],
"resources": []
}
@@ -0,0 +1,162 @@
{
"label": "邀请码管理",
"slug": "06-invite-codes",
"route": "/invite-codes",
"url": "https://admin.adfahi.cn/invite-codes",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/06-invite-codes.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n用户管理\n用户分组\n群组管理\n群成员管理\n邀请码管理\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n邀请码管理\n状态\n全部\n邀请码\n查 询\n重 置\n生成邀请码\n每次生成 1 个邀请码,可多次使用。可指定企业与用户;勾选「不限制时间」则永久有效;开启「自动加好友」后,使用该码注册的用户将直接成为指定用户的好友。点击「查看」可查看使用该邀请码注册的用户列表。\n邀请码\t指定用户\t自动加好友\t使用人数\t创建人\t状态\t过期时间\t创建时间\t操作\n\n邀请码\n\t\n指定用户\n\t\n自动加好友\n\t\n使用人数\n\t\n创建人\n\t\n状态\n\t\n过期时间\n\t\n创建时间\n\t\n操作\n\n323441\t往事随风 (#2271)\t否\t0\t16\t启用\t2026-09-05 00:34:55\t2026-09-04 00:34:55\t\n查 看\n删 除\n1\n20 条/页",
"tables": [
{
"headers": [
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作",
"邀请码",
"指定用户",
"自动加好友",
"使用人数",
"创建人",
"状态",
"过期时间",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"323441",
"往事随风 (#2271)",
"否",
"0",
"16",
"启用",
"2026-09-05 00:34:55",
"2026-09-04 00:34:55",
"查 看删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "6 位数字",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "生成邀请码",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 看",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,171 @@
{
"label": "单聊消息",
"slug": "07-direct-messages",
"route": "/private-messages",
"url": "https://admin.adfahi.cn/private-messages",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/07-direct-messages.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n单聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n接收人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收人\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n接收人\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
}
],
"resources": []
}
@@ -0,0 +1,393 @@
{
"label": "单聊消息",
"slug": "07-direct-messages",
"route": "/private-messages",
"url": "https://admin.adfahi.cn/private-messages",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/07-direct-messages.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n单聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n接收人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收人\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n接收人\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t系统\t\n语音通话 00:13\n\t正常\t2026-07-31 23:02:22\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t系统\t\n语音通话 00:01\n\t正常\t2026-07-31 23:01:45\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t系统\t\n语音通话 已取消\n\t正常\t2026-07-31 23:01:36\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t系统\t\n语音通话 已拒绝\n\t正常\t2026-07-31 23:01:19\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 已取消\n\t正常\t2026-07-31 23:00:20\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 00:19\n\t正常\t2026-07-31 22:59:59\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n语音通话 00:07\n\t正常\t2026-07-31 22:59:32\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t系统\t\n语音通话 已取消\n\t正常\t2026-07-31 22:59:05\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n语音通话 00:15\n\t正常\t2026-07-31 22:58:42\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 00:07\n\t正常\t2026-07-31 22:58:20\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t系统\t\n视频通话 已取消\n\t正常\t2026-07-31 22:58:07\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t文本\t\n红心\n\t正常\t2026-07-31 22:51:35\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t文本\t\n1\n\t正常\t2026-07-31 22:51:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t文本\t\n你想让我\n\t正常\t2026-07-31 22:51:32\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t文本\t\n1\n\t正常\t2026-07-31 22:51:32\t\n删 除\n1\n2\n3\n4\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"接收人",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"Fhy69089908",
"冯仁飞hy91144536",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯宇昂hy50703413",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯宇昂hy50703413",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:05:19",
"删 除"
],
[
"Fhy69089908",
"冯仁飞hy91144536",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:04:12",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,159 @@
{
"label": "群聊消息",
"slug": "08-group-messages",
"route": "/group-messages",
"url": "https://admin.adfahi.cn/group-messages",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/08-group-messages.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t群名称\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n群名称\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
}
],
"resources": []
}
@@ -0,0 +1,379 @@
{
"label": "群聊消息",
"slug": "08-group-messages",
"route": "/group-messages",
"url": "https://admin.adfahi.cn/group-messages",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/08-group-messages.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群聊消息\n全部\n文本\n图片\n视频\n语音\n文件\n名片\n系统\n发送人用户名\n关键词\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t群名称\t类型\t内容\t状态\t发送时间\t操作\n\n发送人\n\t\n群名称\n\t\n类型\n\t\n内容\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\nF\nhy69089908\n\t\n冯\n68127154\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n89233971\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:06:05\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n62262588\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n89233971\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n68127154\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:05:19\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n68127154\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n89233971\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:04:12\t\n删 除\n\n\nF\nhy69089908\n\t\n冯\n62262588\n\t文本\t\n哈哈哈哈\n\t正常\t2026-07-31 23:03:50\t\n删 除\n\n\nF\nhy69089908\n\t\n冯氏家族\n17776111\n\t文本\t\n😊😊😊😊\n\t正常\t2026-07-31 22:53:42\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nkddkkffjc\n\t正常\t2026-07-31 22:53:18\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nkdododdk\n\t正常\t2026-07-31 22:53:17\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkdkkddoso\n\t正常\t2026-07-31 22:53:16\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkkfkfkd\n\t正常\t2026-07-31 22:53:15\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkfkkdod\n\t正常\t2026-07-31 22:53:14\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t文本\t\n敏敏\n\t正常\t2026-07-31 22:53:13\t\n删 除\n\n\n冯仁飞\nhy91144536\n\t\n冯氏家族\n17776111\n\t文本\t\nfkfkfkdo\n\t正常\t2026-07-31 22:53:13\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t文本\t\n您提着\n\t正常\t2026-07-31 22:53:12\t\n删 除\n1\n2\n3\n4\n5\n•••\n8\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作",
"发送人",
"群名称",
"类型",
"内容",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"Fhy69089908",
"冯68127154",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯氏家族89233971",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯氏家族17776111",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:06:05",
"删 除"
],
[
"Fhy69089908",
"冯62262588",
"文本",
"哈哈哈哈",
"正常",
"2026-07-31 23:05:19",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "关键字",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,135 @@
{
"label": "敏感词库",
"slug": "09-sensitive-words",
"route": "/sensitive-words",
"url": "https://admin.adfahi.cn/sensitive-words",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/09-sensitive-words.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n敏感词库\n状态\n全部\n等级\n全部\n关键词\n查 询\n重 置\n新增敏感词\n批量导入\n文件导入\n批量导出\nID\t敏感词\t等级\t替换词\t状态\t创建时间\t操作\n\nID\n\t\n敏感词\n\t\n等级\n\t\n替换词\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作",
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "敏感词",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "新增敏感词"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导入"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "文件导入"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导出"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,150 @@
{
"label": "敏感词库",
"slug": "09-sensitive-words",
"route": "/sensitive-words",
"url": "https://admin.adfahi.cn/sensitive-words",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/09-sensitive-words.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n敏感词库\n状态\n全部\n等级\n全部\n关键词\n查 询\n重 置\n新增敏感词\n批量导入\n文件导入\n批量导出\nID\t敏感词\t等级\t替换词\t状态\t创建时间\t操作\n\nID\n\t\n敏感词\n\t\n等级\n\t\n替换词\n\t\n状态\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作",
"ID",
"敏感词",
"等级",
"替换词",
"状态",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "敏感词",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "新增敏感词",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导入",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "文件导入",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "批量导出",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
+288
View File
@@ -0,0 +1,288 @@
{
"label": "文件管理",
"slug": "10-files",
"route": "/file-records",
"url": "https://admin.adfahi.cn/file-records",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/10-files.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n文件管理\n全部\n图片\n视频\n音频\n文档\n其他\n发送人用户名\n文件名\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收方\t类型\t内容\t文件名\t文件大小\tURL\t状态\t发送时间\t操作\n\n发送人\n\t\n接收方\n\t\n类型\n\t\n内容\n\t\n文件名\n\t\n文件大小\n\t\nURL\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n64919\n92851806\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n346亿916\n75874312\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:46:46\t\n删 除\n1\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作",
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"冯宇昂hy50703413",
"Fhy69089908",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"冯氏家族17776111",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "搜索文件名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,348 @@
{
"label": "文件管理",
"slug": "10-files",
"route": "/file-records",
"url": "https://admin.adfahi.cn/file-records",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/10-files.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n文件管理\n全部\n图片\n视频\n音频\n文档\n其他\n发送人用户名\n文件名\n开始时间\n结束时间\n查 询\n重 置\n刷 新\n发送人\t接收方\t类型\t内容\t文件名\t文件大小\tURL\t状态\t发送时间\t操作\n\n发送人\n\t\n接收方\n\t\n类型\n\t\n内容\n\t\n文件名\n\t\n文件大小\n\t\nURL\n\t\n状态\n\t\n发送时间\n\t\n操作\n\n\n冯宇昂\nhy50703413\n\t\nF\nhy69089908\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯氏家族\n17776111\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n64919\n92851806\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n346亿916\n75874312\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:48:33\t\n删 除\n\n\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t图片\t\n预览\n下载\n\t--\t--\thttps://gdd-chat-fls.oss-a...\n\t正常\t2026-07-31 22:46:46\t\n删 除\n1\n20 条/页",
"tables": [
{
"headers": [
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作",
"发送人",
"接收方",
"类型",
"内容",
"文件名",
"文件大小",
"URL",
"状态",
"发送时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"冯宇昂hy50703413",
"Fhy69089908",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"冯氏家族17776111",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"6491992851806",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
],
[
"冯宇昂hy50703413",
"346亿91675874312",
"图片",
"预览下载",
"--",
"--",
"https://gdd-chat-fls.oss-a...",
"正常",
"2026-07-31 22:48:33",
"删 除"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "搜索文件名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "开始时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "结束时间",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "刷 新",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "下载",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "删 除",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
+141
View File
@@ -0,0 +1,141 @@
{
"label": "用户反馈",
"slug": "11-feedback",
"route": "/feedbacks",
"url": "https://admin.adfahi.cn/feedbacks",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/11-feedback.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户反馈\n用户ID\n状态\n全部\n优先级\n全部\n关键词\n查 询\n重 置\n批量处理\n导出 CSV\n\t用户\t优先级\t内容\t状态\t处理人\t创建时间\t操作\n\n\t\n用户\n\t\n优先级\n\t\n内容\n\t\n状态\n\t\n处理人\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作",
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户ID",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "内容/联系方式",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "批量处理"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV"
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,157 @@
{
"label": "用户反馈",
"slug": "11-feedback",
"route": "/feedbacks",
"url": "https://admin.adfahi.cn/feedbacks",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/11-feedback.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n单聊消息\n群聊消息\n敏感词库\n文件管理\n用户反馈\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户反馈\n用户ID\n状态\n全部\n优先级\n全部\n关键词\n查 询\n重 置\n批量处理\n导出 CSV\n\t用户\t优先级\t内容\t状态\t处理人\t创建时间\t操作\n\n\t\n用户\n\t\n优先级\n\t\n内容\n\t\n状态\n\t\n处理人\n\t\n创建时间\n\t\n操作\n\n\n暂无数据",
"tables": [
{
"headers": [
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作",
"用户",
"优先级",
"内容",
"状态",
"处理人",
"创建时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"暂无数据暂无数据"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户ID",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "内容/联系方式",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查 询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "批量处理",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
},
{
"disabled": true,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "on"
}
]
}
@@ -0,0 +1,180 @@
{
"label": "操作日志",
"slug": "12-operation-logs",
"route": "/audit-logs",
"url": "https://admin.adfahi.cn/audit-logs",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/12-operation-logs.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n操作日志\n动作\n全部\n目标类型\n全部\n目标用户\n目标群\n开始时间\n结束时间\n查询\n重 置\n导出 CSV\n管理员\t动作\t目标类型\t目标\t查询条件\tIP\t时间\n\n管理员\n\t\n动作\n\t\n目标类型\n\t\n目标\n\t\n查询条件\n\t\nIP\n\t\n时间\n\nheiyue\t创建用户\t用户\t\npoter001\nhy73074162\n\t\n--\n\t104.30.173.163\t2026-08-01 00:04:25\nheiyue\t重置密码\t用户\t\n往事随风\nhy17716319\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:23\nheiyue\t重置密码\t用户\t\n坚守的山羊\nhy34899108\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:19\nheiyue\t重置密码\t用户\t\n沉稳的松鼠\nhy45257043\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:16\nheiyue\t重置密码\t用户\t\n专注的白兔\nhy69598195\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:12\nheiyue\t重置密码\t用户\t\n沉稳的黑猫\nhy20144467\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:09\nheiyue\t重置密码\t用户\t\n坚韧的河马\nhy70299474\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:05\nheiyue\t重置密码\t用户\t\n求真的猫头\nhy23799134\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:00\nheiyue\t重置密码\t用户\t\n进取的北狐\nhy18959373\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:55\nheiyue\t重置密码\t用户\t\n温润的乌鸦\nhy80424632\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:51\nheiyue\t重置密码\t用户\t\n乐观的野马\nhy98400466\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:48\nheiyue\t批量创建用户\t用户\t--\t\n详情\n\t141.164.107.33\t2026-07-31 22:44:06\nheiyue\t创建用户\t用户\t\nF\nhy99111080\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:53\nheiyue\t创建用户\t用户\t\nF\nhy92120233\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:37\nheiyue\t创建用户\t用户\t\nF\nhy69089908\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:12\nheiyue\t创建用户\t用户\t\n冯仁飞\nhy91144536\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:02\nheiyue\t创建用户\t用户\t\n冯宇昂\nhy50703413\n\t\n--\n\t141.164.107.33\t2026-07-31 22:42:45\n1\n20 条/页",
"tables": [
{
"headers": [
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间",
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"创建用户",
"用户",
"poter001hy73074162",
"--",
"104.30.173.163",
"2026-08-01 00:04:25"
],
[
"heiyue",
"重置密码",
"用户",
"往事随风hy17716319",
"--",
"141.164.107.33",
"2026-07-31 22:45:23"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详情"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,218 @@
{
"label": "操作日志",
"slug": "12-operation-logs",
"route": "/audit-logs",
"url": "https://admin.adfahi.cn/audit-logs",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/12-operation-logs.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n操作日志\n动作\n全部\n目标类型\n全部\n目标用户\n目标群\n开始时间\n结束时间\n查询\n重 置\n导出 CSV\n管理员\t动作\t目标类型\t目标\t查询条件\tIP\t时间\n\n管理员\n\t\n动作\n\t\n目标类型\n\t\n目标\n\t\n查询条件\n\t\nIP\n\t\n时间\n\nheiyue\t创建用户\t用户\t\npoter001\nhy73074162\n\t\n--\n\t104.30.173.163\t2026-08-01 00:04:25\nheiyue\t重置密码\t用户\t\n往事随风\nhy17716319\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:23\nheiyue\t重置密码\t用户\t\n坚守的山羊\nhy34899108\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:19\nheiyue\t重置密码\t用户\t\n沉稳的松鼠\nhy45257043\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:16\nheiyue\t重置密码\t用户\t\n专注的白兔\nhy69598195\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:12\nheiyue\t重置密码\t用户\t\n沉稳的黑猫\nhy20144467\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:09\nheiyue\t重置密码\t用户\t\n坚韧的河马\nhy70299474\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:05\nheiyue\t重置密码\t用户\t\n求真的猫头\nhy23799134\n\t\n--\n\t141.164.107.33\t2026-07-31 22:45:00\nheiyue\t重置密码\t用户\t\n进取的北狐\nhy18959373\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:55\nheiyue\t重置密码\t用户\t\n温润的乌鸦\nhy80424632\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:51\nheiyue\t重置密码\t用户\t\n乐观的野马\nhy98400466\n\t\n--\n\t141.164.107.33\t2026-07-31 22:44:48\nheiyue\t批量创建用户\t用户\t--\t\n详情\n\t141.164.107.33\t2026-07-31 22:44:06\nheiyue\t创建用户\t用户\t\nF\nhy99111080\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:53\nheiyue\t创建用户\t用户\t\nF\nhy92120233\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:37\nheiyue\t创建用户\t用户\t\nF\nhy69089908\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:12\nheiyue\t创建用户\t用户\t\n冯仁飞\nhy91144536\n\t\n--\n\t141.164.107.33\t2026-07-31 22:43:02\nheiyue\t创建用户\t用户\t\n冯宇昂\nhy50703413\n\t\n--\n\t141.164.107.33\t2026-07-31 22:42:45\n1\n20 条/页",
"tables": [
{
"headers": [
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间",
"管理员",
"动作",
"目标类型",
"目标",
"查询条件",
"IP",
"时间"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"创建用户",
"用户",
"poter001hy73074162",
"--",
"104.30.173.163",
"2026-08-01 00:04:25"
],
[
"heiyue",
"重置密码",
"用户",
"往事随风hy17716319",
"--",
"141.164.107.33",
"2026-07-31 22:45:23"
],
[
"heiyue",
"重置密码",
"用户",
"坚守的山羊hy34899108",
"--",
"141.164.107.33",
"2026-07-31 22:45:19"
],
[
"heiyue",
"重置密码",
"用户",
"沉稳的松鼠hy45257043",
"--",
"141.164.107.33",
"2026-07-31 22:45:16"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "群号",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "导出 CSV",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "详情",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,172 @@
{
"label": "群事件审计",
"slug": "14-group-event-audit",
"route": "/group-events",
"url": "https://admin.adfahi.cn/group-events",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/14-group-event-audit.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群事件审计\n群号\n操作人\n目标用户\n事件类型\n全部\n开始时间\n结束时间\n查询\n重 置\n群名称\t事件类型\t操作人\t目标用户\t可见范围\t旧值\t新值\t时间\n\n群名称\n\t\n事件类型\n\t\n操作人\n\t\n目标用户\n\t\n可见范围\n\t\n旧值\n\t\n新值\n\t\n时间\n\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n68127154\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:52\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯\n62262588\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:38\n\n冯氏家族\n17776111\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:34\n\n346亿916\n75874312\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:57\n\n64919\n92851806\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:50\n\n冯氏家族\n17776111\n\t成员加入\t\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 22:47:19\n\n冯氏家族\n89233971\n\t群创建\t\n冯仁飞\nhy91144536\n\t--\t全员可见\t--\t--\t2026-07-31 22:46:59\n\n冯氏家族\n17776111\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:45:28\n1\n20 条/页",
"tables": [
{
"headers": [
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间",
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间"
],
"idx": 0,
"rows": [
[],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯宇昂hy50703413",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯仁飞hy91144536",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,210 @@
{
"label": "群事件审计",
"slug": "14-group-event-audit",
"route": "/group-events",
"url": "https://admin.adfahi.cn/group-events",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/14-group-event-audit.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n群事件审计\n群号\n操作人\n目标用户\n事件类型\n全部\n开始时间\n结束时间\n查询\n重 置\n群名称\t事件类型\t操作人\t目标用户\t可见范围\t旧值\t新值\t时间\n\n群名称\n\t\n事件类型\n\t\n操作人\n\t\n目标用户\n\t\n可见范围\n\t\n旧值\n\t\n新值\n\t\n时间\n\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n62262588\n\t成员加入\t\nF\nhy69089908\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 23:03:42\n\n冯\n68127154\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:52\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\n冯宇昂\nhy50703413\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯氏家族\n89233971\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:43\n\n冯\n62262588\n\t群创建\t\nF\nhy69089908\n\t--\t全员可见\t--\t--\t2026-07-31 22:50:38\n\n冯氏家族\n17776111\n\t成员加入\t\n冯仁飞\nhy91144536\n\t\nF\nhy69089908\n\t全员可见\t--\t--\t2026-07-31 22:50:34\n\n346亿916\n75874312\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:57\n\n64919\n92851806\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:47:50\n\n冯氏家族\n17776111\n\t成员加入\t\n冯宇昂\nhy50703413\n\t\n冯仁飞\nhy91144536\n\t全员可见\t--\t--\t2026-07-31 22:47:19\n\n冯氏家族\n89233971\n\t群创建\t\n冯仁飞\nhy91144536\n\t--\t全员可见\t--\t--\t2026-07-31 22:46:59\n\n冯氏家族\n17776111\n\t群创建\t\n冯宇昂\nhy50703413\n\t--\t全员可见\t--\t--\t2026-07-31 22:45:28\n1\n20 条/页",
"tables": [
{
"headers": [
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间",
"群名称",
"事件类型",
"操作人",
"目标用户",
"可见范围",
"旧值",
"新值",
"时间"
],
"idx": 0,
"rows": [
[],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯宇昂hy50703413",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
],
[
"冯62262588",
"成员加入",
"Fhy69089908",
"冯仁飞hy91144536",
"全员可见",
"--",
"--",
"2026-07-31 23:03:42"
],
[
"冯68127154",
"群创建",
"Fhy69089908",
"--",
"全员可见",
"--",
"--",
"2026-07-31 22:50:52"
],
[
"冯氏家族89233971",
"成员加入",
"冯仁飞hy91144536",
"冯宇昂hy50703413",
"全员可见",
"--",
"--",
"2026-07-31 22:50:43"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "用户名",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,102 @@
{
"label": "管理员登录",
"slug": "15-admin-login",
"route": "/admin-login-history",
"url": "https://admin.adfahi.cn/admin-login-history",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/15-admin-login.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n管理员登录历史\n开始时间\n结束时间\n查询\n用户名\t登录时间\tUser-Agent\tIP 地址\n\n用户名\n\t\n登录时间\n\t\nUser-Agent\n\t\nIP 地址\n\nheiyue\t2026-09-04 00:10:57\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36\t103.136.251.99\nheiyue\t2026-08-01 00:03:52\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 22:47:31\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t141.164.107.235\nheiyue\t2026-07-31 22:42:09\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t141.164.107.33\nheiyue\t2026-07-31 21:32:28\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 00:45:59\tMozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1\t203.160.80.152\n1\n20 条/页",
"tables": [
{
"headers": [
"用户名",
"登录时间",
"User-Agent",
"IP 地址",
"用户名",
"登录时间",
"User-Agent",
"IP 地址"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"2026-09-04 00:10:57",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
"103.136.251.99"
],
[
"heiyue",
"2026-08-01 00:03:52",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
"104.30.173.163"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,123 @@
{
"label": "管理员登录",
"slug": "15-admin-login",
"route": "/admin-login-history",
"url": "https://admin.adfahi.cn/admin-login-history",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/15-admin-login.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n管理员登录历史\n开始时间\n结束时间\n查询\n用户名\t登录时间\tUser-Agent\tIP 地址\n\n用户名\n\t\n登录时间\n\t\nUser-Agent\n\t\nIP 地址\n\nheiyue\t2026-09-04 00:10:57\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36\t103.136.251.99\nheiyue\t2026-08-01 00:03:52\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 22:47:31\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t141.164.107.235\nheiyue\t2026-07-31 22:42:09\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t141.164.107.33\nheiyue\t2026-07-31 21:32:28\tMozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36\t104.30.173.163\nheiyue\t2026-07-31 00:45:59\tMozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.2 Mobile/15E148 Safari/604.1\t203.160.80.152\n1\n20 条/页",
"tables": [
{
"headers": [
"用户名",
"登录时间",
"User-Agent",
"IP 地址",
"用户名",
"登录时间",
"User-Agent",
"IP 地址"
],
"idx": 0,
"rows": [
[],
[
"heiyue",
"2026-09-04 00:10:57",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36",
"103.136.251.99"
],
[
"heiyue",
"2026-08-01 00:03:52",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
"104.30.173.163"
],
[
"heiyue",
"2026-07-31 22:47:31",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36",
"141.164.107.235"
],
[
"heiyue",
"2026-07-31 22:42:09",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0",
"141.164.107.33"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,202 @@
{
"label": "用户登录",
"slug": "16-user-login",
"route": "/user-login-history",
"url": "https://admin.adfahi.cn/user-login-history",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/16-user-login.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户登录历史\n用户编号\n用户名\n开始时间\n结束时间\nIP\n查询\n重 置\n用户编号\t用户名\t当前状态\t登录时间\t设备类型\t设备型号\t系统版本\tApp 版本\tIP 地址\t归属地\t操作\n\n用户编号\n\t\n用户名\n\t\n当前状态\n\t\n登录时间\n\t\n设备类型\n\t\n设备型号\n\t\n系统版本\n\t\nApp 版本\n\t\nIP 地址\n\t\n归属地\n\t\n操作\n\n16\thy73074162\t正常\t2026-08-01 00:04:47\tweb\tChrome Browser\tMac OS X 10.15.7\t-\t104.30.173.163\t美国 加利福尼亚州 罗斯维尔\t\n快速封禁\n\n15\thy17716319\t正常\t2026-07-31 22:50:19\tandroid\tOPPO PCHM10\tAndroid 11\t1.1.2\t141.164.107.33\t老挝 万象市 永珍\t\n快速封禁\n\n3\thy69089908\t正常\t2026-07-31 22:47:10\tandroid\tOPPO PKW110\tAndroid 16\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n2\thy91144536\t正常\t2026-07-31 22:45:28\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:45:10\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:44:44\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:43:38\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n1\n20 条/页",
"tables": [
{
"headers": [
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作",
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作"
],
"idx": 0,
"rows": [
[],
[
"16",
"hy73074162",
"正常",
"2026-08-01 00:04:47",
"web",
"Chrome Browser",
"Mac OS X 10.15.7",
"-",
"104.30.173.163",
"美国 加利福尼亚州 罗斯维尔",
"快速封禁"
],
[
"15",
"hy17716319",
"正常",
"2026-07-31 22:50:19",
"android",
"OPPO PCHM10",
"Android 11",
"1.1.2",
"141.164.107.33",
"老挝 万象市 永珍",
"快速封禁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "登录IP",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,249 @@
{
"label": "用户登录",
"slug": "16-user-login",
"route": "/user-login-history",
"url": "https://admin.adfahi.cn/user-login-history",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/16-user-login.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n用户登录历史\n用户编号\n用户名\n开始时间\n结束时间\nIP\n查询\n重 置\n用户编号\t用户名\t当前状态\t登录时间\t设备类型\t设备型号\t系统版本\tApp 版本\tIP 地址\t归属地\t操作\n\n用户编号\n\t\n用户名\n\t\n当前状态\n\t\n登录时间\n\t\n设备类型\n\t\n设备型号\n\t\n系统版本\n\t\nApp 版本\n\t\nIP 地址\n\t\n归属地\n\t\n操作\n\n16\thy73074162\t正常\t2026-08-01 00:04:47\tweb\tChrome Browser\tMac OS X 10.15.7\t-\t104.30.173.163\t美国 加利福尼亚州 罗斯维尔\t\n快速封禁\n\n15\thy17716319\t正常\t2026-07-31 22:50:19\tandroid\tOPPO PCHM10\tAndroid 11\t1.1.2\t141.164.107.33\t老挝 万象市 永珍\t\n快速封禁\n\n3\thy69089908\t正常\t2026-07-31 22:47:10\tandroid\tOPPO PKW110\tAndroid 16\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n2\thy91144536\t正常\t2026-07-31 22:45:28\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:45:10\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:44:44\tandroid\tHONOR LOG-AN00\tAndroid 15\t1.1.2\t61.153.184.95\t浙江 绍兴\t\n快速封禁\n\n1\thy50703413\t正常\t2026-07-31 22:43:38\tandroid\tvivo V1809A\tAndroid 9\t1.1.2\t124.160.160.95\t浙江 杭州\t\n快速封禁\n1\n20 条/页",
"tables": [
{
"headers": [
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作",
"用户编号",
"用户名",
"当前状态",
"登录时间",
"设备类型",
"设备型号",
"系统版本",
"App 版本",
"IP 地址",
"归属地",
"操作"
],
"idx": 0,
"rows": [
[],
[
"16",
"hy73074162",
"正常",
"2026-08-01 00:04:47",
"web",
"Chrome Browser",
"Mac OS X 10.15.7",
"-",
"104.30.173.163",
"美国 加利福尼亚州 罗斯维尔",
"快速封禁"
],
[
"15",
"hy17716319",
"正常",
"2026-07-31 22:50:19",
"android",
"OPPO PCHM10",
"Android 11",
"1.1.2",
"141.164.107.33",
"老挝 万象市 永珍",
"快速封禁"
],
[
"3",
"hy69089908",
"正常",
"2026-07-31 22:47:10",
"android",
"OPPO PKW110",
"Android 16",
"1.1.2",
"61.153.184.95",
"浙江 绍兴",
"快速封禁"
],
[
"2",
"hy91144536",
"正常",
"2026-07-31 22:45:28",
"android",
"HONOR LOG-AN00",
"Android 15",
"1.1.2",
"61.153.184.95",
"浙江 绍兴",
"快速封禁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "登录IP",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "快速封禁",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,123 @@
{
"label": "IP 账号统计",
"slug": "17-ip-account-stats",
"route": "/ip-user-stats",
"url": "https://admin.adfahi.cn/ip-user-stats",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/17-ip-account-stats.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\nIP 账号统计\nIP\n查询\n重 置\nIP 地址\t归属地\t关联用户\t注册用户\t登录用户\t正常\t已封禁\t最近活动\t操作\n\nIP 地址\n\t\n归属地\n\t\n关联用户\n\t\n注册用户\n\t\n登录用户\n\t\n正常\n\t\n已封禁\n\t\n最近活动\n\t\n操作\n\n61.153.184.95\t浙江 绍兴\t\n3\n多账号\n\t0\t3\t3\t0\t2026-08-02 22:27:59\t\n用户列表\n共 1 个 IP\n1\n20 条/页",
"tables": [
{
"headers": [
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作",
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作"
],
"idx": 0,
"rows": [
[],
[
"61.153.184.95",
"浙江 绍兴",
"3多账号",
"0",
"3",
"3",
"0",
"2026-08-02 22:27:59",
"用户列表"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "IPv4 或 IPv6",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "用户列表"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,134 @@
{
"label": "IP 账号统计",
"slug": "17-ip-account-stats",
"route": "/ip-user-stats",
"url": "https://admin.adfahi.cn/ip-user-stats",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/17-ip-account-stats.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\nIP 账号统计\nIP\n查询\n重 置\nIP 地址\t归属地\t关联用户\t注册用户\t登录用户\t正常\t已封禁\t最近活动\t操作\n\nIP 地址\n\t\n归属地\n\t\n关联用户\n\t\n注册用户\n\t\n登录用户\n\t\n正常\n\t\n已封禁\n\t\n最近活动\n\t\n操作\n\n61.153.184.95\t浙江 绍兴\t\n3\n多账号\n\t0\t3\t3\t0\t2026-08-02 22:27:59\t\n用户列表\n共 1 个 IP\n1\n20 条/页",
"tables": [
{
"headers": [
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作",
"IP 地址",
"归属地",
"关联用户",
"注册用户",
"登录用户",
"正常",
"已封禁",
"最近活动",
"操作"
],
"idx": 0,
"rows": [
[],
[
"61.153.184.95",
"浙江 绍兴",
"3多账号",
"0",
"3",
"3",
"0",
"2026-08-02 22:27:59",
"用户列表"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "IPv4 或 IPv6",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "用户列表",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,156 @@
{
"label": "登录失败日志",
"slug": "18-login-failures",
"route": "/login-failure-logs",
"url": "https://admin.adfahi.cn/login-failure-logs",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/18-login-failures.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n登录失败日志\n类型\n全部\n登录账号\nIP\n开始时间\n结束时间\n查询\n重 置\n类型\t登录账号\t失败原因\tIP 地址\tUser-Agent\t时间\t操作\n\n类型\n\t\n登录账号\n\t\n失败原因\n\t\nIP 地址\n\t\nUser-Agent\n\t\n时间\n\t\n操作\n\n用户\t13800001111\t密码错误\t106.119.66.106\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t2026-08-01 01:31:20\t\n解锁\n\n用户\t13800001111\t密码错误\t61.153.184.95\tokhttp/4.12.0\t2026-07-31 22:44:36\t\n解锁\n1\n20 条/页",
"tables": [
{
"headers": [
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作",
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"用户",
"13800001111",
"密码错误",
"106.119.66.106",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0",
"2026-08-01 01:31:20",
"解锁"
],
[
"用户",
"13800001111",
"密码错误",
"61.153.184.95",
"okhttp/4.12.0",
"2026-07-31 22:44:36",
"解锁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁"
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
}
],
"resources": []
}
@@ -0,0 +1,172 @@
{
"label": "登录失败日志",
"slug": "18-login-failures",
"route": "/login-failure-logs",
"url": "https://admin.adfahi.cn/login-failure-logs",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/18-login-failures.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n操作日志\n用户账号审计\n群事件审计\n管理员登录\n用户登录\nIP 账号统计\n登录失败日志\n系统设置\nheiyue\nheiyue\n账户操作\n数据概览\n登录失败日志\n类型\n全部\n登录账号\nIP\n开始时间\n结束时间\n查询\n重 置\n类型\t登录账号\t失败原因\tIP 地址\tUser-Agent\t时间\t操作\n\n类型\n\t\n登录账号\n\t\n失败原因\n\t\nIP 地址\n\t\nUser-Agent\n\t\n时间\n\t\n操作\n\n用户\t13800001111\t密码错误\t106.119.66.106\tMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0\t2026-08-01 01:31:20\t\n解锁\n\n用户\t13800001111\t密码错误\t61.153.184.95\tokhttp/4.12.0\t2026-07-31 22:44:36\t\n解锁\n1\n20 条/页",
"tables": [
{
"headers": [
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作",
"类型",
"登录账号",
"失败原因",
"IP 地址",
"User-Agent",
"时间",
"操作"
],
"idx": 0,
"rows": [
[],
[
"用户",
"13800001111",
"密码错误",
"106.119.66.106",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/150.0.0.0 Safari/537.36 Edg/150.0.0.0",
"2026-08-01 01:31:20",
"解锁"
],
[
"用户",
"13800001111",
"密码错误",
"61.153.184.95",
"okhttp/4.12.0",
"2026-07-31 22:44:36",
"解锁"
]
]
}
],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "全部",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "可选",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "查询",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "重 置",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "解锁",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": true,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "DIV",
"text": "20 条/页",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
}
]
}
@@ -0,0 +1,67 @@
{
"label": "私域设置",
"slug": "19-private-domain-settings",
"route": "/tenant-settings",
"url": "https://admin.adfahi.cn/tenant-settings",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/19-private-domain-settings.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n私域设置\n私域设置 - 黑月测试\n消息字符上限\n范围 1~5000,仅允许调低\n图片大小上限(MB)\n范围 1~1000 MB\n文件大小上限(MB)\n范围 1~1000 MB,默认 50 MB\n最大撤回消息时间(分钟)\n群主/管理员撤回自己或他人消息的最长时限,范围 1~1440 分钟;普通用户撤回自己的消息固定为 2 分钟\n是否开启手机验证码\n开启\n关闭\n关闭后,注册仅支持邀请码,短信登录与忘记密码不可用\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "开启关闭"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置"
}
],
"resources": []
}
@@ -0,0 +1,75 @@
{
"label": "私域设置",
"slug": "19-private-domain-settings",
"route": "/tenant-settings",
"url": "https://admin.adfahi.cn/tenant-settings",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/19-private-domain-settings.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n私域设置\n私域设置 - 黑月测试\n消息字符上限\n范围 1~5000,仅允许调低\n图片大小上限(MB)\n范围 1~1000 MB\n文件大小上限(MB)\n范围 1~1000 MB,默认 50 MB\n最大撤回消息时间(分钟)\n群主/管理员撤回自己或他人消息的最长时限,范围 1~1440 分钟;普通用户撤回自己的消息固定为 2 分钟\n是否开启手机验证码\n开启\n关闭\n关闭后,注册仅支持邀请码,短信登录与忘记密码不可用\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "5000"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "10"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "50"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "2"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "开启关闭",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置",
"value": ""
}
]
}
@@ -0,0 +1,157 @@
{
"label": "安全策略",
"slug": "20-security-policy",
"route": "/security-settings",
"url": "https://admin.adfahi.cn/security-settings",
"title": "鸽达达管理后台",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots/20-security-policy.png",
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n安全策略\n用户安全\n验证码触发次数\n连续密码错误该次数后要求验证码(1~10)\n密码最小位数\n用户密码最少字符数(6~32)\n强制包含字母\n开启后密码必须包含英文字母\n注册登录管控\n用户注册功能\n关闭后用户端将隐藏注册入口\n修改密码功能\n关闭后用户无法修改密码,不影响后台重置\n用户登录功能\n关闭后用户无法登录,不影响已登录用户\nWeb 端多端登录\n开启后同一账号可在多个浏览器标签页同时登录\n聊天消息管控\n全局好友功能\n关闭后无法搜索和添加好友\n全局群消息\n关闭后所有群无法发送消息\n全局私聊消息\n关闭后所有私聊无法发送消息\n群聊发言间隔(秒)\n0 表示不限制;1~1000 表示同一群内两次发言的最小间隔\n群组功能管控\n群号搜索功能\n关闭后用户端隐藏群号和群搜索入口\n群创建功能\n关闭后任何人无法新建群聊\n群解散功能\n关闭后任何人无法解散群聊,不影响后台管理员\n群管理员功能\n关闭后任何人无法设置群管理员\n是否显示群人数\n关闭后用户端查看群资料时不显示群总人数\n其他功能开关\n允许用户注销账号\n关闭后用户端将隐藏注销账号按钮\nIP 访问控制\n注册 IP 黑名单\n留空表示不限制;每行一个 IPv4、IPv6 或 CIDR。命中后无法注册及获取注册短信验证码。\n登录 IP 黑名单\n留空表示不限制;命中后无法密码登录、短信登录及刷新 Token。与用户分组登录白名单独立,黑名单优先。\n高级功能\n消息已读状态\n开启后显示消息已读/未读标记\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "203.0.113.10\n10.0.0.0/8",
"tag": "TEXTAREA",
"text": ""
},
{
"disabled": null,
"placeholder": "198.51.100.0/24",
"tag": "TEXTAREA",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置"
}
],
"resources": []
}
@@ -0,0 +1,180 @@
{
"label": "安全策略",
"slug": "20-security-policy",
"route": "/security-settings",
"url": "https://admin.adfahi.cn/security-settings",
"screenshot": "/Users/youshunlai/go_workspace/im_manager/docs/admin-adfahi-research/screenshots-queried/20-security-policy.png",
"queried": true,
"text": "鸽达达管理后台\n数据概览\n用户与群组\n内容审核\n日志审计\n系统设置\n私域设置\n安全策略\nheiyue\nheiyue\n账户操作\n数据概览\n安全策略\n用户安全\n验证码触发次数\n连续密码错误该次数后要求验证码(1~10)\n密码最小位数\n用户密码最少字符数(6~32)\n强制包含字母\n开启后密码必须包含英文字母\n注册登录管控\n用户注册功能\n关闭后用户端将隐藏注册入口\n修改密码功能\n关闭后用户无法修改密码,不影响后台重置\n用户登录功能\n关闭后用户无法登录,不影响已登录用户\nWeb 端多端登录\n开启后同一账号可在多个浏览器标签页同时登录\n聊天消息管控\n全局好友功能\n关闭后无法搜索和添加好友\n全局群消息\n关闭后所有群无法发送消息\n全局私聊消息\n关闭后所有私聊无法发送消息\n群聊发言间隔(秒)\n0 表示不限制;1~1000 表示同一群内两次发言的最小间隔\n群组功能管控\n群号搜索功能\n关闭后用户端隐藏群号和群搜索入口\n群创建功能\n关闭后任何人无法新建群聊\n群解散功能\n关闭后任何人无法解散群聊,不影响后台管理员\n群管理员功能\n关闭后任何人无法设置群管理员\n是否显示群人数\n关闭后用户端查看群资料时不显示群总人数\n其他功能开关\n允许用户注销账号\n关闭后用户端将隐藏注销账号按钮\nIP 访问控制\n注册 IP 黑名单\n留空表示不限制;每行一个 IPv4、IPv6 或 CIDR。命中后无法注册及获取注册短信验证码。\n登录 IP 黑名单\n留空表示不限制;命中后无法密码登录、短信登录及刷新 Token。与用户分组登录白名单独立,黑名单优先。\n高级功能\n消息已读状态\n开启后显示消息已读/未读标记\n保存设置",
"tables": [],
"controls": [
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "3"
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "INPUT",
"text": "",
"value": "0"
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "203.0.113.10\n10.0.0.0/8",
"tag": "TEXTAREA",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "198.51.100.0/24",
"tag": "TEXTAREA",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "",
"value": ""
},
{
"disabled": null,
"placeholder": "",
"tag": "BUTTON",
"text": "保存设置",
"value": ""
}
]
}
@@ -0,0 +1,409 @@
# Admin Server 改造文档:对齐鸽达达管理后台
本文基于 `docs/admin-adfahi-research/需求文档.md`、当前 `im_manager` 代码、`im_server` 文档与本地 MySQL 数据核对结果编写。目标是在 HotGo Admin Server 中复刻鸽达达后台的 20 个页面,并保持当前系统“Admin 只直连 hotgo 控制库,IM Server 只直连租户业务库”的边界。
> 实施状态(2026-09-07):Admin 第二批已完成 `imDashboard`、`imPrivateMessage`、`imGroupMessage`、`imUserLoginHistory`、`imLoginFailureLog` 的 API、service、logic、controller、路由、Vue 页面、字典、导出和菜单权限。消息隐藏/删除、失败日志封禁用户使用 Kafka Admin Command;失败 IP 加黑复用安全策略。对应菜单 SQL 为 `server/storage/data/generate/im_adfahi_batch2_menu.sql`,已在本地 `hotgo` 执行。上述查询仍依赖 IM Server 本文约定的 internal-query;本地 `tinode` 尚无 `im_user_login_logs`、`im_login_failure_logs`,需在 IM Server 批次建表并接入日志采集后才会产生真实数据。
## 1. 改造边界
Admin Server 负责后台菜单、权限、页面、查询聚合、运营配置、Kafka 管理命令投递、命令结果展示与后台审计落库。业务 IM 数据不直接跨库读写,用户、群、消息、文件、反馈、登录等租户数据通过 IM Server 内部只读接口 `/internal/admin-query/tenants/{tenant_code}/...` 查询;用户封禁、群治理、消息处置、文件处置等写操作通过 Kafka Admin Command 下发。
当前本地库观察:
| 数据库 | 现状 |
| --- | --- |
| `hotgo` | 当前有 66 张表,已存在 `im_admin_commands`、`im_admin_operation_audit`、`im_admin_outbox`、`im_registration_invite_codes`、`im_external_config`、`im_file_policy`,并已新增策略分组、敏感词、安全策略和宵禁控制表。 |
| `tinode` | 当前有 73 张表,已有 `im_users`、`im_topics`、`im_subscriptions`、`im_messages`、`im_group_messages`、`im_group_settings`、`im_fileuploads`、`im_feedback_submissions` 等 IM 业务表。 |
| 联调状态 | `hotgo.im_tenant` 已补默认租户映射(`id=1`、`code=default`);`tinode` 尚无用户登录历史和登录失败日志表,对应页面待 IM Server 批次补齐数据源。 |
敏感配置说明:`hg_sys_config` 中已有声网、阿里云、OSS、短信等配置项。文档与页面只能展示脱敏值,后续应补充加密存储、轮换、审计与权限控制。
## 2. 当前 Admin 能力盘点
已有后台 API 模块:
| 模块 | 主要能力 | 复用结论 |
| --- | --- | --- |
| `imUser` | 用户列表、详情、设备、重置密码、状态、封禁/解封、冻结/解冻、踢设备、全端登出、注销、删除 | 可作为“用户管理”的主体,补鸽达达字段、筛选项、批量操作与审计页联动。 |
| `imGroup` | 群列表、详情、成员、群治理、资料更新、解散、禁言、成员禁言/移除、入群申请、邀请、公告、文件、管理日志 | 可覆盖“群组管理”和“群成员管理”主体,需补独立菜单页与鸽达达状态/字段映射。 |
| `imInvite` | 邀请码列表、新增、批量新增、批量查看、撤销、批量撤销、导出 | 可覆盖“邀请码管理”,但邀请码格式与鸽达达需求存在差异。 |
| `imFile` | 文件列表、详情、隔离、删除、清理孤儿文件 | 可覆盖“文件管理”,需补预览/下载链接、审核枚举、内容风险字段。 |
| `imAuditLog` | 操作日志列表、详情、导出 | 可覆盖后台操作日志的一部分,需扩展为用户账号审计、群事件审计等专题页。 |
| `imKafkaCommand`、`imDeadLetter`、`imOutbox`、`imKafkaAudit` | Kafka 命令、死信、出站事件、审计事件 | 保留为运维治理菜单,不直接出现在鸽达达 20 个业务页中。 |
| `imRegistration`、`imExternalConfig`、`imFilePolicy`、`imFeedbackConfig`、`imMailboxConfig`、`imNotification`、`imAppRelease` | 注册、第三方、文件策略、反馈、邮箱、通知、版本配置 | 可组合成“私域设置”和“安全策略”,需要按鸽达达页面重组展示。 |
已有菜单已包含 `IM管理`、`注册邀请码`、`用户列表`、`群组列表`、`文件治理`、`Kafka命令`、`Kafka死信`、`Kafka审计事件`、`租户管理` 等。缺少与鸽达达完全一致的“单聊消息、群聊消息、敏感词库、用户反馈、用户登录、IP账号统计、登录失败日志”等前台页面。
## 3. 页面级改造
### 3.1 数据概览
新增或改造 `imDashboard` 页面。顶部统计按需求文档展示总用户数、活跃用户、总群组数、总会话数、总消息数、今日消息;趋势表展示最近 7 天用户增长、消息增长。现有 IM Server 内部接口已有 `/internal/admin-query/tenants/{tenant}/dashboard/summary`,但仅返回 `dead_kafka`、`pending_business`、`pending_group`、`pending_realname`、`received_abuse`、`received_feedback`、`active_groups`、`suspended_users`,不足以还原鸽达达数据概览。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| API | 新增 `/imDashboard/overview`、`/imDashboard/trends`。 |
| 数据源 | 控制库租户信息来自 `im_tenant`;统计与趋势来自 IM internal query。 |
| 字段 | `totalUsers`、`activeUsers`、`totalGroups`、`totalConversations`、`totalMessages`、`todayMessages`、`date`、`userCount`、`messageCount`。 |
| 按钮 | 页面无写操作;支持租户切换、日期范围查询、刷新。 |
| 缺口 | IM Server 需补 dashboard 聚合项与趋势接口;Admin 需补租户为空时的默认租户兜底提示。 |
### 3.2 用户管理
复用 `imUser`,页面按鸽达达字段重排。列表数据通过 IM internal `users` 查询,详情通过 `/users/{id}`,设备通过 `/users/{id}/devices`。当前 Admin 已有 `/imUser/list`、`/imUser/view`、`/imUser/devices`、`/imUser/ban`、`/imUser/unban`、`/imUser/freeze`、`/imUser/unfreeze`、`/imUser/deviceKick`、`/imUser/logoutAll`、`/imUser/accountCancel`、`/imUser/accountDelete`、`/imUser/resetPassword`。
字段说明:
| 字段 | 来源 | 说明 |
| --- | --- | --- |
| `user_id` | `tinode.im_users.id` 编码后 | 用户唯一 ID,列表、详情、审计联动主键。 |
| `username` | `im_users.username` | 登录账号。 |
| `phone` | `im_users.phone` | 手机号,页面默认脱敏,导出需权限。 |
| `nickname` | `im_user_profiles.nickname` | 昵称,支持模糊查询。 |
| `public_account` | `im_user_profiles.public_account` | 对外账号/靓号。 |
| `state` | `im_users.state` | Tinode 用户状态。建议映射为正常、封禁、冻结、注销、删除。 |
| `last_seen` | `im_users.last_seen` | 最后在线/活跃时间。 |
| `created_at` | `im_users.created_at` | 注册时间。 |
| `credentials` | IM internal 聚合 | 认证方式摘要,不展示密钥。 |
按钮功能:
| 按钮 | 已有接口 | 功能 |
| --- | --- | --- |
| 查询 | `/imUser/list` | 按用户 ID、昵称、账号、手机号、状态、注册时间、最后活跃时间筛选。 |
| 重置 | 前端表单重置 | 清空筛选项并重新查询。 |
| 查看 | `/imUser/view` | 展示基础信息、资料、设备、最近审计事件。 |
| 封禁/解封 | `/imUser/ban`、`/imUser/unban` | Kafka 命令 `user.ban` / `user.unban`,需填写原因和过期时间。 |
| 冻结/解冻 | `/imUser/freeze`、`/imUser/unfreeze` | Kafka 命令 `user.freeze` / `user.unfreeze`。 |
| 踢设备 | `/imUser/deviceKick` | 指定设备下线。 |
| 全端登出 | `/imUser/logoutAll` | 下发 `user.logout_all`。 |
| 注销/删除 | `/imUser/accountCancel`、`/imUser/accountDelete` | 高危操作,二次确认并写审计。 |
枚举说明:`state` 当前来自 Tinode 状态值,Admin 应新增统一字典 `im_user_state`,至少包含正常、封禁、冻结、注销、删除。封禁/冻结原因不应写死在前端,可复用“安全策略”中的处置原因配置。
### 3.3 用户分组
当前缺少等价模块,需要新增 `imUserPolicyGroup`。该页面不是组织部门,而是账号策略分组,用于控制注册、登录、私聊、建群、入群、发文件、发消息、IP 限制等策略。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| 后端模块 | `api/admin/imuserpolicygroup`、`controller/admin/sys/im_user_policy_group.go`、`logic/sys/im_user_policy_group.go`、`model/input/sysin/im_user_policy_group.go`。 |
| 控制表 | 新增 `im_user_policy_groups`、`im_user_policy_group_members`。 |
| API | `/imUserPolicyGroup/list`、`/view`、`/edit`、`/delete`、`/status`、`/memberList`、`/memberAdd`、`/memberRemove`。 |
| 同步 | 策略变更写 `im_admin_outbox`,发布 Kafka config event;IM Server 缓存策略并执行。 |
| 页面按钮 | 查询、新增、编辑、删除、启用/禁用、成员管理、导出。 |
字段说明:
| 字段 | 说明 |
| --- | --- |
| `group_code` | 分组编码,唯一,不可编辑。 |
| `group_name` | 分组名称。 |
| `priority` | 命中多个分组时的优先级,数字越大优先级越高。 |
| `status` | 启用、禁用。 |
| `policy_json` | 策略 JSON,包含消息、好友、群、文件、登录等开关与限额。 |
| `ip_whitelist`、`ip_blacklist` | IP 白名单/黑名单,支持 CIDR。 |
| `remark` | 备注。 |
好友策略必须覆盖移动端所有加好友入口,不能只依赖前端隐藏。分组策略中至少包含 `allow_add_friend`、`direct_add_friend`、`max_friend_count`、`allow_view_profile_when_friend_disabled`、`allow_qrcode_add_friend`、`allow_group_member_profile`。当全局或分组禁止加好友时,从搜索、通讯录推荐、群成员列表、个人名片、二维码解析、邀请注册自动加好友、转发目标联系人等入口都必须由后端返回禁止或脱敏数据。
枚举说明:`status` 使用 `sys_normal_disable`;策略开关使用布尔值;策略动作建议统一枚举 `allow`、`deny`、`challenge`、`audit_only`。好友入口枚举建议 `search`、`profile_card`、`group_member`、`qrcode`、`invite_auto_add`、`recommendation`。
### 3.4 群组管理
复用 `imGroup`。列表通过 IM internal `groups` 查询,详情通过 `/groups/{topic}`,现有 Admin 已有群资料、治理、成员、申请、邀请、公告、文件、日志等接口。
字段说明:
| 字段 | 来源 | 说明 |
| --- | --- | --- |
| `topic` | `im_topics.name` | 群 Topic,等价群号/群唯一标识。 |
| `group_name` | `im_group_settings.group_name` | 群名称。 |
| `owner_id` | `im_topics.owner` | 群主。 |
| `group_status` | `im_group_settings.group_status` | 群业务状态。 |
| `state` | `im_topics.state` | Tinode topic 状态。 |
| `business_member_count` | `im_group_settings.business_member_count` | 成员数。 |
| `join_approval` | `im_group_settings.join_approval` | 入群审批开关。 |
| `mute_all` | `im_group_settings.mute_all` | 全员禁言。 |
| `created_at`、`updated_at` | topic/settings | 创建与更新时间。 |
按钮功能:
| 按钮 | 已有接口 | 功能 |
| --- | --- | --- |
| 查询 | `/imGroup/list` | 按群号、群名、群主、状态、创建时间查询。 |
| 查看 | `/imGroup/view` | 群资料、成员、公告、邀请、文件、治理配置汇总。 |
| 编辑资料 | `/imGroup/profileUpdate` | Kafka `group.profile.update`。 |
| 治理设置 | `/imGroup/governanceUpdate` | Kafka `group.governance.update`,控制入群审批、全员禁言、成员上传文件、成员私聊、成员邀请。 |
| 解散 | `/imGroup/dissolve` | 高危操作,二次确认。 |
| 禁言/解禁言 | `/imGroup/mute`、`/imGroup/unmute` | 群级禁言。 |
| 成员/申请/邀请/公告/文件/日志 | 已有子接口 | 打开子面板或跳转专题页。 |
枚举说明:新增 `im_group_status`,建议值 `active` 正常、`muted` 禁言、`suspended` 封停、`dissolved` 已解散。当前 IM 命令未看到明确 `group.ban`/`group.unban`,如鸽达达要求群封停,需要新增 Kafka 命令或通过治理状态统一处理。
### 3.5 群成员管理
复用 `/imGroup/memberList` 与成员治理命令,新增独立菜单页 `imGroupMember`,默认要求输入群号或从群详情跳转携带 `topic`。
字段说明:
| 字段 | 说明 |
| --- | --- |
| `topic` | 群 Topic。 |
| `user_id` | 成员用户 ID。 |
| `display_name`/`nickname` | 群内展示名或用户昵称。 |
| `role` | 群主、管理员、普通成员。 |
| `status` | 正常、禁言、已移除。 |
| `muted_until` | 禁言截止时间。 |
| `joined_at` | 入群时间。 |
按钮功能:查询、查看用户、设为管理员、取消管理员、转让群主、成员禁言、解除禁言、移除成员。现有接口分别为 `/imGroup/adminSet`、`/adminRemove`、`/ownerTransfer`、`/memberMute`、`/memberUnmute`、`/memberRemove`。
枚举说明:`role` 新增字典 `im_group_member_role`:`owner` 群主、`admin` 管理员、`member` 成员;成员状态新增 `im_group_member_status`。
### 3.6 邀请码管理
复用 `imInvite`。本地 `hotgo.im_registration_invite_codes` 有样例数据,批次表为空。当前需求文档按鸽达达页面描述 6 位数字邀请码;历史 IM 文档曾描述 5 位 ASCII 字母邀请码,二者冲突。若以复刻鸽达达为目标,应统一改为 6 位数字,Admin 生成、IM 注册校验、数据库唯一索引与错误文案同步调整。
字段说明:`code` 邀请码、`batch_id` 批次、`status` 状态、`max_uses` 最大使用次数、`used_count` 已使用次数、`expires_at` 过期时间、`created_by` 创建管理员、`created_at` 创建时间、`revoked_at` 撤销时间。
按钮功能:查询、新增、批量生成、批量查看、撤销、批量撤销、导出。接口已有 `/imInvite/list`、`/create`、`/batchCreate`、`/batchView`、`/revoke`、`/batchRevoke`、`/export`。
枚举说明:新增或确认 `im_invite_status`:`unused` 未使用、`used` 已用完、`expired` 已过期、`revoked` 已撤销。
### 3.7 单聊消息
当前 Admin 缺少独立消息管理模块,需要新增 `imPrivateMessage`。IM Server 已有消息表 `im_messages`,但 internal admin query 目前只支持 `/messages/{topic}/{seq}` 单条查询,缺少按会话分页检索单聊消息的后台列表接口。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| API | `/imPrivateMessage/list`、`/view`、`/hide`、`/delete`、`/export`。 |
| 数据源 | 新增 IM internal query:`/tenants/{tenant}/private-messages` 或 `/messages?kind=p2p`。 |
| 写操作 | 通过 Kafka 命令新增 `message.hide`、`message.delete` 或复用 IM 已有 recall/delete 能力封装为后台命令。 |
| 页面按钮 | 查询、查看上下文、隐藏、删除、导出。 |
字段说明:`topic` 会话 Topic、`seq_id` 消息序号、`from_user_id` 发送者、`to_user_id` 接收者、`content_type` 内容类型、`content` 内容摘要、`status` 状态、`created_at` 发送时间、`updated_at` 更新时间。
枚举说明:`content_type` 建议 `text`、`image`、`file`、`audio`、`video`、`card`、`system`;`status` 建议 `normal`、`recalled`、`hidden`、`deleted`。
### 3.8 群聊消息
新增 `imGroupMessage` 页面。IM Server 本地有 `im_group_messages` 表,需求需要按群号、发送者、时间、内容关键字筛选。
Admin 改造:新增 `/imGroupMessage/list`、`/view`、`/hide`、`/delete`、`/export`。IM Server 补 internal query `group-messages`,支持 `topic`、`sender_user_id`、`content_type`、`status`、`created_at_start/end`、`keyword`。
字段、按钮、枚举与单聊消息一致,额外展示 `group_name`、`member_role`、`at_users`、`reply_to_seq`。
### 3.9 敏感词库
当前 Admin 与 IM Server 未发现完整敏感词库模块,需要新增。Admin 维护词库,IM Server 在消息发送、昵称/群名修改、反馈/举报提交时执行匹配。
Admin 改造:
| 项 | 方案 |
| --- | --- |
| 表 | `im_sensitive_words`、`im_sensitive_word_groups`、`im_sensitive_word_hits` 可按需拆分。 |
| API | `/imSensitiveWord/list`、`/view`、`/edit`、`/delete`、`/status`、`/import`、`/export`。 |
| 同步 | 词库变更发布 config event 或 cache invalidate,IM Server 热加载。 |
| 按钮 | 查询、新增、编辑、删除、启用/禁用、导入、导出。 |
字段说明:`word` 敏感词、`match_type` 匹配类型、`scene` 生效场景、`action` 命中动作、`replacement` 替换文本、`level` 风险等级、`status` 状态。
枚举说明:`match_type` 为 `exact` 精确、`contains` 包含、`regex` 正则;`scene` 为 `message` 消息、`profile` 资料、`group` 群资料、`feedback` 反馈;`action` 为 `block` 拦截、`replace` 替换、`review` 人审、`audit_only` 仅记录。
### 3.10 文件管理
复用 `imFile`,补齐鸽达达文件字段和预览能力。IM internal 已支持 `files`、`files/{id}`、`files/{id}/links`。本地 `tinode.im_fileuploads` 有数据,`im_group_files` 暂无数据。
字段说明:`file_id` 文件 ID、`user_id` 上传者、`topic` 所属会话、`mime_type` MIME、`size` 文件大小、`location` 存储位置、`etag` 校验、`status` 状态、`review_status` 审核状态、`risk_level` 风险等级、`created_at` 上传时间。
按钮功能:查询、查看、预览/下载、隔离、删除、清理孤儿文件。现有接口 `/imFile/quarantine`、`/imFile/delete`、`/imFile/cleanupOrphan` 可复用;预览/下载建议由 Admin 获取短期签名 URL,不直接暴露 OSS 原始地址。
枚举说明:现有字典包括 `im_storage_provider=oss`、`im_file_review_provider`、`im_file_review_status(pending/running/clean/risky/failed)`、`im_file_review_task_type(content/malware)`;还需补 `im_file_status`:`normal` 正常、`quarantined` 已隔离、`deleted` 已删除。
### 3.11 用户反馈
IM Server 已有 `im_feedback_submissions` 和 internal resource `feedback-submissions`,Admin 目前有反馈配置和工单模块,但缺少完全按鸽达达展示的反馈列表页。建议新增 `imFeedback` 或改造 `imWorkOrder` 作为反馈处理页。
字段说明:`feedback_id` 反馈 ID、`user_id` 用户、`category` 分类、`content` 内容、`contact` 联系方式、`file_ids` 附件、`client_context` 客户端环境、`include_diagnostics` 是否含诊断、`status` 状态、`created_at`、`updated_at`。
按钮功能:查询、查看、标记处理中、标记已处理、关闭、导出。写操作建议先落 Admin 工单状态,再通过 Kafka 或 IM internal 写接口回写业务状态。
枚举说明:`status` 建议 `received` 已收到、`processing` 处理中、`resolved` 已解决、`closed` 已关闭;`category` 来自 `im_feedback_config.categories`,禁止前端硬编码。
### 3.12 操作日志
复用 `imAuditLog` 与 `im_admin_operation_audit`。本地该表已有 45 条记录。页面应覆盖后台管理员对 IM 资源的所有操作,包括用户、群、消息、文件、配置、邀请码、敏感词。
字段说明:`id`、`request_id`、`admin_id`、`admin_name`、`action`、`target_type`、`target_id`、`before_data`、`after_data`、`ip`、`user_agent`、`result`、`created_at`。
按钮功能:查询、查看详情、导出。详情页需展示变更前后 JSON,敏感字段脱敏。
枚举说明:`target_type` 建议 `user`、`group`、`message`、`file`、`invite`、`config`、`sensitive_word`;`result` 为 `success`、`failed`、`pending`。
### 3.13 用户账号审计
新增专题页 `imUserAccountAudit`,从 Admin 操作审计、IM `audit-logs`、Kafka 命令结果中聚合用户生命周期事件。
字段说明:`event_id`、`user_id`、`action`、`operator_type`、`operator_id`、`reason`、`ip`、`device_id`、`before_state`、`after_state`、`created_at`。
按钮功能:查询、查看、跳转用户详情、导出。动作枚举建议包含注册、登录、登出、登录失败、改密、重置密码、封禁、解封、冻结、解冻、注销、删除、踢设备。
缺口:IM Server 当前 `im_audit_logs` 本地为空,需要补登录、失败、状态变更等事件写入。
### 3.14 群事件审计
可复用 `imGroup/adminLogList`、`outbox/group-events` 和 Admin audit,新增独立页面 `imGroupEventAudit`。
字段说明:`event_id`、`topic`、`group_name`、`operator_id`、`target_user_id`、`action`、`object_type`、`object_id`、`before_data`、`after_data`、`created_at`。
按钮功能:查询、查看、跳转群详情、导出。动作枚举建议包含建群、解散、修改资料、修改治理设置、入群申请、审批通过、审批拒绝、成员禁言、解除禁言、移除成员、设管理员、取消管理员、转让群主、公告发布/删除、文件删除。
### 3.15 管理员登录历史
复用 HotGo 系统登录日志能力,不需要 IM Server 改造。若现有登录日志字段不足,补 Admin 聚合接口 `/imAdminLoginHistory/list` 或直接配置菜单指向系统日志页面。
字段说明:管理员 ID、账号、昵称、登录 IP、地区、浏览器、系统、登录结果、失败原因、登录时间。
按钮功能:查询、查看、导出。枚举 `result` 为成功、失败。
### 3.16 用户登录历史
当前缺少完整用户登录历史。IM Server 路由中未看到客户端 `/v1/im/auth/logout` 或独立 session 管理路由;本地 `im_devices` 为空,不能满足鸽达达登录历史。
Admin 改造:新增 `imUserLoginHistory` 页面,查询 IM internal `user-login-logs`。字段包括 `user_id`、`phone`、`login_type`、`device_id`、`platform`、`ip`、`region`、`user_agent`、`result`、`login_at`、`logout_at`。
枚举说明:`login_type` 为 `password`、`sms`、`token_refresh`、`third_party`;`platform` 为 `ios`、`android`、`web`、`desktop`;`result` 为 `success`、`failed`。
### 3.17 IP 账号统计
新增 `imIpAccountStats` 页面。数据由 IM Server 基于注册 IP、登录 IP、失败登录 IP 聚合生成,Admin 只查询展示。
字段说明:`ip`、`region`、`user_count`、`register_count`、`login_count`、`failure_count`、`first_seen_at`、`last_seen_at`、`risk_level`。
按钮功能:查询、查看关联账号、导出、加入黑名单。加入黑名单写入“安全策略”或“用户分组”策略并同步 IM Server。
枚举说明:`risk_level` 为 `low`、`medium`、`high`;黑名单动作 `block_login`、`challenge`、`audit_only`。
### 3.18 登录失败日志
新增 `imLoginFailureLog` 页面。IM Server 需要记录认证失败事件,包含错误原因、IP、设备、账号、风控命中信息。
字段说明:`id`、`account`、`user_id`、`failure_type`、`reason`、`ip`、`region`、`device_id`、`platform`、`user_agent`、`attempt_at`。
按钮功能:查询、查看、导出、加入 IP 黑名单、封禁账号。封禁账号复用 `/imUser/ban`。
枚举说明:`failure_type` 为 `password_error`、`code_error`、`account_not_found`、`account_disabled`、`rate_limited`、`risk_blocked`。
### 3.19 私域设置
以现有 `imRegistration`、`imExternalConfig`、`imFeedbackConfig`、`imMailboxConfig`、`imNotification`、`imAppRelease` 为基础,新增汇总页面 `imTenantSettings`,按鸽达达页面展示私域基础配置。
字段说明:租户标识、注册开关、邀请码开关、短信开关、默认策略分组、客服/反馈配置、上传配置、客户端功能开关、版本策略、更新时间。客户端功能开关中新增 `global_friend_enabled` 和 `message_curfew_enabled` 的只读摘要,便于运营在私域设置页看到当前私域是否允许加好友、是否存在宵禁;实际编辑入口放在“安全策略”。
按钮功能:查看、编辑、保存、测试第三方配置、发布配置。保存后写 `hg_sys_config` 或 `im_tenant_config`,并通过 Kafka config event 通知 IM Server 刷新缓存。
枚举说明:开关统一使用布尔;配置发布状态 `draft`、`published`、`failed`。
### 3.20 安全策略
新增或重组 `imSecurityPolicy` 页面,统一管理登录安全、密码策略、IP 风控、敏感词动作、文件审核、用户处置原因、消息处置策略、全局好友功能与消息宵禁。
字段说明:`password_policy`、`login_rate_limit`、`ip_blacklist`、`ip_whitelist`、`sensitive_word_policy`、`file_review_policy`、`realname_policy`、`risk_challenge_policy`、`global_friend_enabled`、`friend_disabled_policy`、`message_curfew_enabled`、`message_curfew_rules`。
全局好友功能说明:
| 字段 | 说明 |
| --- | --- |
| `global_friend_enabled` | 全局是否允许加好友。关闭后所有移动端加好友入口都禁止。 |
| `friend_disabled_policy.hide_profile` | 禁止加好友时,群成员列表、个人名片、二维码解析是否隐藏个人资料。 |
| `friend_disabled_policy.block_qrcode` | 是否禁止通过二维码添加好友。 |
| `friend_disabled_policy.block_group_member_entry` | 是否禁止从群成员入口查看个人信息或发起添加。 |
| `friend_disabled_policy.blocked_entries` | 被禁用的移动端入口数组:`search`、`profile_card`、`group_member`、`qrcode`、`invite_auto_add`、`recommendation`。关闭全局好友功能时默认全部禁用。 |
| `friend_disabled_policy.message` | 移动端提示文案,例如“当前时段暂不允许添加好友”。 |
宵禁功能说明:
| 字段 | 说明 |
| --- | --- |
| `message_curfew_enabled` | 是否启用消息宵禁。 |
| `message_curfew_rules` | 宵禁规则数组,支持每天、工作日、周末、自定义日期。 |
| `start_time`、`end_time` | 宵禁开始和结束时间,支持跨天,例如 `18:00` 到次日 `09:00`。 |
| `scope` | 管控范围,默认至少包含 `private_message`,即单聊/私聊不能发消息;如产品确认,可扩展 `group_message`。 |
| `whitelist_user_ids`、`whitelist_group_ids` | 豁免用户或群,可选。 |
| `message` | 宵禁期间发送失败提示。 |
按钮功能:查询当前策略、编辑、保存、发布、恢复默认、查看发布历史。保存必须写操作审计。
枚举说明:风控动作统一 `allow`、`challenge`、`block`、`audit_only`;文件审核动作 `allow`、`quarantine`、`delete`、`manual_review`;宵禁范围 `private_message` 单聊/私聊、`group_message` 群聊;日期类型 `daily` 每天、`weekday` 工作日、`weekend` 周末、`custom` 自定义。
## 4. 后端实现方案
按 HotGo 规范新增模块时,必须完成 API、controller、service、logic、model/input、router/genrouter、前端 API、views/model、index、edit。接口命名沿用当前项目风格,例如 `/imPrivateMessage/list`,不直接照搬鸽达达 `/admin/private-messages` 路径;如后续需要兼容外部路径,可单独加网关映射。
新增模块建议:
| 优先级 | 模块 | 说明 |
| --- | --- | --- |
| P0 | `imDashboard` | 首页统计与趋势。 |
| P0 | `imPrivateMessage`、`imGroupMessage` | 单聊/群聊消息查询与处置。 |
| P0 | `imSensitiveWord` | 敏感词库与热更新。 |
| P0 | `imUserLoginHistory`、`imLoginFailureLog` | 用户登录与失败日志。 |
| P1 | `imUserPolicyGroup` | 用户策略分组。 |
| P1 | `imFeedback` | 用户反馈处理页。 |
| P1 | `imIpAccountStats` | IP 聚合与风控联动。 |
| P1 | `imUserAccountAudit`、`imGroupEventAudit` | 专题审计页。 |
| P2 | `imTenantSettings`、`imSecurityPolicy` | 运营配置整合页。 |
Admin 查询 IM 数据统一走 `server/internal/library/iminternal` 或现有 `imadmin` 封装,不在 logic 层直接拼接外部 HTTP 细节。写操作统一创建 `im_admin_commands` 与 `im_admin_outbox`,等待 Kafka result 更新状态;页面展示 `pending/success/failed/dead`。
## 5. 数据库与字典
配套建表与兼容迁移脚本见 [`sql/Admin-Server改造.sql`](sql/Admin-Server改造.sql)。脚本面向 `hotgo` 控制库,只保存管理配置和发布版本,不保存租户业务消息。
控制库新增表优先放在 `hotgo`:
| 表 | 用途 |
| --- | --- |
| `im_user_policy_groups` | 用户策略分组主表。 |
| `im_user_policy_group_members` | 用户与策略分组关系。 |
| `im_sensitive_word_groups` | 敏感词分组。 |
| `im_sensitive_words` | 敏感词主表。 |
| `im_security_policy` | 安全策略草稿/发布版本。 |
| `im_admin_query_snapshots` | 可选,用于缓存重型统计查询快照。 |
字典由后端 `internal/consts` 注册,前端通过 `dict.loadOptions()` 加载。禁止在 `model.ts` 中硬编码枚举中文。
必补字典:`im_user_state`、`im_group_status`、`im_group_member_role`、`im_group_member_status`、`im_invite_status`、`im_message_content_type`、`im_message_status`、`im_sensitive_match_type`、`im_sensitive_scene`、`im_sensitive_action`、`im_feedback_status`、`im_login_type`、`im_login_failure_type`、`im_risk_level`、`im_policy_action`、`im_friend_entry`、`im_curfew_scope`、`im_curfew_date_type`。
## 6. 权限与审计
每个按钮权限与后端路由保持一致,例如 `/imPrivateMessage/delete`、`/imSensitiveWord/import`。高危操作包括用户删除、账号注销、群解散、消息删除、文件删除、IP 拉黑、安全策略发布,必须二次确认、记录原因、写 `im_admin_operation_audit`,并展示 Kafka 命令执行结果。
审计脱敏规则:
| 类型 | 展示规则 |
| --- | --- |
| 手机号 | 中间 4 位脱敏。 |
| 身份证/实名 | 默认只展示脱敏字段。 |
| 配置密钥 | 只展示前后 3-4 位或完全隐藏。 |
| 消息内容 | 列表展示摘要,详情按权限展示全文。 |
| 文件地址 | 不展示原始存储地址,使用短期签名 URL。 |
## 7. 联调与验收
验收顺序建议:
1. 补齐 `hotgo.im_tenant` 与 `tinode` 默认租户映射,确保 Admin 能通过 tenant code 查询 IM internal。
2. 完成数据概览、用户、群、邀请码、文件这些已有基础模块的字段对齐。
3. 新增消息、敏感词、反馈、登录日志、IP 统计等缺口模块。
4. 为 Kafka 命令增加幂等键、结果回写、失败重试、死信展示。
5. 使用本地 `hotgo` 与 `tinode` 数据验证每个页面的查询、按钮、详情和导出。
最低验收标准:20 个菜单均可打开;查询按钮能返回真实或空态数据;表格字段与需求文档一致;所有枚举来自字典;高危按钮有二次确认、原因、审计与结果状态;敏感数据脱敏。
@@ -0,0 +1,390 @@
# IM Server 改造文档:支撑鸽达达管理后台复刻
本文面向 `/Users/youshunlai/go_workspace/im_server` 的现状改造,目标是为 Admin Server 复刻鸽达达后台提供准确、稳定、可审计的数据与治理能力。当前 IM Server 已有 Tinode 风格实时链路、产品 HTTP API、独立租户库、Kafka 命令消费、配置事件消费、内部 Admin Query,但鸽达达 20 个后台页面仍需要补充若干查询资源、审计日志、风控策略与消息处置能力。
## 1. 当前实现现状
核心架构:
| 能力 | 现状 |
| --- | --- |
| 实时链路 | `Session -> Hub -> Topic -> Store`。 |
| 客户端入口 | WebSocket `/v0/channels`,产品 HTTP `/v1/im/...`。 |
| 后台只读入口 | `/internal/admin-query/tenants/{tenant_code}/...`,要求内部签名与 capability。 |
| 后台写操作 | Kafka Admin Command 消费器 `startKafkaAdminCommandConsumer`。 |
| 配置同步 | Kafka config event、cache invalidate consumer 已存在。 |
| 消息转发 | `/v1/im/messages/forward` 已存在。 |
| 音视频入会 | `/v1/im/calls/rtc-join` 已存在。 |
本地 `tinode` 库观察:`im_users` 有数据、`im_topics` 有群/会话、`im_subscriptions` 有订阅、`im_messages` 有大量单聊或通用消息、`im_group_messages` 有群消息、`im_feedback_submissions`、`im_abuse_reports`、`im_kafka_dead_letters` 等表已存在;`im_audit_logs` 本地为空,`im_devices` 和 `im_group_files` 数据不足,不能直接满足后台登录历史、设备历史和群文件验收。
已有内部查询资源:
| 资源 | 已支持字段/场景 |
| --- | --- |
| `users` | 用户 ID、账号、手机号、状态、资料、凭证摘要、最后在线。 |
| `user_devices` | 用户设备列表。 |
| `groups` | Topic、群名、群主、群状态、治理开关、成员数。 |
| `groups/{topic}/members` | 群成员。 |
| `group_join-requests`、`group_invitations`、`group_announcements`、`group_mutes`、`group_admin-logs`、`group_files` | 群治理专题资源。 |
| `messages/{topic}/{seq}` | 单条消息详情。 |
| `files`、`files/{id}`、`files/{id}/links` | 文件列表、详情、链接关系。 |
| `feedback-submissions`、`abuse-reports`、`realname-verifications`、`audit-logs` | 用户反馈、举报、实名、审计。 |
| `dashboard/summary` | 当前只返回待办类指标,不足以覆盖鸽达达首页统计。 |
已有 Kafka Admin Command:
| 命令 | 用途 |
| --- | --- |
| `user.ban`、`user.unban` | 用户封禁/解封。 |
| `user.freeze`、`user.unfreeze` | 用户冻结/解冻。 |
| `user.device.kick`、`user.logout_all` | 设备踢下线、全端登出。 |
| `user.account.cancel`、`user.account.delete`、`user.password.reset` | 注销、删除、重置密码。 |
| `group.profile.update` | 群资料更新。 |
| `group.governance.update` | 群治理开关更新。 |
| `group.admin.set`、`group.admin.remove`、`group.owner.transfer` | 群角色管理。 |
| `group.member.mute`、`group.member.unmute`、`group.member.remove` | 群成员治理。 |
| `kafka.dead_letter.replay`、`ignore`、`resolve` | 死信处理。 |
## 2. 改造原则
IM Server 只负责租户业务库的数据查询、业务状态变更、实时生效与审计事件产生,不直接依赖 HotGo 后台表。Admin Server 下发的配置、策略和治理命令必须具备幂等性,并通过事件结果回传 Admin。所有 internal query 必须保持只读、分页、最大 `per_page=200`、内部签名鉴权、字段脱敏。
响应协议继续沿用现有 `imHTTPResponse`:`code`、`data`、`reason`、`message`、`details`、`request_id`、`server_time`。写请求继续要求 `Idempotency-Key`,避免后台重复点击导致重复处置。
## 3. 支撑 20 个后台页面的 IM 改造
### 3.1 数据概览
现有 `dashboard/summary` 只覆盖死信、待处理 outbox、待实名、待举报、待反馈、活跃群、封禁用户等运营待办。需要新增统计字段以匹配鸽达达首页。
新增 internal query:
| 路径 | 方法 | 说明 |
| --- | --- | --- |
| `/internal/admin-query/tenants/{tenant}/dashboard/overview` | GET | 返回总用户、活跃用户、总群组、总会话、总消息、今日消息。 |
| `/internal/admin-query/tenants/{tenant}/dashboard/trends` | GET | 返回最近 N 天用户增长与消息增长。 |
字段口径:
| 字段 | 计算建议 |
| --- | --- |
| `total_users` | `im_users` 有效用户数。 |
| `active_users` | `last_seen` 在指定窗口内或当天有消息/登录行为的用户数。 |
| `total_groups` | `im_group_settings.group_status='active'` 或有效群 topic 数。 |
| `total_conversations` | 有效 P2P topic + group topic 数。 |
| `total_messages` | `im_messages` + `im_group_messages`,按最终存储口径去重。 |
| `today_messages` | 当天创建的消息数,按租户时区对齐。 |
| `date`、`user_count`、`message_count` | 趋势表行字段。 |
### 3.2 用户管理
现有 `users`、`users/{id}`、`users/{id}/devices` 基本可支撑。需要补足鸽达达页面要求的筛选和字段脱敏。
改造点:
| 项 | 内容 |
| --- | --- |
| 查询筛选 | 增加 `phone`、`public_account`、`state`、`created_at_start/end`、`last_seen_start/end`。 |
| 字段补充 | 返回 `nickname`、头像文件 ID、性别、地区、签名、认证方式摘要、设备数、最近登录 IP。 |
| 状态映射 | 将 Tinode `state` 转换为 Admin 字典所需状态;保留原始值 `raw_state` 便于排错。 |
| 治理命令 | 继续使用已有 `user.ban/unban/freeze/unfreeze/device.kick/logout_all/account.cancel/account.delete/password.reset`。 |
| 审计 | 每次状态变更、重置密码、踢设备必须写 `im_audit_logs` 并产出 Kafka command result。 |
枚举建议:`user_state` 包含正常、封禁、冻结、注销、删除;`device_platform` 包含 `ios`、`android`、`web`、`desktop`、`unknown`。
### 3.3 用户分组
当前 IM Server 没有看到独立的后台用户策略分组执行层。Admin 将新增策略分组后,IM Server 需要消费配置事件并在运行时执行。
新增能力:
| 能力 | 说明 |
| --- | --- |
| 策略缓存 | 按租户缓存 `policy_group`、成员关系、优先级与规则。 |
| 匹配入口 | 登录、注册、加好友、建群、入群、发消息、上传文件、发起音视频前匹配策略。 |
| 配置事件 | 消费 `user_policy_group.updated`、`user_policy_group.deleted`,刷新缓存。 |
| 命中审计 | 命中 `deny`、`challenge` 或高风险规则时写审计与风控日志。 |
策略动作枚举:`allow` 放行、`deny` 拦截、`challenge` 二次验证、`audit_only` 仅记录。
好友策略需要在后端覆盖所有移动端入口。禁止加好友时,IM Server 不能只让客户端隐藏按钮,而要在搜索用户、查看个人名片、群成员资料、二维码解析、好友申请、邀请注册自动加好友、推荐联系人等入口统一拦截或返回脱敏数据。策略字段建议包含 `allow_add_friend`、`direct_add_friend`、`max_friend_count`、`allow_qrcode_add_friend`、`allow_group_member_profile`、`hide_profile_when_friend_disabled`。
### 3.4 群组管理
已有 `groups` 查询与群治理命令可复用。需要补鸽达达群状态语义和可能的群封停动作。
改造点:
| 项 | 内容 |
| --- | --- |
| 字段 | 确保 `topic`、`group_name`、`owner_id`、`group_status`、`business_member_count`、`join_approval`、`mute_all`、`created_at` 可稳定返回。 |
| 状态 | 明确 `group_status` 值:`active`、`muted`、`suspended`、`dissolved`。 |
| 命令 | 已有资料更新、治理设置、角色与成员治理;若需求要求群封停/解封,新增 `group.suspend`、`group.unsuspend`。 |
| 审计 | 群资料、治理开关、解散、禁言、成员操作均写 `im_group_admin_logs` 与 `im_audit_logs`。 |
### 3.5 群成员管理
现有 `groups/{topic}/members` 与成员命令可支撑。需要确保成员列表返回群角色、禁言状态与入群时间。
字段建议:`topic`、`user_id`、`nickname`、`display_name`、`role`、`status`、`muted_until`、`joined_at`、`last_seen`。`role` 枚举为 `owner`、`admin`、`member`;`status` 枚举为 `active`、`muted`、`removed`。
按钮对应命令:设管理员 `group.admin.set`、取消管理员 `group.admin.remove`、转让群主 `group.owner.transfer`、成员禁言 `group.member.mute`、解除禁言 `group.member.unmute`、移除成员 `group.member.remove`。
### 3.6 邀请码管理
IM Server 注册链路已在 `user.go` 中记录邀请码使用,`RecordUse` 已存在。需要解决邀请码格式冲突:鸽达达需求为 6 位数字,既有 IM 文档曾要求 5 位 ASCII 字母。若严格复刻鸽达达,应将 IM 注册校验、错误提示、唯一性检查、大小写处理统一改为 6 位数字。
改造点:
| 项 | 内容 |
| --- | --- |
| 校验 | 仅允许 `^[0-9]{6}$`,不做大小写转换。 |
| 使用记录 | 注册成功后写 `im_registration_invite_uses`,包含 `code`、`user_id`、`request_id`、`used_at`。 |
| 并发 | 使用事务或唯一约束防止超发。 |
| 状态 | `unused`、`used`、`expired`、`revoked`。 |
### 3.7 单聊消息
当前只有单条消息 detail 查询,不足以支撑后台列表。需要新增 internal message list resource。
新增 internal query:
| 路径 | 方法 | 查询参数 |
| --- | --- | --- |
| `/internal/admin-query/tenants/{tenant}/private-messages` | GET | `topic`、`from_user_id`、`to_user_id`、`content_type`、`status`、`keyword`、`created_at_start/end`、`page`、`per_page`。 |
| `/internal/admin-query/tenants/{tenant}/messages/{topic}/{seq}` | GET | 已有,继续作为详情。 |
返回字段:`topic`、`seq_id`、`from_user_id`、`to_user_id`、`head`、`content_type`、`content_excerpt`、`status`、`created_at`、`updated_at`。全文内容只在详情返回,并根据 Admin 权限决定是否脱敏。
新增 Kafka 命令建议:`message.hide`、`message.unhide`、`message.delete`、`message.restore`。如果只允许软删除,命令语义必须明确为后台不可见还是客户端不可见。
### 3.8 群聊消息
新增 internal group message list resource。可读取 `im_group_messages` 或统一消息表,但需要确保与客户端可见状态一致。
路径:`/internal/admin-query/tenants/{tenant}/group-messages`。
筛选:`topic`、`sender_user_id`、`content_type`、`status`、`keyword`、`created_at_start/end`。返回字段在单聊基础上增加 `group_name`、`reply_to_seq`、`at_users`。
处置命令复用消息命令,审计动作记录为 `message.hidden`、`message.deleted`、`message.restored`。
### 3.9 敏感词库
IM Server 需要新增敏感词运行时,而词库主数据由 Admin 控制库维护。IM Server 只消费配置事件和执行匹配。
改造点:
| 项 | 内容 |
| --- | --- |
| 缓存 | 按租户加载敏感词,支持分组、场景、优先级、状态。 |
| 匹配场景 | 消息发送、编辑消息、用户昵称/签名、群名/公告、反馈/举报内容。 |
| 动作 | `block` 拦截、`replace` 替换、`review` 进入审核、`audit_only` 仅审计。 |
| 审计 | 命中记录写 `im_sensitive_word_hits` 或统一 `im_audit_logs`。 |
| 性能 | 正常词库用 AC 自动机或等价高效结构;正则词单独限量执行。 |
### 3.10 文件管理
已有 internal `files`、`files/{id}`、`files/{id}/links` 与文件生命周期 Kafka 基础。需要补后台预览、下载和处置一致性。
改造点:
| 项 | 内容 |
| --- | --- |
| 文件状态 | `normal`、`quarantined`、`deleted`、`orphan`。 |
| 审核状态 | 复用 `pending`、`running`、`clean`、`risky`、`failed`。 |
| 预览下载 | 提供短期签名 URL 或代理下载,禁止返回永久存储地址。 |
| 隔离删除 | 后台命令应更新文件状态、断开消息/群文件链接、写审计。 |
| 安全审核 | 接入内容安全/恶意文件检测结果,并可重试审核。 |
### 3.11 用户反馈
已有 `feedback-submissions` 资源可查询。需要补处理状态回写能力和状态审计。
新增能力:
| 能力 | 说明 |
| --- | --- |
| 状态更新 | 提供 Kafka 命令或内部写接口:`feedback.status.update`。 |
| 附件 | 通过文件链接查询反馈附件,并返回签名预览 URL。 |
| 分类 | 分类来自 `im_feedback_config.categories`,Admin 配置后 IM bootstrap 同步。 |
| 审计 | 处理人、处理备注、前后状态写入审计。 |
状态枚举:`received`、`processing`、`resolved`、`closed`。
### 3.12 操作日志
IM Server 需要补充业务侧审计完整性。当前本地 `im_audit_logs` 为空,不能支撑后台多页面审计。
写入场景:用户状态变更、群治理、消息处置、文件处置、反馈处理、敏感词命中、登录成功/失败、配置热更新、Kafka 命令执行结果。
字段:`request_id`、`actor_user_id`、`action`、`target_type`、`target_id`、`ip`、`user_agent`、`detail`、`created_at`。`detail` 中敏感值必须脱敏或存 hash。
### 3.13 用户账号审计
新增或扩展 internal resource `user-account-audit`,按用户聚合账号生命周期事件。
路径:`/internal/admin-query/tenants/{tenant}/user-account-audit`。
筛选:`user_id`、`action`、`operator_id`、`ip`、`created_at_start/end`。返回:`event_id`、`user_id`、`action`、`operator_type`、`operator_id`、`before_state`、`after_state`、`reason`、`ip`、`device_id`、`created_at`。
动作枚举:`registered`、`login_success`、`logout`、`login_failed`、`password_changed`、`password_reset`、`banned`、`unbanned`、`frozen`、`unfrozen`、`device_kicked`、`cancelled`、`deleted`。
### 3.14 群事件审计
已有 `im_group_admin_logs` 与 group outbox,可新增 internal resource `group-event-audit` 或复用 `group_admin-logs` 扩展为全局查询。
路径:`/internal/admin-query/tenants/{tenant}/group-event-audit`。
筛选:`topic`、`operator_id`、`target_user_id`、`action`、`created_at_start/end`。返回:`event_id`、`topic`、`operator_id`、`target_user_id`、`action`、`object_type`、`object_id`、`before_data`、`after_data`、`created_at`。
### 3.15 管理员登录历史
该页面主要由 Admin Server 的 HotGo 登录日志提供,IM Server 无需改造。若以后要展示 Admin 调用 IM internal API 的访问审计,可在 internal API 层增加 `internal_api_access_logs`,但不作为复刻 P0。
### 3.16 用户登录历史
当前缺少完整登录历史表与查询。需要在认证成功、token 刷新、主动/被动登出时记录日志。
新增表建议:`im_user_login_logs`。
字段:`id`、`user_id`、`login_type`、`device_id`、`platform`、`ip`、`region`、`user_agent`、`request_id`、`result`、`login_at`、`logout_at`、`created_at`。
新增 internal query:`/internal/admin-query/tenants/{tenant}/user-login-logs`,支持按 `user_id`、`phone/account`、`ip`、`platform`、`login_at_start/end` 查询。
### 3.17 IP 账号统计
新增聚合查询,来源为注册、登录成功、登录失败、风控命中日志。
路径:`/internal/admin-query/tenants/{tenant}/ip-account-stats`。
字段:`ip`、`region`、`user_count`、`register_count`、`login_count`、`failure_count`、`first_seen_at`、`last_seen_at`、`risk_level`。该接口可以实时聚合,也可以由定时任务写入快照表以降低查询成本。
### 3.18 登录失败日志
新增表建议:`im_login_failure_logs`。
字段:`id`、`account`、`user_id`、`failure_type`、`reason`、`ip`、`region`、`device_id`、`platform`、`user_agent`、`request_id`、`attempt_at`。
新增 internal query:`/internal/admin-query/tenants/{tenant}/login-failure-logs`。失败类型枚举:`password_error`、`code_error`、`account_not_found`、`account_disabled`、`rate_limited`、`risk_blocked`。
### 3.19 私域设置
IM Server 需要确保客户端 `/v1/im/bootstrap` 使用 Admin 发布后的配置,并支持热更新。
配置来源:
| 配置 | 使用位置 |
| --- | --- |
| 注册/邀请码/短信 | 注册、验证码、密码找回。 |
| 上传/文件审核 | 上传、预览、消息附件。 |
| 反馈/邮箱/通知 | 客户端入口与提交表单。 |
| 版本/工作台 | bootstrap、app release、workbench。 |
| 声网/音视频 | `/v1/im/calls/rtc-join`。 |
| 好友功能 | `/v1/im/bootstrap` 返回 `global_friend_enabled`、入口隐藏建议和提示文案;实际拦截由后端完成。 |
| 消息宵禁 | `/v1/im/bootstrap` 返回当前宵禁摘要和提示文案;发送消息时仍以后端实时校验为准。 |
改造点:配置事件必须带版本号;IM 缓存应支持按租户回滚到上一发布版本;bootstrap 返回的配置只包含客户端需要的非敏感字段。
### 3.20 安全策略
IM Server 是安全策略的执行层。需要将 Admin 发布的策略接入登录、验证码、消息、文件、群、好友、实名、音视频等关键入口。全局禁止加好友和宵禁必须后端强制执行,客户端展示仅作为体验优化。
策略项:
| 策略 | 执行点 |
| --- | --- |
| 密码强度 | 注册、改密、重置密码。 |
| 登录限频 | 登录、验证码校验、密码找回。 |
| IP 黑白名单 | 登录、注册、发消息、上传文件。 |
| 敏感词 | 消息、资料、群资料、反馈。 |
| 文件审核 | 上传完成、消息发送前后、后台重审。 |
| 实名要求 | 加好友、建群、入群、发起音视频等敏感能力。 |
| 风控挑战 | 登录异常、IP 异常、设备异常。 |
| 全局好友功能 | 用户搜索、好友申请、二维码解析、群成员资料、个人名片、推荐联系人、邀请注册自动加好友。 |
| 消息宵禁 | 单聊/私聊发送消息入口,默认拦截 P2P topic 的 `{pub}`;如产品确认群聊也管控,可扩展到 group topic。 |
动作枚举统一为 `allow`、`challenge`、`block`、`audit_only`。
全局禁止加好友执行规则:
| 入口 | 后端行为 |
| --- | --- |
| 用户搜索 `/v1/im/users/search` | 可直接返回空列表,或返回脱敏结果但不允许发起好友申请。 |
| 用户详情 `/v1/im/users/{id}` | 非好友场景隐藏手机号、账号、二维码、个人扩展资料,只保留必要公开字段。 |
| 好友申请 `/v1/im/friend-requests` | 返回业务错误 reason,例如 `FRIEND_DISABLED`。 |
| 二维码解析 `/v1/im/qrcodes/resolve` | 禁止解析出可加好友目标,或返回脱敏提示。 |
| 群成员资料 | 群成员列表可保留昵称/头像用于群聊展示,但点击个人信息时返回脱敏资料并禁止添加。 |
| 邀请注册自动加好友 | 注册成功后跳过自动加好友并写审计,或按策略直接拒绝该邀请码能力。 |
宵禁执行规则:
| 项 | 说明 |
| --- | --- |
| 配置 | `message_curfew_enabled`、`message_curfew_rules`,规则包含时区、日期类型、开始时间、结束时间、scope、白名单、提示文案。 |
| 判断时机 | P2P topic 发布消息前校验;HTTP 消息发送、转发、草稿转发送等入口也要校验。 |
| 拦截结果 | 返回明确 reason,例如 `MESSAGE_CURFEW_ACTIVE`,并记录 `im_audit_logs` 或风控日志。 |
| 白名单 | 支持用户、系统账号、特定会话豁免。 |
| 跨天 | 支持如下班后 `18:00-09:00` 的跨天时间段。 |
## 4. Internal Admin Query 新增清单
建议在 `server/internal_admin_query_resources.go` 增补资源,并在 `server/internal_api.go` 路由分发中注册:
| 资源 | 必要性 | 说明 |
| --- | --- | --- |
| `dashboard/overview`、`dashboard/trends` | P0 | 首页统计。 |
| `private-messages`、`group-messages` | P0 | 消息管理列表。 |
| `user-login-logs`、`login-failure-logs` | P0 | 登录审计。 |
| `ip-account-stats` | P1 | IP 风险聚合。 |
| `user-account-audit`、`group-event-audit` | P1 | 专题审计。 |
| `sensitive-word-hits` | P1 | 敏感词命中查询。 |
所有列表接口返回现有结构:`items`、`total`、`page`、`per_page`。时间筛选继续使用 `created_at_start`、`created_at_end` 等字段,UID 参数继续支持数字 ID 与 `usr...` 编码。
## 5. Kafka 命令新增清单
已有用户与群治理命令保留。为复刻后台补充以下命令:
| 命令 | 说明 |
| --- | --- |
| `message.hide` | 后台隐藏消息,客户端不可见或后台标记隐藏。 |
| `message.unhide` | 恢复隐藏消息。 |
| `message.delete` | 后台删除消息,建议软删除。 |
| `message.restore` | 恢复误删消息。 |
| `file.quarantine` | 文件隔离。 |
| `file.delete` | 文件删除。 |
| `feedback.status.update` | 反馈处理状态更新。 |
| `group.suspend`、`group.unsuspend` | 如产品确认需要群封停/解封,则新增。 |
| `security.policy.reload` | 强制刷新安全策略缓存。 |
| `friend.policy.reload` | 强制刷新好友入口策略缓存,可与 `security.policy.reload` 合并实现。 |
| `curfew.policy.reload` | 强制刷新宵禁策略缓存,可与 `security.policy.reload` 合并实现。 |
命令 payload 必须包含 `command_id`、`actor_user_id`、`reason`、目标 ID、`version` 或 `expected_version`。执行后发布 result,状态为 `success`、`failed`、`dead`。
## 6. 数据库改造建议
配套建表与兼容迁移脚本见 [`sql/IM-Server改造.sql`](sql/IM-Server改造.sql)。脚本默认使用本地 `tinode`,上线时必须对每个独立租户数据库分别执行,租户业务表不增加 `tenant_id`。
租户库新增或补强表:
| 表 | 用途 |
| --- | --- |
| `im_user_login_logs` | 用户登录成功、登出、刷新等历史。 |
| `im_login_failure_logs` | 登录失败与风控拦截历史。 |
| `im_message_admin_actions` | 后台消息处置记录,可与消息表状态解耦。 |
| `im_sensitive_word_hits` | 敏感词命中记录。 |
| `im_ip_account_stats_daily` | IP 账号统计快照,可选。 |
已有表需补数据写入:`im_audit_logs` 当前本地为空,需要在关键业务入口统一写;`im_devices` 本地为空,需要确认设备绑定/解绑路径是否完整;`im_group_files` 本地为空,需要验证群文件写入链路。
索引建议:消息列表按 `(topic, created_at)`、`(from, created_at)`;登录日志按 `(user_id, login_at)`、`(ip, login_at)`;失败日志按 `(account, attempt_at)`、`(ip, attempt_at)`;敏感词命中按 `(scene, created_at)`、`(user_id, created_at)`。
## 7. 验收要求
1. Admin 20 个页面的查询按钮均能通过 internal query 返回真实数据或标准空态。
2. 用户、群、消息、文件、反馈的后台写操作均通过 Kafka 命令,具备幂等、结果回传、死信处理。
3. 登录历史、失败日志、账号审计、群事件审计不再依赖空表,关键入口可产生真实审计数据。
4. 敏感词与安全策略变更可热更新,无需重启 IM Server。
5. 所有 internal query 均保持只读、分页、鉴权、脱敏和错误 reason 规范。
6. 邀请码格式在 Admin 与 IM 注册链路完全一致,避免后台生成后客户端无法使用。
@@ -0,0 +1,70 @@
[
{
"endpoint": "/admin/stats",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/users",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/user-policy-groups",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/groups",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/groups/events",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/invite-codes",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/messages/search",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/sensitive-words",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/feedbacks",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/audit/logs",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/audit/user-account-logs",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/admin-login-history",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/user-login-history",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/audit/ip-user-stats",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/login-failure-logs",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/tenant-settings",
"error": "TypeError: window.fetch is not a function"
},
{
"endpoint": "/admin/security-settings",
"error": "TypeError: window.fetch is not a function"
}
]
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

@@ -0,0 +1,350 @@
-- Admin Server schema changes for the GedaDa management-console adaptation.
-- Target: MySQL 8.0+, control database `hotgo`.
-- Scope: control-plane configuration only. Do not create tenant business data here.
-- Review before execution. DDL statements auto-commit in MySQL.
SET NAMES utf8mb4;
USE `hotgo`;
-- ============================================================
-- 1. User policy groups.
-- status: 1=enabled, 2=disabled.
-- policy_json action values: allow, deny, challenge, audit_only.
-- Friend entry values: search, profile_card, group_member, qrcode,
-- invite_auto_add, recommendation.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_user_policy_groups` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global default',
`group_code` varchar(64) NOT NULL COMMENT 'Stable policy group code',
`group_name` varchar(128) NOT NULL COMMENT 'Policy group name',
`priority` int NOT NULL DEFAULT 0 COMMENT 'Higher value has higher priority',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`policy_json` json NOT NULL COMMENT 'Login, friend, message, group and file policies',
`ip_whitelist` json DEFAULT NULL COMMENT 'IPv4/IPv6 or CIDR list',
`ip_blacklist` json DEFAULT NULL COMMENT 'IPv4/IPv6 or CIDR list',
`version` int unsigned NOT NULL DEFAULT 1 COMMENT 'Optimistic-lock/config version',
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_user_policy_group_code` (`tenant_id`, `group_code`),
KEY `idx_im_user_policy_group_list` (`tenant_id`, `status`, `priority`, `id`),
KEY `idx_im_user_policy_group_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM user policy groups';
CREATE TABLE IF NOT EXISTS `im_user_policy_group_members` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0,
`group_id` bigint NOT NULL,
`user_id` bigint NOT NULL COMMENT 'User ID in the tenant database',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`valid_from` datetime(3) DEFAULT NULL,
`valid_until` datetime(3) DEFAULT NULL,
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_user_policy_group_member` (`group_id`, `user_id`),
KEY `idx_im_user_policy_member_user` (`tenant_id`, `user_id`, `status`),
KEY `idx_im_user_policy_member_validity` (`status`, `valid_from`, `valid_until`),
KEY `idx_im_user_policy_member_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM user-to-policy-group assignments';
-- ============================================================
-- 2. Sensitive word dictionary.
-- match_type: exact, contains, regex, pinyin.
-- action: replace, block, audit_only, manual_review.
-- risk_level: low, medium, high.
-- scene_json values include private_message, group_message, user_profile,
-- group_profile, feedback, report.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_sensitive_word_groups` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global dictionary',
`group_code` varchar(64) NOT NULL,
`group_name` varchar(128) NOT NULL,
`priority` int NOT NULL DEFAULT 0,
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_sensitive_word_group_code` (`tenant_id`, `group_code`),
KEY `idx_im_sensitive_word_group_list` (`tenant_id`, `status`, `priority`, `id`),
KEY `idx_im_sensitive_word_group_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM sensitive word groups';
CREATE TABLE IF NOT EXISTS `im_sensitive_words` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global dictionary',
`group_id` bigint NOT NULL,
`word` varchar(255) NOT NULL,
`word_hash` char(64) NOT NULL COMMENT 'SHA-256 of normalized word',
`match_type` varchar(24) NOT NULL DEFAULT 'contains',
`scene_json` json NOT NULL COMMENT 'Enabled scenes',
`action` varchar(24) NOT NULL DEFAULT 'block',
`replacement` varchar(255) NOT NULL DEFAULT '',
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
`priority` int NOT NULL DEFAULT 0,
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_sensitive_word_normalized` (`tenant_id`, `group_id`, `word_hash`),
KEY `idx_im_sensitive_word_list` (`tenant_id`, `status`, `risk_level`, `id`),
KEY `idx_im_sensitive_word_group` (`group_id`, `status`, `priority`),
KEY `idx_im_sensitive_word_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM sensitive words';
-- ============================================================
-- 3. Versioned security policy.
-- publish_status: draft, published, archived, failed.
-- The latest published row is the source of truth. The summary switches in
-- im_tenant_config are updated in the same application transaction on publish.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_security_policy` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '0 means global fallback policy',
`policy_version` int unsigned NOT NULL,
`policy_name` varchar(128) NOT NULL DEFAULT '',
`publish_status` varchar(16) NOT NULL DEFAULT 'draft',
`password_policy` json DEFAULT NULL,
`login_rate_limit` json DEFAULT NULL,
`sensitive_word_policy` json DEFAULT NULL,
`file_review_policy` json DEFAULT NULL,
`realname_policy` json DEFAULT NULL,
`risk_challenge_policy` json DEFAULT NULL,
`global_friend_enabled` tinyint(1) NOT NULL DEFAULT 1 COMMENT 'Backend master switch for every add-friend entry',
`friend_disabled_policy` json DEFAULT NULL COMMENT 'Profile masking, QR and group-member restrictions',
`message_curfew_enabled` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'Backend master switch for message curfew',
`remark` varchar(500) NOT NULL DEFAULT '',
`published_at` datetime(3) DEFAULT NULL,
`published_by` bigint DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_security_policy_version` (`tenant_id`, `policy_version`),
KEY `idx_im_security_policy_publish` (`tenant_id`, `publish_status`, `published_at`),
KEY `idx_im_security_policy_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Versioned IM security policies';
CREATE TABLE IF NOT EXISTS `im_message_curfew_rules` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0,
`policy_id` bigint NOT NULL,
`rule_code` varchar(64) NOT NULL,
`rule_name` varchar(128) NOT NULL,
`date_type` varchar(16) NOT NULL DEFAULT 'daily' COMMENT 'daily, weekday, weekend, custom',
`custom_dates` json DEFAULT NULL COMMENT 'YYYY-MM-DD values for custom date type',
`weekdays` json DEFAULT NULL COMMENT 'ISO weekdays 1 through 7',
`timezone` varchar(64) NOT NULL DEFAULT 'Asia/Shanghai',
`start_time` time NOT NULL,
`end_time` time NOT NULL COMMENT 'Less than start_time means crossing midnight',
`scope` varchar(24) NOT NULL DEFAULT 'private_message' COMMENT 'private_message or group_message',
`whitelist_user_ids` json DEFAULT NULL,
`whitelist_group_ids` json DEFAULT NULL,
`message` varchar(255) NOT NULL DEFAULT '当前时段暂不能发送消息',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`sort` int NOT NULL DEFAULT 0,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_message_curfew_rule` (`policy_id`, `rule_code`),
KEY `idx_im_message_curfew_policy` (`tenant_id`, `policy_id`, `status`, `sort`),
KEY `idx_im_message_curfew_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Message curfew rules';
CREATE TABLE IF NOT EXISTS `im_security_ip_rules` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` bigint NOT NULL DEFAULT 0,
`rule_type` varchar(16) NOT NULL COMMENT 'whitelist or blacklist',
`ip_cidr` varchar(64) NOT NULL COMMENT 'IPv4, IPv6 or CIDR',
`action` varchar(24) NOT NULL DEFAULT 'block' COMMENT 'allow, challenge, block, audit_only',
`scene_json` json NOT NULL COMMENT 'login, register, message, upload and other scenes',
`risk_level` varchar(16) NOT NULL DEFAULT 'high',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '1 enabled, 2 disabled',
`expires_at` datetime(3) DEFAULT NULL,
`remark` varchar(500) NOT NULL DEFAULT '',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_by` bigint NOT NULL DEFAULT 0,
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
`updated_by` bigint NOT NULL DEFAULT 0,
`deleted_at` datetime(3) DEFAULT NULL,
`deleted_by` bigint DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_security_ip_rule` (`tenant_id`, `rule_type`, `ip_cidr`),
KEY `idx_im_security_ip_rule_list` (`tenant_id`, `status`, `rule_type`, `id`),
KEY `idx_im_security_ip_rule_expiry` (`status`, `expires_at`),
KEY `idx_im_security_ip_rule_deleted` (`deleted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='IM IP whitelist and blacklist rules';
-- ============================================================
-- 4. Lightweight published-policy summary in im_tenant_config.
-- These columns are read optimizations, not the policy source of truth.
-- The conditional ALTER blocks are safe to rerun on MySQL 8.0.
-- ============================================================
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'default_policy_group_id') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `default_policy_group_id` BIGINT DEFAULT NULL COMMENT ''Default user policy group'' AFTER `max_group_members`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'security_policy_version') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `security_policy_version` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''Published security policy version'' AFTER `default_policy_group_id`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'global_friend_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `global_friend_enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''Published global friend switch'' AFTER `security_policy_version`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_tenant_config'
AND COLUMN_NAME = 'message_curfew_enabled') = 0,
'ALTER TABLE `im_tenant_config` ADD COLUMN `message_curfew_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT ''Published message curfew switch'' AFTER `global_friend_enabled`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- ============================================================
-- 5. Invitation-code compatibility for the 6-digit target format.
-- Existing 5-letter codes remain stored and valid during the application
-- rollout. New code generation must switch to exactly six ASCII digits, while
-- Admin and IM Server temporarily accept both legacy 5-letter and new 6-digit
-- values. max_uses=0 preserves the current unlimited-use behavior.
-- ============================================================
SET @im_sql = IF(
(SELECT CHARACTER_MAXIMUM_LENGTH FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_codes'
AND COLUMN_NAME = 'code') = 5,
'ALTER TABLE `im_registration_invite_codes` MODIFY COLUMN `code` CHAR(6) CHARACTER SET ascii COLLATE ascii_bin NOT NULL',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_codes'
AND COLUMN_NAME = 'max_uses') = 0,
'ALTER TABLE `im_registration_invite_codes` ADD COLUMN `max_uses` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''0 means unlimited'' AFTER `expires_at`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_registration_invite_batches'
AND COLUMN_NAME = 'max_uses_per_code') = 0,
'ALTER TABLE `im_registration_invite_batches` ADD COLUMN `max_uses_per_code` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT ''0 means unlimited'' AFTER `quantity`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- ============================================================
-- 6. Safe global fallback policy.
-- Tenant-specific published policies override tenant_id=0.
-- This keeps existing behavior permissive until an administrator publishes
-- the first tenant policy.
-- ============================================================
INSERT INTO `im_security_policy` (
`tenant_id`, `policy_version`, `policy_name`, `publish_status`,
`global_friend_enabled`, `friend_disabled_policy`,
`message_curfew_enabled`, `remark`, `published_at`, `published_by`,
`created_by`, `updated_by`
)
SELECT
0, 1, '全局默认安全策略', 'published',
1,
JSON_OBJECT(
'hide_profile', TRUE,
'block_qrcode', TRUE,
'block_group_member_entry', TRUE,
'blocked_entries', JSON_ARRAY(
'search', 'profile_card', 'group_member',
'qrcode', 'invite_auto_add', 'recommendation'
),
'message', '当前时段暂不允许添加好友'
),
0, '系统初始化:保持原有好友和消息行为', CURRENT_TIMESTAMP(3), 0, 0, 0
WHERE NOT EXISTS (
SELECT 1 FROM `im_security_policy`
WHERE `tenant_id` = 0 AND `publish_status` = 'published' AND `deleted_at` IS NULL
);
UPDATE `im_security_policy`
SET `friend_disabled_policy` = JSON_SET(
`friend_disabled_policy`,
'$.blocked_entries',
JSON_ARRAY('search', 'profile_card', 'group_member', 'qrcode', 'invite_auto_add', 'recommendation')
)
WHERE `tenant_id` = 0
AND `publish_status` = 'published'
AND `deleted_at` IS NULL
AND JSON_CONTAINS_PATH(`friend_disabled_policy`, 'one', '$.blocked_entries') = 0;
-- Post-deployment checks (read-only):
-- SELECT tenant_id, group_code, status, priority FROM im_user_policy_groups;
-- SELECT tenant_id, policy_version, publish_status,
-- global_friend_enabled, message_curfew_enabled
-- FROM im_security_policy ORDER BY tenant_id, policy_version DESC;
-- SELECT tenant_id, policy_id, rule_code, date_type, start_time, end_time, scope
-- FROM im_message_curfew_rules WHERE deleted_at IS NULL;
@@ -0,0 +1,464 @@
-- IM Server schema changes for the GedaDa management-console adaptation.
-- Target: MySQL 8.0+, one independent tenant business database.
-- Default local tenant database: `tinode`.
-- IMPORTANT: execute this migration once for every tenant database. Tenant
-- business tables intentionally do not contain tenant_id.
-- Review before execution. DDL statements auto-commit in MySQL.
SET NAMES utf8mb4;
USE `tinode`;
-- ============================================================
-- 0. User account number.
-- account_no is the short user number shown in Admin. The real primary key
-- im_users.id remains the internal Tinode/Snowflake user ID.
-- Existing users are backfilled from 1000 by created time; new users are
-- assigned by IM Server in the user creation transaction.
-- ============================================================
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_users' AND COLUMN_NAME = 'account_no') = 0,
'ALTER TABLE `im_users` ADD COLUMN `account_no` int unsigned NOT NULL DEFAULT 0 COMMENT ''用户编号'' AFTER `id`',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @im_user_account_no := (
SELECT GREATEST(COALESCE(MAX(`account_no`), 999), 999)
FROM `im_users`
WHERE `account_no` > 0
);
UPDATE `im_users` AS u
JOIN (
SELECT ordered.`id`, (@im_user_account_no := @im_user_account_no + 1) AS next_no
FROM (
SELECT `id`
FROM `im_users`
WHERE `account_no` = 0
ORDER BY `createdat`, `id`
) AS ordered
) AS seq ON seq.`id` = u.`id`
SET u.`account_no` = seq.next_no
WHERE u.`account_no` = 0;
SET @sql := IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_users' AND INDEX_NAME = 'uk_im_users_account_no') = 0,
'CREATE UNIQUE INDEX `uk_im_users_account_no` ON `im_users` (`account_no`)',
'SELECT 1'
);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
-- ============================================================
-- 1. Applied runtime policy snapshots.
-- policy_type: security, user_policy_groups, sensitive_words.
-- The application uses the latest row with apply_status=applied and also keeps
-- an in-memory cache. This table makes restart and Kafka replay deterministic.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_runtime_policy_snapshots` (
`id` bigint NOT NULL AUTO_INCREMENT,
`policy_type` varchar(32) NOT NULL,
`policy_version` bigint unsigned NOT NULL,
`payload` json NOT NULL,
`payload_sha256` char(64) NOT NULL,
`source_event_id` varchar(96) DEFAULT NULL COMMENT 'NULL for a manual bootstrap snapshot',
`apply_status` varchar(16) NOT NULL DEFAULT 'applied' COMMENT 'applied or failed',
`apply_error` varchar(1000) NOT NULL DEFAULT '',
`applied_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_runtime_policy_version` (`policy_type`, `policy_version`),
UNIQUE KEY `uk_im_runtime_policy_event` (`source_event_id`),
KEY `idx_im_runtime_policy_latest` (`policy_type`, `apply_status`, `policy_version` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Last applied Admin policy payloads';
-- ============================================================
-- 2. User login history and login failure history.
-- Never store plaintext passwords, verification codes, access tokens or refresh
-- tokens. account_hash is SHA-256 of the normalized login account; account_mask
-- is the value shown in Admin.
-- login_type: password, sms_code, sso, qr_code, token_refresh.
-- event_type: login, refresh, logout, kicked, session_expired.
-- failure_type: password_error, code_error, account_not_found,
-- account_disabled, rate_limited, risk_blocked.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_user_login_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`username` varchar(64) NOT NULL DEFAULT '',
`account_type` varchar(24) NOT NULL DEFAULT '',
`account_hash` char(64) NOT NULL DEFAULT '',
`account_mask` varchar(128) NOT NULL DEFAULT '',
`login_type` varchar(24) NOT NULL,
`event_type` varchar(24) NOT NULL DEFAULT 'login',
`session_id` varchar(96) NOT NULL DEFAULT '' COMMENT 'Opaque session identifier, never an auth token',
`device_id` varchar(255) NOT NULL DEFAULT '',
`platform` varchar(32) NOT NULL DEFAULT 'unknown',
`app_version` varchar(32) NOT NULL DEFAULT '',
`ip` varchar(64) NOT NULL DEFAULT '',
`region` varchar(128) NOT NULL DEFAULT '',
`user_agent` varchar(512) NOT NULL DEFAULT '',
`risk_level` varchar(16) NOT NULL DEFAULT 'low',
`request_id` varchar(64) NOT NULL DEFAULT '',
`login_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`logout_at` datetime(3) DEFAULT NULL,
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_user_login_user_time` (`user_id`, `login_at` DESC, `id` DESC),
KEY `idx_im_user_login_ip_time` (`ip`, `login_at` DESC, `id` DESC),
KEY `idx_im_user_login_device_time` (`device_id`(64), `login_at` DESC),
KEY `idx_im_user_login_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='User login and session event history';
CREATE TABLE IF NOT EXISTS `im_login_failure_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint DEFAULT NULL COMMENT 'NULL when the account cannot be resolved',
`account_type` varchar(24) NOT NULL DEFAULT '',
`account_hash` char(64) NOT NULL DEFAULT '',
`account_mask` varchar(128) NOT NULL DEFAULT '',
`failure_type` varchar(32) NOT NULL,
`reason` varchar(96) NOT NULL DEFAULT '' COMMENT 'Stable backend reason code',
`detail` varchar(500) NOT NULL DEFAULT '' COMMENT 'Sanitized detail without credentials',
`ip` varchar(64) NOT NULL DEFAULT '',
`region` varchar(128) NOT NULL DEFAULT '',
`device_id` varchar(255) NOT NULL DEFAULT '',
`platform` varchar(32) NOT NULL DEFAULT 'unknown',
`app_version` varchar(32) NOT NULL DEFAULT '',
`user_agent` varchar(512) NOT NULL DEFAULT '',
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
`risk_action` varchar(24) NOT NULL DEFAULT 'audit_only',
`request_id` varchar(64) NOT NULL DEFAULT '',
`attempt_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_login_failure_account_time` (`account_hash`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_user_time` (`user_id`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_ip_time` (`ip`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_type_time` (`failure_type`, `attempt_at` DESC, `id` DESC),
KEY `idx_im_login_failure_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Authentication failure and risk-block history';
-- ============================================================
-- 3. Message administration actions.
-- Messages remain in im_messages/im_group_messages. This table is an immutable
-- action overlay so hide/delete can be audited and restored.
-- conversation_type: private, group.
-- action: hide, unhide, delete, restore.
-- action_status: pending, success, failed, dead.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_message_admin_actions` (
`id` bigint NOT NULL AUTO_INCREMENT,
`action_id` varchar(96) NOT NULL,
`command_id` varchar(96) NOT NULL,
`conversation_type` varchar(16) NOT NULL,
`topic` varchar(64) NOT NULL,
`seqid` bigint NOT NULL,
`sender_user_id` bigint DEFAULT NULL,
`action` varchar(16) NOT NULL,
`action_status` varchar(16) NOT NULL DEFAULT 'pending',
`reason` varchar(500) NOT NULL DEFAULT '',
`actor_admin_id` bigint NOT NULL DEFAULT 0,
`request_id` varchar(64) NOT NULL DEFAULT '',
`before_snapshot` json DEFAULT NULL,
`after_snapshot` json DEFAULT NULL,
`failure_reason` varchar(96) NOT NULL DEFAULT '',
`failure_message` varchar(1000) NOT NULL DEFAULT '',
`processed_at` datetime(3) DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_message_admin_action_id` (`action_id`),
UNIQUE KEY `uk_im_message_admin_command` (`command_id`),
KEY `idx_im_message_admin_target` (`conversation_type`, `topic`, `seqid`, `id` DESC),
KEY `idx_im_message_admin_status` (`action_status`, `created_at`, `id`),
KEY `idx_im_message_admin_actor` (`actor_admin_id`, `created_at` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Auditable Admin message actions';
-- ============================================================
-- 4. Sensitive-word hits and policy decisions.
-- Store only a digest and a short masked excerpt. Do not duplicate complete
-- private messages in log tables.
-- policy decision reasons include FRIEND_DISABLED and MESSAGE_CURFEW_ACTIVE.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_sensitive_word_hits` (
`id` bigint NOT NULL AUTO_INCREMENT,
`word_id` bigint NOT NULL DEFAULT 0 COMMENT 'Admin control-database word ID',
`word_group_id` bigint NOT NULL DEFAULT 0,
`word_snapshot` varchar(255) NOT NULL DEFAULT '' COMMENT 'Masked word snapshot',
`scene` varchar(32) NOT NULL,
`user_id` bigint NOT NULL DEFAULT 0,
`target_user_id` bigint DEFAULT NULL,
`topic` varchar(64) NOT NULL DEFAULT '',
`seqid` bigint DEFAULT NULL,
`content_sha256` char(64) NOT NULL DEFAULT '',
`matched_excerpt` varchar(500) NOT NULL DEFAULT '',
`match_type` varchar(24) NOT NULL DEFAULT 'contains',
`action` varchar(24) NOT NULL,
`risk_level` varchar(16) NOT NULL DEFAULT 'medium',
`request_id` varchar(64) NOT NULL DEFAULT '',
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_sensitive_hit_scene_time` (`scene`, `created_at` DESC, `id` DESC),
KEY `idx_im_sensitive_hit_user_time` (`user_id`, `created_at` DESC, `id` DESC),
KEY `idx_im_sensitive_hit_target` (`topic`, `seqid`, `id` DESC),
KEY `idx_im_sensitive_hit_word` (`word_id`, `created_at` DESC),
KEY `idx_im_sensitive_hit_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Sensitive-word match events';
CREATE TABLE IF NOT EXISTS `im_policy_decision_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`policy_type` varchar(32) NOT NULL COMMENT 'security or user_policy_group',
`policy_version` bigint unsigned NOT NULL DEFAULT 0,
`policy_group_id` bigint NOT NULL DEFAULT 0,
`scene` varchar(64) NOT NULL COMMENT 'friend_request, qrcode_resolve, private_message and so on',
`action` varchar(24) NOT NULL COMMENT 'deny, challenge or audit_only',
`reason_code` varchar(96) NOT NULL COMMENT 'For example FRIEND_DISABLED or MESSAGE_CURFEW_ACTIVE',
`user_id` bigint NOT NULL DEFAULT 0,
`target_user_id` bigint DEFAULT NULL,
`topic` varchar(64) NOT NULL DEFAULT '',
`request_id` varchar(64) NOT NULL DEFAULT '',
`ip` varchar(64) NOT NULL DEFAULT '',
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
KEY `idx_im_policy_decision_user_time` (`user_id`, `created_at` DESC, `id` DESC),
KEY `idx_im_policy_decision_reason_time` (`reason_code`, `created_at` DESC, `id` DESC),
KEY `idx_im_policy_decision_scene_time` (`scene`, `created_at` DESC, `id` DESC),
KEY `idx_im_policy_decision_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Runtime policy deny/challenge decisions';
-- ============================================================
-- 5. Group event history and daily IP/account statistics.
-- im_group_events_outbox remains the delivery table; this log is the queryable
-- business history after the operation result is known.
-- ============================================================
CREATE TABLE IF NOT EXISTS `im_group_event_logs` (
`id` bigint NOT NULL AUTO_INCREMENT,
`event_id` varchar(96) NOT NULL,
`topic` varchar(64) NOT NULL,
`group_id` bigint DEFAULT NULL,
`event_type` varchar(64) NOT NULL,
`actor_user_id` bigint NOT NULL DEFAULT 0,
`target_user_id` bigint DEFAULT NULL,
`result` varchar(16) NOT NULL DEFAULT 'success',
`reason` varchar(500) NOT NULL DEFAULT '',
`request_id` varchar(64) NOT NULL DEFAULT '',
`before_snapshot` json DEFAULT NULL,
`after_snapshot` json DEFAULT NULL,
`metadata` json DEFAULT NULL,
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_group_event_log_event` (`event_id`),
KEY `idx_im_group_event_topic_time` (`topic`, `created_at` DESC, `id` DESC),
KEY `idx_im_group_event_type_time` (`event_type`, `created_at` DESC, `id` DESC),
KEY `idx_im_group_event_actor_time` (`actor_user_id`, `created_at` DESC, `id` DESC),
KEY `idx_im_group_event_request` (`request_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Queryable group event audit history';
CREATE TABLE IF NOT EXISTS `im_ip_account_stats_daily` (
`id` bigint NOT NULL AUTO_INCREMENT,
`stat_date` date NOT NULL,
`ip` varchar(64) NOT NULL,
`region` varchar(128) NOT NULL DEFAULT '',
`account_count` int unsigned NOT NULL DEFAULT 0,
`user_count` int unsigned NOT NULL DEFAULT 0,
`login_success_count` int unsigned NOT NULL DEFAULT 0,
`login_failure_count` int unsigned NOT NULL DEFAULT 0,
`first_seen_at` datetime(3) DEFAULT NULL,
`last_seen_at` datetime(3) DEFAULT NULL,
`risk_level` varchar(16) NOT NULL DEFAULT 'low',
`risk_action` varchar(24) NOT NULL DEFAULT 'audit_only',
`sample_user_ids` json DEFAULT NULL COMMENT 'Bounded sample only, not an unlimited array',
`created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
`updated_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
PRIMARY KEY (`id`),
UNIQUE KEY `uk_im_ip_account_stats_day_ip` (`stat_date`, `ip`),
KEY `idx_im_ip_account_stats_ip_day` (`ip`, `stat_date` DESC),
KEY `idx_im_ip_account_stats_risk_day` (`risk_level`, `stat_date` DESC, `id` DESC)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Daily IP and account aggregation';
-- ============================================================
-- 6. Compatible enhancements to existing tables.
-- Every ALTER is guarded by information_schema and can be rerun.
-- ============================================================
-- Message management queries need topic/time and sender/time indexes.
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_messages'
AND INDEX_NAME = 'idx_im_messages_topic_created') = 0,
'ALTER TABLE `im_messages` ADD INDEX `idx_im_messages_topic_created` (`topic`, `createdat`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_messages'
AND INDEX_NAME = 'idx_im_messages_from_created') = 0,
'ALTER TABLE `im_messages` ADD INDEX `idx_im_messages_from_created` (`from`, `createdat`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- Extend the existing Tinode device record for device and login-history views.
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'firstseen') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `firstseen` DATETIME(3) DEFAULT NULL AFTER `platform`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'lastip') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `lastip` VARCHAR(64) NOT NULL DEFAULT '''' AFTER `lastseen`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'useragent') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `useragent` VARCHAR(512) NOT NULL DEFAULT '''' AFTER `lastip`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'appversion') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `appversion` VARCHAR(32) NOT NULL DEFAULT '''' AFTER `useragent`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_devices'
AND COLUMN_NAME = 'status') = 0,
'ALTER TABLE `im_devices` ADD COLUMN `status` VARCHAR(16) NOT NULL DEFAULT ''active'' AFTER `appversion`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- Feedback handling fields required by the Admin processing workflow.
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'assigned_to') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `assigned_to` BIGINT DEFAULT NULL AFTER `status`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'handled_by') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `handled_by` BIGINT DEFAULT NULL AFTER `assigned_to`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'handled_at') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `handled_at` DATETIME(3) DEFAULT NULL AFTER `handled_by`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'resolution') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `resolution` VARCHAR(1000) NOT NULL DEFAULT '''' AFTER `handled_at`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND COLUMN_NAME = 'admin_remark') = 0,
'ALTER TABLE `im_feedback_submissions` ADD COLUMN `admin_remark` VARCHAR(1000) NOT NULL DEFAULT '''' AFTER `resolution`',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_feedback_submissions'
AND INDEX_NAME = 'idx_im_feedback_status_time') = 0,
'ALTER TABLE `im_feedback_submissions` ADD INDEX `idx_im_feedback_status_time` (`status`, `created_at`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
SET @im_sql = IF(
(SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'im_audit_logs'
AND INDEX_NAME = 'idx_im_audit_logs_ip_time') = 0,
'ALTER TABLE `im_audit_logs` ADD INDEX `idx_im_audit_logs_ip_time` (`ip`, `created_at`, `id`)',
'SELECT 1'
);
PREPARE im_stmt FROM @im_sql;
EXECUTE im_stmt;
DEALLOCATE PREPARE im_stmt;
-- Post-deployment checks (read-only):
-- SELECT policy_type, policy_version, apply_status, applied_at
-- FROM im_runtime_policy_snapshots ORDER BY policy_type, policy_version DESC;
-- SELECT reason_code, scene, COUNT(*) AS deny_count
-- FROM im_policy_decision_logs
-- WHERE created_at >= CURRENT_DATE
-- GROUP BY reason_code, scene;
-- SELECT stat_date, ip, account_count, login_failure_count, risk_level
-- FROM im_ip_account_stats_daily ORDER BY stat_date DESC, login_failure_count DESC;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,570 @@
# 服务端消息加密与内容审核改造文档
> 文档状态:待开发
> 编制日期:2026-09-13
> 适用项目:`im_manager`(Admin Server)、`im_server`(IM Server)
> 本文档是后续数据库设计、后端开发、联调、迁移和验收的共同依据。
## 1. 建设目标
本次改造同时建设两项能力:
1. 内容审核:IM Server 在保存或修改内容前,根据商户策略执行本地敏感词检查和阿里云文本内容审核。
2. 消息加密存储:审核通过的消息在 IM Server 内部使用 AES-256-GCM 加密,国内 IM 业务库只保存密文;经授权的后台查询由 IM Server 解密后返回明文。
本次不做以下内容:
- 不做客户端 AES 二次加密。
- 不做端到端加密(E2EE)。
- 不改变现有 HTTPS/WSS 传输协议。
- 不允许 Admin Server 直接读取 IM 数据库并自行解密。
- 不在日志、Kafka 事件、死信或监控标签中保存消息明文。
## 2. 安全边界和部署前提
### 2.1 部署分工
| 区域 | 系统 | 保存数据 |
| --- | --- | --- |
| 国外 | HotGo / Admin Server | 商户审核策略、阿里云配置、字典根密钥、商户消息密钥 |
| 国内 | IM Server | 用户、会话、消息密文、审核结果和处置记录 |
该方案主要防护目标是:任意一侧数据库单独泄露时,不能直接恢复消息明文。
已知边界:HotGo 数据库、IM 数据库和完整代码同时泄露不在本方案的主要防护范围内。
### 2.2 传输安全
- 客户端调用必须使用 `https://` 和 `wss://`。
- 阿里云审核请求必须使用 HTTPS。
- Admin Server 与 IM Server 的内部配置和查询接口必须使用 HTTPS,并保留现有签名鉴权;跨公网部署时建议叠加 mTLS 或固定出口 IP 白名单。
- TLS 只解决传输保护,AES-256-GCM 解决消息落库后的静态保护。
## 3. 总体架构
```text
客户端
| HTTPS/WSS明文业务数据(TLS通道内不可见)
v
IM Server
|-- 实时权限、宵禁、用户状态等校验
|-- 获取商户审核策略
|-- 本地敏感词补充检查
|-- 阿里云 TextModerationPlus 审核
|-- AES-256-GCM 加密
`-- 密文写入国内IM库
Admin Server(国外HotGo)
|-- 管理阿里云配置
|-- 管理商户审核策略
|-- 管理商户消息密钥
|-- 通过内部接口下发配置/密钥
`-- 通过IM内部查询接口查看解密后消息
```
## 4. 内容审核设计
### 4.1 审核范围
| 场景 | 阿里云 Service | 审核时机 |
| --- | --- | --- |
| 用户昵称 | `nickname_detection_pro` | 更新用户资料前 |
| 群昵称 | `nickname_detection_pro` | 更新群成员资料前 |
| 群名称 | `nickname_detection_pro` | 创建群或修改群资料前 |
| 用户签名 | `nickname_detection_pro` | 更新用户资料前 |
| 单聊文本 | `chat_detection_pro` | 消息入库及广播前 |
| 群聊文本 | `chat_detection_pro` | 消息入库及广播前 |
| 编辑后消息 | `chat_detection_pro` | 新版本入库前 |
| 群公告 | `chat_detection_pro` | 发布或修改前 |
图片、视频和文件继续走文件审核链路,不属于本文本审核接口范围。
### 4.2 商户范围
- 第一阶段只为 `tenant.code=default` 的上架默认商户开启阿里云内容审核。
- 其他团队商户默认关闭,不调用阿里云,不产生审核费用。
- 数据模型保留按商户开启的能力,但只有平台管理员可变更。
- 商户端管理员不得看到阿里云 AK/SK,不得修改平台级审核服务配置。
### 4.3 后台配置项
#### 4.3.1 阿里云服务配置
复用 `im_external_config` 的 `service=moderation`,由平台管理员维护:
| 配置 | 说明 |
| --- | --- |
| `provider` | 固定为 `aliyun_green` |
| `endpoint` | 默认 `green-cip.cn-shanghai.aliyuncs.com` |
| `enabled` | 平台总开关 |
| `config_json.region_id` | 默认 `cn-shanghai` |
| `config_json.nickname_service` | 默认 `nickname_detection_pro` |
| `config_json.chat_service` | 默认 `chat_detection_pro` |
| `config_json.timeout_ms` | 单次请求超时,建议 1000~2000 ms |
| `config_json.failure_mode` | `allow` 或 `block` |
| `secret_json.access_key_id` | 只写不回显 |
| `secret_json.access_key_secret` | 只写不回显 |
“检查配置”必须真实调用阿里云测试文本,不能只检查字段非空。返回仅包含成功与否、请求耗时、RequestID 和脱敏错误。
#### 4.3.2 商户审核策略
可在 `im_security_policy.sensitive_word_policy` 中保存,避免新增重复策略表。JSON 结构固定为:
```json
{
"enabled": true,
"aliyun_enabled": true,
"local_words_enabled": true,
"scenes": {
"user_nickname": true,
"user_signature": true,
"group_name": true,
"group_nickname": true,
"private_message": true,
"group_message": true,
"group_announcement": true
},
"risk_actions": {
"high": "block",
"medium": "block",
"low": "audit_only",
"none": "allow"
},
"failure_mode": "allow",
"block_message": "内容不符合平台规范,请修改后重试"
}
```
策略发布后使用现有 `im.config.event` 发布配置事件,IM Server 按商户清理本地缓存。
### 4.4 文本提取
审核输入不能直接对任意 JSON 执行字符串化,应按 Drafty 或消息类型提取用户可见文本:
- 普通文本:提取完整文本。
- Drafty 富文本:提取 `txt` 及按钮、链接等用户可见标题。
- 图片/文件/语音:审核文件名、说明和附带文本,文件本体走文件审核。
- 系统消息、音视频信令和已签名服务端消息不调用文本审核。
- 空文本直接放行。
### 4.5 阿里云结果处置
IM Server 统一映射为:
| 内部结果 | 动作 |
| --- | --- |
| `allow` | 继续加密、入库和广播 |
| `block` | 不入库、不广播,返回统一业务错误 |
| `audit_only` | 允许发送,但写入审核命中日志 |
| `retryable_error` | 根据 `failure_mode` 放行或拦截 |
不只依赖 `RiskLevel/Label` 一个字段,需保留与验证阿里云原始结果中的扩展判断,针对引流、诈骗等样本建立回归测试集。
### 4.6 超时与降级
- 阿里云请求不得无限等待。
- 建议默认 `failure_mode=allow`,阿里云异常时允许发送并记录告警,防止第三方故障造成 IM 全局不可用。
- 连续失败达到阈值后进入短时熔断;熔断期间按失败策略处理。
- 阿里云调用不得在数据库事务中执行。
## 5. 消息加密设计
### 5.1 算法
- 算法:AES-256-GCM。
- DEK 长度:32 字节。
- Nonce:每次加密使用 `crypto/rand` 生成的独立 12 字节随机值。
- GCM 标签:使用 Go 标准库默认 16 字节认证标签。
- 明文编码:将原 `content` JSON 规范化序列化为 UTF-8 字节。
- 不允许固定 Nonce,不允许 AES-ECB,不使用未认证的 AES-CBC/CTR。
### 5.2 AAD
每条消息加密时绑定以下 AAD:
```text
tenant_id | topic | seqid | from | client_msg_id | key_version | content
```
`content` 是字面标识,用于与未来可能加密的 `head` 区分。AAD 按固定字段顺序和长度前缀编码,禁止使用可产生拼接歧义的简单字符串。
### 5.3 密钥层级
```text
HotGo字典根密钥(KEK)
-> 加密每个商户、每个版本的DEK
-> DEK加密该商户消息
```
根密钥使用 HotGo `hg_sys_dict_data` 保存,但必须满足:
1. 使用独立的内部字典类型,不出现在通用字典管理页。
2. 后端字典列表、选项和导出接口必须对该类型做服务端拒绝,不能仅依赖菜单隐藏。
3. 根密钥为 32 字节强随机值的 Base64 编码,不使用口令、UUID 或可读文本。
4. 根密钥只由 Admin Server 密钥服务读取,不通过通用配置接口返回。
5. 根密钥不写日志,不在后台页面回显,不进入 Kafka 消息。
### 5.4 商户密钥管理表
在 HotGo 库新增 `im_message_encryption_keys`:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | bigint | 主键 |
| `tenant_id` | bigint | 商户 ID |
| `key_version` | int | 密钥版本,从 1 递增 |
| `algorithm` | varchar(32) | 固定 `AES-256-GCM` |
| `encrypted_dek` | blob | KEK 加密后的 DEK |
| `dek_nonce` | varbinary(12) | 加密 DEK 时的随机 Nonce |
| `status` | varchar(16) | `pending` / `active` / `retired` / `disabled` |
| `created_at` | datetime | 创建时间 |
| `activated_at` | datetime | 启用时间 |
| `retired_at` | datetime | 退役时间 |
| `created_by` | bigint | 操作人 |
| `remark` | varchar(255) | 备注 |
约束:
- 唯一键:`tenant_id + key_version`。
- 每个商户同时只能有一把 `active` 密钥。
- `retired` 密钥仍可用于解密历史消息,不得再用于新消息加密。
- `disabled` 只用于安全事故应急,禁用前必须完成历史消息转密钥。
### 5.5 IM 消息表改造
`im_messages` 和 `im_message_revisions` 新增:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `content_ciphertext` | longblob | 消息密文,包含 GCM 认证标签 |
| `content_nonce` | varbinary(12) | 本条消息随机 Nonce |
| `key_version` | int | 商户 DEK 版本 |
| `encryption_alg` | varchar(32) | `AES-256-GCM` |
| `encrypted` | tinyint | `0` 明文兼容,`1` 密文 |
迁移完成前保留现有 `content JSON`,规则如下:
- 新写入:`content=NULL`、`encrypted=1`,只保存密文。
- 旧数据:`content IS NOT NULL`、`encrypted=0`,按原逻辑读取。
- 解密成功后还原为原 `any` JSON 结构,不修改对客户端的协议。
- `head` 第一阶段不加密,但禁止在 `head` 中放置消息正文、联系方式等敏感明文。
`im_group_messages` 是群消息派生查询表,不得另存一份明文。实现时二选一:
1. 推荐:仅保存索引、状态和统计字段,通过 `topic + seqid` 关联 `im_messages` 解密。
2. 兼容:同步增加密文字段,但加密输入和密文必须与主消息表保持一致。
不允许 `im_messages` 已加密而 `im_group_messages.content` 仍保留明文。
### 5.6 加密和解密时机
#### 新消息
```text
校验消息
-> 提取审核文本
-> 内容审核
-> 分配seqid/组装AAD
-> 序列化content
-> 使用当前active DEK加密
-> 事务写入密文和消息元数据
-> 向在线订阅者广播原内存明文
```
#### 历史消息和离线同步
```text
从数据库读取密文
-> 按tenant_id + key_version获取DEK
-> 重建AAD
-> GCM验签并解密
-> JSON反序列化
-> 按原Tinode协议返回
```
任何验签失败均必须当作数据篡改或密钥错误,禁止返回部分内容或尝试忽略错误。
### 5.7 密钥获取和缓存
- IM Server 不得每发送一条消息都跨境请求 HotGo。
- IM Server 按 `tenant_id + key_version` 在内存中缓存已解密 DEK。
- 新消息使用当前 `active` 密钥,历史消息按记录中的 `key_version` 取密钥。
- 密钥变更发布 `message.encryption.key.updated` 配置事件,IM Server 按商户刷新。
- HotGo 短时不可用时继续使用内存密钥。
- IM Server 重启后如无法取得当前密钥,该商户禁止写入新消息,不得降级为明文落库。
### 5.8 密钥轮换
1. Admin Server 生成新 DEK,使用 KEK 加密后以 `pending` 状态入库。
2. 通知 IM Server 拉取并验证新密钥。
3. 验证成功后,在事务中将旧密钥设为 `retired`,新密钥设为 `active`。
4. 新消息立即使用新版本,旧消息仍按原版本解密。
5. 历史转密钥作为单独低速任务执行,不阻塞即时通信。
## 6. Admin Server 改造
### 6.1 内容审核页面
将现有“审核插件”配置补齐为可用功能:
- 阿里云基础配置。
- 真实连通性检查。
- 商户审核开关和场景开关。
- 风险等级与处置动作映射。
- 异常降级策略。
- 配置发布版本和生效时间。
非默认商户显示“未开启内容审核”,不显示阿里云密钥编辑项。
### 6.2 消息加密管理页面
增加平台级“消息加密”页面,只允许最高权限平台管理员使用:
- 查看商户加密状态、当前密钥版本和最后轮换时间。
- 初始化商户密钥。
- 启用/暂停商户消息加密。
- 轮换密钥。
- 查看历史迁移进度。
- 密钥明文、根密钥和 `encrypted_dek` 均不展示。
### 6.3 内部接口
建议增加:
| 方法 | 路径 | 用途 |
| --- | --- | --- |
| GET | `/internal/im-config/tenants/{code}/moderation-policy` | IM Server 拉取商户审核策略 |
| GET | `/internal/im-config/moderation/provider` | IM Server 拉取阿里云配置 |
| GET | `/internal/im-config/tenants/{code}/message-keys/active` | 获取当前消息 DEK |
| GET | `/internal/im-config/tenants/{code}/message-keys/{version}` | 获取指定版本 DEK |
| POST | `/internal/im-config/tenants/{code}/message-keys/ack` | IM Server 确认新密钥可用 |
密钥接口返回的是服务端使用的 DEK,不允许从浏览器或通用 Admin API 调用。接口必须校验服务身份、时间窗、签名、请求 ID 和重放。
### 6.4 后台消息查询
- Admin Server 继续调用 IM Server internal admin-query,不直连 IM 业务库解密。
- IM Server 在商户数据权限通过后批量解密当页消息。
- 每次查看明文记录管理员 ID、商户、IP、查询条件、返回数量、时间和 TraceID。
- 列表只解密当页,禁止一次加载全部历史消息。
## 7. IM Server 改造
### 7.1 新增内部模块
```text
server/moderation/
provider.go 审核Provider接口
policy.go 商户策略和缓存
text.go 文本提取与结果映射
aliyun/text.go TextModerationPlus实现
server/messagecrypto/
cipher.go AES-256-GCM加解密
key_provider.go 密钥获取接口
key_cache.go tenant/version内存缓存
serializer.go content规范序列化
```
不将加密和阿里云调用直接写进 MySQL adapter;adapter 只保存已加密的存储模型和解密所需元数据。
### 7.2 消息发送接入点
在 `Topic.handlePubBroadcast` 中,于 `saveAndBroadcastMessage` 之前执行:
1. 过滤 WebRTC/系统信令等不审核内容。
2. 提取文本。
3. 根据商户策略执行审核。
4. 拦截时通过 Tinode ctrl 回复业务错误,不进入保存和广播。
5. 放行时进入加密保存。
进程内广播使用本次请求已有明文,不为广播执行“加密后立即解密”。
### 7.3 资料修改接入点
用户昵称/签名、群名称/公告、群昵称的 HTTP 和 Tinode meta 修改通道都必须接入同一 moderation service,不能只修改某一个 HTTP API 而留下协议绕过路径。
### 7.4 存储层改造
- `types.Message` 和 `types.MessageRevision` 增加密文及密钥元数据字段。
- MySQL 的普通库和共享库/多商户 adapter 必须同时改造。
- 新建库 schema、启动 ensure 迁移和手工 SQL 脚本必须保持一致。
- Save、GetOne、GetAll、编辑、撤回、删除、历史修订、管理后台查询均需覆盖。
- 解密层应在业务存储服务边界统一执行,避免上层遗漏。
### 7.5 缓存与多 Pod
- 每个 Pod 保持本地密钥和审核策略缓存。
- Kafka 配置事件使所有 Pod 同时失效。
- 事件处理必须幂等,重复消息不得导致重复创建密钥。
- 新旧密钥切换期间,所有 Pod 必须同时具备旧版本解密和新版本加密能力。
## 8. 审核和解密日志
在 IM 业务库增加 `im_content_moderation_logs`:
| 字段 | 说明 |
| --- | --- |
| `id` | 主键 |
| `request_id` | 审核请求 ID |
| `scene` | 昵称、单聊、群聊等 |
| `topic` / `seqid` | 关联消息,拦截时 `seqid` 可为空 |
| `actor_id` | 内容提交人 |
| `provider` | `local` / `aliyun_green` |
| `provider_request_id` | 第三方 RequestID |
| `risk_level` | 统一风险等级 |
| `risk_labels` | 命中标签 JSON |
| `action` | `allow` / `block` / `audit_only` |
| `content_hash` | SHA-256 摘要,不保存明文 |
| `latency_ms` | 审核耗时 |
| `error_code` | 脱敏错误码 |
| `created_at` | 创建时间 |
后台明文查看继续写入统一审计事件,必须区分“列表解密”、“详情解密”和“导出解密”。默认不提供明文批量导出;需要开放时必须增加独立权限和审批。
## 9. 关键词查询限制
消息加密后,MySQL 无法对密文执行原有 `LIKE '%keyword%'`。第一阶段采用:
1. 后台查询必须先提供商户、时间范围,并尽量提供用户或群号。
2. IM Server 先用明文元数据缩小候选集。
3. 对有上限的候选集批量解密,再在内存中匹配关键词。
4. 限制查询最大时间范围和候选消息数,超限返回明确提示。
不建议在第一阶段建设明文分词索引,否则会降低加密保护效果。若后续必须支持全库高性能检索,再单独评审 HMAC 盲索引方案。
## 10. 历史数据迁移
### 10.1 发布顺序
1. 发布 HotGo 表结构、根密钥和密钥管理能力。
2. 为默认商户初始化第 1 版 DEK,但不开启加密。
3. 发布 IM Server 双读兼容版本,验证密钥获取、加解密和内容审核。
4. 开启新消息加密写入,保持旧数据明文可读。
5. 启动历史数据分批迁移。
6. 对比数量、抽样解密和 GCM 验签,完成后清空明文 `content`。
7. 观察期结束后,再决定是否删除原明文字段。
### 10.2 迁移任务要求
- 按主键或 `topic + seqid` 分批,不长时锁表。
- 每批使用独立事务,记录游标、成功数、失败数和最后错误。
- 重复执行必须幂等,`encrypted=1` 的记录直接跳过。
- 先写入密文并验证可解密,再将明文字段置空。
- 失败记录保留明文并进入重试列表,不得丢失原数据。
- 迁移速率可配置,并监控数据库负载和消息延迟。
## 11. 错误码和客户端兼容
客户端不参与加解密,不修改消息协议。新增业务错误建议:
| 错误 | 说明 | 客户端处理 |
| --- | --- | --- |
| `CONTENT_REJECTED` | 内容审核拒绝 | 保留未发送文本并提示修改 |
| `CONTENT_REVIEW_UNAVAILABLE` | 审核不可用且策略要求拦截 | 提示稍后重试 |
| `MESSAGE_ENCRYPTION_UNAVAILABLE` | 未取得加密密钥 | 提示服务暂时不可用 |
| `MESSAGE_DECRYPT_FAILED` | 历史消息验签/解密失败 | 展示“消息暂时无法加载”,不展示乱码 |
不向客户端返回阿里云原始错误、风险标签详情、密钥版本内部信息或解密堆栈。
## 12. 监控指标
### 12.1 内容审核
- 按商户/场景统计请求量、放行数、拦截数、降级数。
- 阿里云耗时 P50/P95/P99。
- 超时率、错误率、熔断状态。
- 各风险等级和标签命中数。
### 12.2 消息加密
- 加密/解密成功数、失败数和耗时。
- 各商户当前密钥版本。
- 密钥获取成功率、缓存命中率和缓存最后刷新时间。
- 明文历史记录剩余数、迁移速率和迁移失败数。
- GCM 验签失败必须产生高优先级告警。
监控标签不得使用消息正文、密文、Nonce、DEK 或 KEK。
## 13. 验收标准
### 13.1 审核验收
- `default` 商户开启后,单聊、群聊、用户昵称、群名称和群昵称均实际调用对应阿里云 Service。
- 其他商户关闭时不调用阿里云。
- 拦截内容不进入 `im_messages` 和 `im_group_messages`,也不向订阅者广播。
- 本地敏感词与阿里云开关可独立启停。
- 阿里云超时严格按 `failure_mode` 处理。
- 配置发布后所有 IM Pod 在规定时间内生效。
### 13.2 加密验收
- 开启商户的新消息在数据库中不存在可读正文。
- 相同内容连续发送两次,Nonce 和密文必须不同。
- 修改密文、Nonce、AAD 关联字段的任意一处后,解密必须失败。
- 在线发送、离线同步、多端漫游、编辑、回复、撤回、删除保持原有业务行为。
- Admin 有权限账号可查看解密明文,无权限账号被拒绝。
- 查看明文必须形成完整审计记录。
- 旧密钥退役后仍可解密旧版本消息,新消息只使用新版本。
- HotGo 暂时断开时,已有内存密钥的 IM Pod 仍可工作;无密钥时绝不允许明文降级落库。
### 13.3 性能验收
- 纯 AES 加密不得成为消息链路主要瓶颈。
- 密钥缓存命中时不发生跨境 HotGo 请求。
- 审核关闭的商户不增加阿里云网络耗时。
- 审核开启时应分别统计本地处理、阿里云调用和消息入库耗时。
- 历史迁移期间即时消息发送和数据库负载不得超过上线阈值。
## 14. 开发分批建议
### 批次一:配置和密钥基础
- Admin 阿里云内容审核配置、商户策略、密钥表和密钥管理页。
- 字典根密钥隔离和专用密钥服务。
- Admin internal config/key API 及 Kafka 失效事件。
### 批次二:IM 文本审核
- 阿里云 `TextModerationPlus` Go 客户端。
- 策略缓存、文本提取、消息和资料修改接入。
- 审核日志、错误码、告警和回归测试。
### 批次三:新消息加密
- AES-256-GCM 模块、密钥缓存和存储模型。
- 新消息加密写入和历史消息解密读取。
- 单聊、群聊、修订、后台查询的一致性验证。
### 批次四:历史迁移和轮换
- 历史明文分批加密。
- 迁移仪表盘、少量失败重试和数据对账。
- 密钥轮换和历史转密钥测试。
- 完成观察后清理明文。
## 15. 开发前必须确认的决策
| 决策 | 文档默认值 |
| --- | --- |
| 阿里云异常时放行还是拦截 | 放行并告警 |
| 是否审核系统消息 | 不审核 |
| 是否加密 `head` | 第一阶段不加密 |
| 第一阶段启用商户 | 仅 `code=default` |
| 是否允许无密钥时明文降级 | 不允许 |
| 后台是否允许明文导出 | 默认不允许 |
| 历史消息是否迁移 | 是,分批迁移 |
| 根密钥位置 | HotGo 内部字典数据,严禁通用接口暴露 |
## 16. 现有实现对照
| 能力 | 现状 | 本次改造 |
| --- | --- | --- |
| 阿里云文本审核 | 文档已人工验证,IM Server 无 Go 调用实现 | 增加 TextModerationPlus 客户端和业务接入 |
| 本地敏感词 | Admin 已有 CRUD,IM Server 未消费 | 作为可选补充审核 |
| 审核配置 | `im_external_config` 已有 `moderation` 占位 | 增加真实检查、商户策略和发布 |
| 配置同步 | 已有 internal config 客户端、Kafka 配置事件和缓存失效 | 复用并扩展新的策略/密钥类型 |
| 消息存储 | `im_messages.content` 和修订表 `content` 为 JSON 明文 | 改为 AES-256-GCM 密文且双读迁移 |
| 群消息派生表 | `im_group_messages.content` 为 JSON 明文 | 去明文副本或同步密文化 |
| 后台消息查询 | IM internal-query 返回库内内容 | 增加权限校验、解密和查看审计 |
@@ -0,0 +1,460 @@
# 鸽达达管理后台改造测试用例
> 依据:`需求文档.md`、`Admin-Server改造文档.md`、`IM-Server改造文档.md`及当前 Admin Server、IM Server 代码。
> 测试环境:`https://admin.118im.com/admin#/dashboard/console`
> 文档日期:2026-09-07
## 1. 测试目标与范围
本测试用于验证鸽达达管理后台改造后的页面操作、Admin Server 接口、IM Server 内部查询与命令执行是否一致,并重点验证以下闭环:
1. 后台查询条件、表格字段、详情、导出及权限控制正确。
2. Admin Server 能正确访问 IM Server,真实展示概览、消息和登录审计数据。
3. 消息隐藏、删除经 Kafka 下发后由 IM Server 幂等执行并记录结果。
4. 全局禁止加好友发布后,移动端所有加好友入口均受后端控制,不能绕过。
5. 宵禁发布后,命中规则的单聊/私聊不能发送消息;配置群聊规则时群聊也必须受限。
6. 登录成功、失败及退出均产生安全审计数据,且账号、口令和验证码等敏感信息不泄露。
### 1.1 本轮交付范围
| 页面/能力 | 本轮状态 | 测试结论口径 |
|---|---|---|
| 3.1 数据概览 | 已改造 | 本轮必测,需联调 Admin 与 IM |
| 3.3 用户分组 | Admin 已改造为用户策略分组 | 后台 CRUD 本轮必测;IM 策略消费待补齐 |
| 3.7 单聊消息 | 已改造 | 本轮必测,需验证 Kafka 处置闭环 |
| 3.8 群聊消息 | 已改造 | 本轮必测,需验证 Kafka 处置闭环 |
| 3.9 敏感词库 | Admin 管理端已改造 | 后台 CRUD 本轮必测;IM 运行时拦截待补齐 |
| 3.16 用户登录历史 | 已改造 | 本轮必测,数据来自 IM Server |
| 3.18 登录失败日志 | 已改造 | 本轮必测,包含加入 IP 黑名单、封禁用户 |
| 3.20 安全策略 | Admin 管理与发布已改造,IM 执行端未完成 | P0 验收目标;当前不得判定端到端通过 |
| 其余 3.2、3.4-3.6、3.10-3.15、3.17、3.19 | 原系统能力/后续改造 | 执行基础回归,不作为本轮新增功能全部交付的判定依据 |
### 1.2 代码核查结论
本节是对当前代码的静态核查结论,不等同于测试环境已经执行通过:
| 服务 | 已实现内容 | 当前缺口/测试关注点 |
|---|---|---|
| Admin Server | 数据概览、单聊/群聊消息查询与处置、用户登录历史、登录失败日志;用户策略分组、敏感词、安全策略和 IP 规则的管理页面、接口、权限与数据表 | 对 IM 的依赖异常、异步命令最终状态、租户隔离仍需按本文实测 |
| IM Server | 概览/趋势、单聊/群聊消息、登录成功/失败日志的 Internal Query;`message.hide`、`message.delete` 消费、幂等处置及动作落库;真实登录成功、失败和退出审计 | 未找到 `security.policy.updated`、用户策略分组、敏感词策略的运行时消费;未找到全局禁加好友、资料隐藏、二维码拦截和消息宵禁在 IM 请求链路的执行代码 |
| 联调结论 | 消息治理、概览和登录审计具备端到端测试基础 | 安全策略页面“发布成功”目前只能证明 Admin 保存并发出事件,不能证明移动端已生效;`FRIEND-*`、`CURFEW-*`、敏感词运行时用例预计失败或阻塞,须补齐 IM 实现后回归 |
因此,测试报告必须分别记录“Admin 配置成功”“事件发布成功”“IM 接收成功”“移动端行为生效”四个结果,禁止合并为一个“发布成功”。
### 1.3 优先级与结果
| 标识 | 说明 |
|---|---|
| P0 | 核心链路或安全控制,失败时禁止上线 |
| P1 | 主要页面功能,失败时原则上阻断上线 |
| P2 | 辅助功能、兼容性或易用性问题 |
| 通过 | 实际结果与预期完全一致,接口、数据库及异步结果无异常 |
| 失败 | 页面、接口、数据库或 IM 最终行为任一不符合预期 |
| 阻塞 | 环境、账号、数据或依赖不可用,无法执行 |
## 2. 测试准备
### 2.1 账号、权限与数据
准备以下数据,所有测试数据使用唯一前缀 `QA_YYYYMMDD_`:
| 数据 | 用途 |
|---|---|
| 超级管理员 A | 验证全部菜单与操作权限 |
| 只读管理员 B | 仅授予列表、详情权限,验证写按钮隐藏和接口拒绝 |
| 租户管理员 C | 验证租户隔离,不得查看或操作其他租户数据 |
| 移动端用户 U1、U2、U3 | U1/U2 为普通用户,U3 用作好友或宵禁白名单 |
| 群组 G1 | U1、U2、U3 入群,用于群消息及群成员入口测试 |
| 消息样本 | 文本、图片、视频、语音、文件、名片、系统消息各至少 1 条 |
| 登录样本 | 密码成功、密码错误、账号不存在、账号禁用、退出各至少 1 条 |
执行前确认:
- Admin Server 的 IM Internal API 地址与共享密钥配置正确。
- Admin Server 和 IM Server 的 Kafka broker、topic 和鉴权配置一致。
- Admin 库已存在策略、敏感词、策略分组及消息命令相关表。
- IM 库已存在 `im_user_login_logs`、`im_login_failure_logs`、`im_message_admin_actions`。
- 浏览器清理旧缓存后重新登录,确认当前构建版本与待测提交一致。
### 2.2 通用检查
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| COM-001 | P0 | 使用超级管理员登录并展开 IM 管理菜单 | 已交付的 8 个功能域可见,页面可打开,无 404、白屏和控制台致命错误 |
| COM-002 | P0 | 使用只读管理员打开各页面 | 可查询和查看;新增、编辑、删除、发布、隐藏、封禁等无权限按钮不可见或禁用;直接请求写接口返回无权限 |
| COM-003 | P0 | 使用租户管理员查询列表、详情和导出 | 后端强制按当前租户过滤,修改 `tenantId` 不能越权读取或操作其他租户数据 |
| COM-004 | P1 | 对列表执行查询、重置、翻页、切换每页条数 | 查询条件生效;重置恢复默认值;总数和分页正确;每页超过 200 时接口明确报错 |
| COM-005 | P1 | 查询无匹配数据、模拟 IM 超时或不可用 | 空数据展示为空列表而非报错;依赖异常有明确提示,不把历史缓存伪装成实时成功 |
| COM-006 | P1 | 连续快速点击保存、发布、删除等按钮 | 提交期间按钮有加载/防重复状态,不产生重复记录或重复处置 |
| COM-007 | P1 | 检查新增写操作后的操作日志 | 操作者、租户、目标对象、动作、时间、结果和原因可追溯,不记录密码、验证码、Token 或完整敏感账号 |
## 3. 页面测试用例
### 3.1 数据概览
当前按钮和字段:`刷新`,时间范围 `近7天/近14天/近30天`;指标包含总用户数、活跃用户、总群组数、总会话数、总消息数、今日消息;趋势表包含日期、新增用户、新增消息。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| DASH-001 | P0 | 打开数据概览 | 调用 `/imDashboard/overview` 和 `/imDashboard/trends`;六项指标展示真实数字,不显示 `undefined`、`NaN` 或静态占位值 |
| DASH-002 | P0 | 记录 IM 库统计结果后刷新页面 | 总用户、活跃用户、群组、会话、消息及今日消息与 IM Server 同口径统计一致 |
| DASH-003 | P1 | 分别选择近 7、14、30 天 | 趋势行数和日期区间正确,日期连续;切换后重新请求,新增用户和新增消息按日聚合 |
| DASH-004 | P1 | 在 IM 端新增用户并发送一条消息,再点刷新 | 对应总量和当日趋势增加;刷新按钮显示加载状态且不会重复并发请求 |
| DASH-005 | P1 | 检查跨天边界和无数据日期 | 按 `Asia/Shanghai` 自然日统计;无数据日期补 0,不遗漏日期 |
| DASH-006 | P0 | 使用租户管理员查看概览 | 只统计本租户数据;不能通过修改请求参数获得全局或其他租户数据 |
### 3.2 用户管理
本页属于既有能力回归。重点回归查询、详情、状态修改、重置密码、IP 白名单、强制下线、注销、批量封禁、按 IP 封禁和导入。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| USER-001 | P1 | 按用户 ID、用户名、昵称、手机号、状态和时间查询 | 条件可组合,表格字段与详情一致,手机号等敏感字段按权限脱敏 |
| USER-002 | P1 | 新增或编辑用户,切换启用/禁用 | 校验必填项和唯一性;状态写入成功,禁用用户不能继续登录 |
| USER-003 | P1 | 执行重置密码、强制下线、注销 | 均需二次确认;成功后客户端会话失效;操作日志记录动作但不记录明文密码 |
| USER-004 | P1 | 执行批量封禁及按 IP 封禁 | 仅命中目标租户和目标用户;返回成功、跳过、失败数量,重复提交保持幂等 |
### 3.3 用户分组
当前页面为“用户策略分组”,按钮包括新增/编辑、成员、启用/禁用、删除;成员弹窗支持添加成员、移除成员。表格重点字段:分组编码、分组名称、优先级、状态、版本、成员数、备注、更新时间。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| UPG-001 | P1 | 按租户、编码/名称关键字、状态查询并重置 | 条件生效;状态 `1=启用、2=禁用` 显示字典标签;成员数与成员关系表一致 |
| UPG-002 | P1 | 新增分组,填写合法编码、名称、优先级、策略 JSON、IP 白/黑名单 | 保存成功并生成版本;编码以字母开头且仅含字母、数字、下划线、中划线,长度 2-64 |
| UPG-003 | P1 | 输入数字开头、空名称、非法 JSON、超过 128 字名称 | 前后端拒绝保存并给出明确中文错误,数据库不产生脏数据 |
| UPG-004 | P1 | 编辑分组策略后保存 | 原记录更新,版本按实现规则变化;详情与列表一致;其他租户同编码记录不受影响 |
| UPG-005 | P1 | 点击状态开关切换启用/禁用 | 调用 `/imUserPolicyGroup/status`;页面状态即时刷新,非法状态值被后端拒绝 |
| UPG-006 | P1 | 打开成员列表,添加 U1,设置有效开始/结束时间 | 成员成功加入,成员数 +1;结束时间早于开始时间时拒绝保存 |
| UPG-007 | P1 | 重复添加同一用户,然后移除成员 | 不产生重复有效关系;移除后成员列表和成员数同步更新 |
| UPG-008 | P1 | 删除含成员的分组 | 显示二次确认;按业务约束拒绝删除或级联解除关系,不能遗留指向不存在分组的成员关系 |
### 3.4 群组管理
本页属于既有能力回归,验证查询、详情、封禁/解封、解散、成员、文件和快速建群。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| GROUP-001 | P1 | 按群 ID、群号、群名、群主、状态查询 | 列表与 IM 群数据一致,分页及租户隔离正确 |
| GROUP-002 | P1 | 封禁 G1 后尝试群内发消息,再解封 | 封禁后发送被 IM Server 拒绝;解封后恢复;后台和客户端状态一致 |
| GROUP-003 | P1 | 查看成员、文件并删除一个群文件 | 只展示 G1 数据;删除需确认且客户端不可再访问已删除文件 |
| GROUP-004 | P1 | 快速创建群组并解散 | 成员、群主校验正确;解散后群不可继续使用,操作有审计记录 |
### 3.5 群成员管理
本页属于既有能力回归,重点验证踢出、禁言、解除禁言,以及全局禁加好友时的群成员隐私联动。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| GMEM-001 | P1 | 查询 G1 成员并按用户筛选 | 角色、入群时间、禁言状态正确,只返回该群成员 |
| GMEM-002 | P1 | 对 U2 执行禁言、解除禁言、踢出 | 对应客户端行为立即生效;群主/管理员等受保护角色遵守业务限制 |
| GMEM-003 | P0 | 发布“全局禁止加好友”策略后,U1 在群成员列表点击 U2 | 不显示可识别的个人详情或个人名片,不显示加好友入口;抓包直接访问资料/加好友接口也被后端拒绝 |
### 3.6 邀请码管理
本页属于既有能力回归。邀请码创建、删除、使用记录必须与全局好友开关联动。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| INV-001 | P1 | 创建邀请码并设置有效期、次数、状态 | 校验正确,创建成功后可查询,过期或耗尽后不可继续使用 |
| INV-002 | P1 | 查看使用记录并删除未使用邀请码 | 使用人、时间、状态正确;删除需确认,已删除邀请码失效 |
| INV-003 | P0 | 全局禁止加好友期间通过邀请码注册或自动加好友 | 注册能力按私域设置执行,但 `invite_auto_add` 入口必须被后端阻断,不能自动建立好友关系 |
### 3.7 单聊消息
筛选项:租户、会话 Topic、发送用户 ID、接收用户 ID、内容类型、消息状态、内容关键词、发送时间。表格字段:Topic、消息序号、发送人、接收人、类型、内容摘要、状态、发送时间;按钮:详情、隐藏、删除、导出。
内容类型枚举:`text` 文本、`image` 图片、`video` 视频、`audio/voice` 语音、`file` 文件、`card` 名片、`system` 系统。状态枚举:`normal` 正常、`deleted` 后台删除、`recalled` 用户撤回、`blocked` 风控拦截、`hidden` 已隐藏。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| PM-001 | P0 | 打开页面并点击查询 | `/imPrivateMessage/list` 返回真实 IM 消息;单聊双方、Topic、Seq、类型、摘要、状态和时间与 IM 库一致 |
| PM-002 | P1 | 组合发送人、接收人、类型、状态、关键词和时间查询 | 所有条件正确下传并组合生效;非法类型、非法状态、每页大于 200 被后端拒绝 |
| PM-003 | P1 | 点击详情 | `/imPrivateMessage/view` 返回消息头、完整内容、元数据、回复序号、@用户等;空 Topic 或 `seqId<=0` 被拒绝 |
| PM-004 | P0 | 点击隐藏,输入处置原因并确认 | 原因必填且不超过 500 字;Admin 生成唯一 command/event,返回待处理状态,Kafka 发布 `message.hide` |
| PM-005 | P0 | 等待隐藏命令完成后刷新并在移动端查看 | Admin 命令状态变为成功;IM `im_message_admin_actions` 有一条成功记录;列表状态为 `hidden`,普通客户端不再展示该消息 |
| PM-006 | P0 | 点击删除,确认后等待执行 | Kafka 发布 `message.delete`;IM 幂等落库并清除相关缓存;列表状态为 `deleted`,详情/客户端不能再获得可展示原文 |
| PM-007 | P0 | 使用同一 command/event 重放隐藏或删除命令 | 不重复执行、不新增重复有效记录,最终状态保持一致,并可追踪重复消费结果 |
| PM-008 | P1 | Kafka 不可用时执行隐藏/删除 | 页面明确提示失败或命令进入失败/待重试状态,不显示为已成功;恢复后可按设计重试 |
| PM-009 | P1 | 按当前筛选条件导出 | 导出字段含 Topic、Seq、双方 ID、类型、摘要、状态、发送时间;数据范围与筛选及租户权限一致 |
### 3.8 群聊消息
筛选项:租户、群 Topic、发送用户 ID、内容类型、消息状态、关键词、发送时间。表格除公共消息字段外还应展示群名称、群号;按钮与单聊一致。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| GM-001 | P0 | 按 G1 Topic 查询 | `/imGroupMessage/list` 仅返回群消息,群名称、群号、发送人、Seq、类型、摘要和状态正确 |
| GM-002 | P1 | 按发送人、类型、状态、关键词、时间组合查询并查看详情 | 条件准确,详情完整;不能用单聊 Topic 越界取得群消息 |
| GM-003 | P0 | 隐藏一条群消息并在多个群成员客户端刷新 | `message.hide` 执行成功;所有普通成员均不可见,后台状态和 IM 动作记录一致 |
| GM-004 | P0 | 删除一条群消息 | `message.delete` 执行成功;群消息缓存被清除,各客户端不能继续通过历史缓存看到原内容 |
| GM-005 | P1 | 导出群消息 | 导出包含群名、Topic、Seq、发送人、类型、摘要、状态、时间,范围与查询一致 |
| GM-006 | P0 | 重放相同命令、乱序投递隐藏与删除命令 | IM Server 幂等处理;删除是最终不可见状态,不因迟到的隐藏命令恢复内容 |
### 3.9 敏感词库
按钮:新增、编辑、启用/禁用、删除、批量导入、分组管理。筛选字段:租户、分组、敏感词、匹配类型、命中动作、风险等级、状态。
匹配枚举:`exact` 精确、`contains` 包含、`regex` 正则、`pinyin` 拼音。动作枚举:`replace` 替换、`block` 拦截、`audit_only` 仅审计、`manual_review` 人工审核。风险等级:`low/medium/high`。场景:单聊、群聊、用户资料、群资料、反馈、举报。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| SW-001 | P1 | 新建敏感词分组 | 编码格式、名称 1-128 字和状态校验正确;同租户编码保持唯一 |
| SW-002 | P1 | 新增敏感词并逐项选择匹配方式、动作、风险、场景 | 保存后列表字典显示正确;敏感词长度 1-255;至少选择一个生效场景 |
| SW-003 | P1 | 动作选择“替换”但不填替换文本 | 前后端均拒绝并提示“替换动作必须填写替换文本” |
| SW-004 | P1 | 输入非法匹配类型、动作、风险、状态或无效正则 | 后端拒绝;无效正则不得发布到 IM 运行时 |
| SW-005 | P1 | 切换启用/禁用并发送命中消息 | 禁用后不执行该词规则;启用后按所选场景和动作处理,后台状态与 IM 行为一致 |
| SW-006 | P1 | 批量导入含空行、重复词和已存在词的数据 | 单次仅允许 1-5000 条;返回 `inserted` 和 `skipped`,重复数据不产生多条记录 |
| SW-007 | P1 | 删除敏感词后再次发送命中内容 | 删除需确认;运行时规则及时失效,且操作有审计记录 |
### 3.10 文件管理
本页属于既有能力回归。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| FILE-001 | P1 | 按上传人、文件类型、大小、来源、审核状态、时间查询 | 表格元数据、下载地址、引用对象与存储记录一致,租户隔离正确 |
| FILE-002 | P1 | 预览、下载、删除文件 | 权限校验正确;删除需确认,引用中的已删除文件不能继续匿名访问 |
| FILE-003 | P1 | 执行审核通过/拒绝及批量处置 | 状态流转正确,拒绝原因必填,相关消息或群文件同步更新 |
### 3.11 用户反馈
本页属于既有能力回归。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| FB-001 | P1 | 按反馈类型、状态、优先级、用户和时间查询 | 查询、分页、详情及附件展示正确 |
| FB-002 | P1 | 修改处理状态、优先级和回复内容 | 状态流转符合约束,处理人和处理时间自动记录 |
| FB-003 | P2 | 批量处理并导出 | 成功/失败数量准确,导出范围与当前筛选一致 |
### 3.12 操作日志
本页属于既有能力回归,也是所有后台写操作的审计入口。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| OPLOG-001 | P0 | 完成策略发布、消息删除、用户封禁后查询日志 | 三类操作均可按模块、操作者、目标和时间检索,结果、原因、请求 ID 完整 |
| OPLOG-002 | P0 | 检查请求参数与响应摘要 | 密码、验证码、Token、共享密钥、完整手机号/邮箱等不以明文出现 |
| OPLOG-003 | P1 | 只读管理员尝试越权写接口 | 记录拒绝结果及必要上下文,不记录敏感正文,不改变业务数据 |
### 3.13 用户账号审计
本页属于既有能力回归。动作至少覆盖修改资料、修改密码、注销、管理员改用户名/状态、重置密码、更新安全权限、强制下线、强制注销、调整策略分组、批量封禁、按 IP 封禁。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| UAUD-001 | P1 | 分别触发用户自助和管理员账号变更 | 动作枚举显示中文,操作者类型、目标用户、变更前后摘要和时间正确 |
| UAUD-002 | P0 | 执行重置密码并查看审计详情 | 仅记录“已重置”等安全摘要,绝不保存新旧明文密码 |
| UAUD-003 | P1 | 调整 U1 策略分组 | 产生 `assign_policy_group` 审计,旧分组、新分组及管理员可追溯 |
### 3.14 群事件审计
本页属于既有能力回归。事件包括建群、加入/退出/踢出、禁言/解禁、转让群主、增删管理员、改群名、封禁/解封和解散。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| GAUD-001 | P1 | 在 G1 触发踢人、禁言、改群名 | 对应事件各产生一条审计,操作人、目标人、群、时间和原因正确 |
| GAUD-002 | P1 | 系统执行群封禁或解散 | 操作人显示系统或实际管理员,事件类型与最终群状态一致 |
### 3.15 管理员登录历史
本页属于 HotGo 既有能力回归。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| ALOGIN-001 | P1 | 管理员成功登录、失败登录、退出后查询 | 账号、IP、浏览器、系统、结果和时间正确;失败原因不泄露密码 |
| ALOGIN-002 | P1 | 使用日期、账号、IP、状态筛选并导出 | 列表和导出范围一致,普通管理员不能查看超出权限的数据 |
### 3.16 用户登录历史
筛选项:租户、用户 ID、脱敏账号、登录方式、事件类型、客户端平台、IP、登录时间。表格字段:ID、用户、脱敏账号、登录方式、事件、客户端、版本、IP、地区、风险等级、登录时间、退出时间。
登录方式:`password` 密码、`sms_code` 短信验证码、`sso` 单点登录、`qr_code` 二维码、`token_refresh` 令牌刷新。事件:`login` 登录、`refresh` 刷新会话、`logout` 主动退出、`kicked` 被踢下线、`session_expired` 会话过期。平台:iOS、Android、Web、桌面端、未知。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| ULOGIN-001 | P0 | U1 密码登录成功后在后台查询 | IM `im_user_login_logs` 新增成功记录;后台展示用户、脱敏账号、会话、设备、平台、IP、请求 ID 和登录时间 |
| ULOGIN-002 | P0 | U1 主动退出后刷新列表 | 对应会话 `logout_at` 被更新或产生退出事件;退出时间不早于登录时间 |
| ULOGIN-003 | P1 | 分别模拟刷新会话、被踢、会话过期 | 事件字典和发生时间正确,同一 session 可串联追踪 |
| ULOGIN-004 | P1 | 按全部筛选项组合查询 | Admin 参数正确透传到 IM Internal API;结果按登录时间倒序,分页不重复不漏项 |
| ULOGIN-005 | P0 | 检查列表、详情、Admin/IM 日志及数据库 | 只保存账号掩码和不可逆哈希;密码、短信验证码、Token、Cookie 不得出现 |
| ULOGIN-006 | P1 | 导出当前筛选结果 | 含 ID、用户、脱敏账号、事件、平台、IP、地区、风险、原因、时间;租户隔离和脱敏规则不因导出而放宽 |
### 3.17 IP 账号统计
本页属于后续改造/回归能力。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| IPSTAT-001 | P1 | 按 IP、账号数、风险和时间查询 | 同一 IP 关联账号数、成功/失败次数和最近时间统计准确 |
| IPSTAT-002 | P1 | 查看关联用户并按 IP 封禁 | 关联列表受租户隔离;封禁需确认并记录命中用户数、成功数和失败数 |
### 3.18 登录失败日志
筛选项:租户、用户 ID、脱敏账号、失败类型、平台、IP、尝试时间。表格字段:ID、用户、脱敏账号、失败类型、原因、IP、地区、客户端、版本、风险等级、风控动作、尝试时间;按钮:详情、加入 IP 黑名单、封禁用户、导出。
失败类型:`password_error` 密码错误、`code_error` 验证码错误、`account_not_found` 账号不存在、`account_disabled` 账号已禁用、`rate_limited` 访问频率受限、`risk_blocked` 风控拦截。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| LFAIL-001 | P0 | 使用错误密码登录 U1 | IM `im_login_failure_logs` 新增 `password_error`;后台展示脱敏账号、IP、设备、平台、风险及尝试时间 |
| LFAIL-002 | P0 | 分别触发验证码错误、账号不存在、账号禁用、限频和风控拦截 | 失败类型、原因码、详情、风险动作正确;未知账号场景不泄露账号是否真实存在 |
| LFAIL-003 | P1 | 点击详情 | 根据日志 ID 返回完整安全上下文;`id<=0` 或跨租户 ID 被拒绝 |
| LFAIL-004 | P0 | 点击“加入 IP 黑名单”并确认 | IP 必填;在安全策略 IP 黑名单生成规则,不重复插入相同有效规则,后续登录按场景被阻断 |
| LFAIL-005 | P0 | 对有关联用户的记录点击“封禁用户”,填写原因 | 用户 ID 和原因必填;用户状态变为禁用,已有会话按设计失效,账号审计和操作日志可追溯 |
| LFAIL-006 | P1 | 对账号不存在、无关联用户的记录点击封禁 | 按钮不可用或接口明确提示无法关联用户,不得误封其他用户 |
| LFAIL-007 | P1 | 导出失败日志 | 结果与筛选一致,账号继续脱敏,不包含提交的密码、验证码和完整鉴权数据 |
### 3.19 私域设置
本页属于既有/后续能力回归,重点验证注册与验证码开关不绕过安全策略。
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| TENANT-001 | P1 | 修改短信验证码、邀请码注册等开关并保存 | 保存后重新进入值保持一致,移动端登录/注册入口按租户配置变化 |
| TENANT-002 | P0 | 关闭短信验证码并尝试短信登录、忘记密码、验证码注册 | 后端拒绝被关闭的流程,不能仅靠隐藏移动端按钮 |
| TENANT-003 | P0 | 全局禁加好友期间通过注册流程触发自动加好友 | 注册成功与否按私域设置判断,但任何自动加好友均被安全策略统一拒绝 |
### 3.20 安全策略
页面包含“策略版本”和“IP 黑白名单”两个页签。策略按钮:新建草稿、编辑/查看、发布、恢复默认;策略内含好友控制、消息宵禁和其他 JSON 策略。IP 规则按钮:新增、编辑、删除。
发布状态:`draft` 草稿、`published` 已发布、`archived` 已归档、`failed` 发布失败。IP 规则类型:白名单/黑名单;动作:`allow` 放行、`challenge` 二次验证、`block` 阻断、`audit_only` 仅审计;场景:登录、注册、发消息、上传文件;风险:低、中、高。
#### 3.20.1 策略版本与发布
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| SEC-001 | P0 | 点击新建草稿,填写策略名称并保存 | `/imSecurityPolicy/save` 创建 `draft`;只有草稿可编辑,已发布版本只读 |
| SEC-002 | P0 | 修改草稿但不发布,在移动端验证旧策略 | 草稿不影响 IM 运行时,移动端继续执行最后一个已发布版本 |
| SEC-003 | P0 | 点击发布并确认 | 发布成功后当前版本为 `published`,原已发布版本归档;IM Server 收到策略更新并按新版本执行 |
| SEC-004 | P0 | 模拟 Kafka/IM 策略应用失败 | 不得提示完全成功;版本进入失败或可重试状态,运行时继续使用上一个有效版本 |
| SEC-005 | P1 | 点击恢复默认 | 仅创建一个默认草稿,不直接发布、不改变当前生效策略 |
| SEC-006 | P1 | 输入非法 JSON、空策略名或超过 500 字发布原因 | 前后端拒绝提交并明确指出错误字段 |
#### 3.20.2 全局禁止加好友
关闭“允许加好友”时,后端必须强制设置:隐藏个人信息、禁止二维码、禁止群成员入口,并封锁 `search`、`profile_card`、`group_member`、`qrcode`、`invite_auto_add`、`recommendation` 六类入口。该限制必须由后端执行,移动端隐藏按钮不能作为唯一控制。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| FRIEND-001 | P0 | 关闭“允许加好友”并观察表单 | 隐藏资料、禁止二维码、禁止群成员入口自动开启且不可取消;六类入口全部选中 |
| FRIEND-002 | P0 | 仅保存草稿,不发布;U1 从用户搜索添加 U2 | 仍按旧策略执行,证明草稿不直接生效 |
| FRIEND-003 | P0 | 发布禁加好友策略;U1 从用户搜索添加 U2 | 页面不展示添加入口或提示“当前时段暂不允许添加好友”;直接调用加好友接口同样被拒绝 |
| FRIEND-004 | P0 | U1 打开 U2 个人名片并尝试添加 | 个人资料按策略隐藏,不展示可用个人名片及加好友入口;直接请求资料/好友接口不可绕过 |
| FRIEND-005 | P0 | U1 从 G1 群成员列表点击 U2 | 不显示 U2 可识别的个人信息、个人名片和加好友入口 |
| FRIEND-006 | P0 | U1 扫描 U2 二维码并构造二维码加好友请求 | 二维码入口不可用,后端拒绝建立好友关系 |
| FRIEND-007 | P0 | 使用邀请码注册并触发自动添加,再查看推荐联系人 | `invite_auto_add` 和 `recommendation` 均被阻断,不产生好友关系 |
| FRIEND-008 | P0 | 修改客户端请求绕过 UI、伪造入口类型或不传入口类型 | 后端按全局开关统一拒绝,不能因缺失/伪造入口字段放行 |
| FRIEND-009 | P0 | 将 U3 配置为例外用户(若本版本支持)并重复六入口测试 | 仅 U3 按明确白名单规则放行,U1/U2 仍被阻断;未实现例外时页面和接口不得暗示支持 |
| FRIEND-010 | P0 | 重新开启并发布,再从六入口验证 | 限制解除,资料展示及加好友恢复为常规权限;已被拒绝的历史请求不会自动补建好友关系 |
#### 3.20.3 消息宵禁
规则字段:规则编码、名称、日期类型、作用范围、开始/结束时间、时区、状态、提示语、白名单用户 ID;支持每天、工作日、周末、自定义日期,范围为单聊/私聊或群聊。默认时区为 `Asia/Shanghai`,支持跨午夜时间段。
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| CURFEW-001 | P0 | 开启宵禁但不添加规则并保存 | 后端拒绝;开启时至少存在一条规则,且至少一条启用的单聊/私聊规则 |
| CURFEW-002 | P0 | 新增工作日 18:00-09:00、范围单聊、状态启用的规则并发布 | 保存和发布成功,规则编码合法且唯一,IM Server 使用 `Asia/Shanghai` 解释跨午夜区间 |
| CURFEW-003 | P0 | 命中宵禁时段,U1 向 U2 发送文本、图片、语音、文件和名片 | 所有单聊/私聊消息均由后端拒绝,不写入可投递消息;客户端显示配置的宵禁提示语 |
| CURFEW-004 | P0 | 在开始时刻前 1 秒、开始时刻、结束时刻前 1 秒、结束时刻发送 | 边界按统一规则执行;开始时刻进入限制,结束时刻解除,Admin 与 IM 时间口径一致 |
| CURFEW-005 | P0 | 分别验证每天、工作日、周末、自定义日期 | 仅命中日期生效;自定义日期为空或格式非法时拒绝保存 |
| CURFEW-006 | P0 | 将 U3 加入白名单,在命中时段由 U3 与 U1 发消息 | 白名单方向规则符合产品定义且保持一致;非白名单 U1/U2 仍被拒绝 |
| CURFEW-007 | P0 | 增加群聊范围规则并发布,在 G1 发消息 | 命中时群消息被后端拒绝;只有单聊规则时群聊不受影响 |
| CURFEW-008 | P0 | 禁用规则或关闭宵禁并发布 | 消息恢复发送;旧策略缓存及时失效,不需重启 IM Server |
| CURFEW-009 | P0 | 仅保存新的时间规则但不发布 | IM Server 继续执行最后已发布规则,不能提前生效 |
| CURFEW-010 | P0 | 客户端修改本地时间、时区或直接调用发消息接口 | 后端以策略时区和服务器可信时间判定,无法绕过宵禁 |
#### 3.20.4 IP 黑白名单
| 用例 ID | 优先级 | 操作步骤 | 预期结果及核验点 |
|---|---|---|---|
| IPRULE-001 | P1 | 新增黑名单规则,填写单 IP、阻断、登录场景、高风险 | 保存成功,列表字段及字典显示正确;登录请求从该 IP 发起时被阻断 |
| IPRULE-002 | P1 | 新增 CIDR 白名单并选择多个场景 | CIDR 校验正确,命中范围内 IP 按白名单优先级执行,其他场景不受影响 |
| IPRULE-003 | P1 | 输入非法 IP/CIDR、空场景、非法动作/风险/状态 | 后端拒绝且不产生记录 |
| IPRULE-004 | P1 | 编辑规则后重新触发对应场景 | 新规则生效,旧匹配缓存失效;详情和列表一致 |
| IPRULE-005 | P1 | 删除规则并确认 | 记录删除且运行时规则失效;取消确认不产生变化 |
| IPRULE-006 | P0 | 从登录失败日志加入同一 IP 黑名单 | 安全策略页可查询到规则;重复操作不产生冲突规则,审计来源可追踪 |
## 4. Admin Server 与 IM Server 联调核验
### 4.1 接口映射
| 后台操作 | Admin Server | IM Server Internal / Kafka |
|---|---|---|
| 概览指标 | `GET /imDashboard/overview` | `GET /internal/admin-query/tenants/{tenant}/dashboard/overview` |
| 趋势数据 | `GET /imDashboard/trends` | `GET /internal/admin-query/tenants/{tenant}/dashboard/trends` |
| 单聊列表 | `GET /imPrivateMessage/list` | `GET /internal/admin-query/tenants/{tenant}/private-messages` |
| 群聊列表 | `GET /imGroupMessage/list` | `GET /internal/admin-query/tenants/{tenant}/group-messages` |
| 消息详情 | `GET /imPrivateMessage/view` 或 `/imGroupMessage/view` | `GET /internal/admin-query/tenants/{tenant}/messages/{topic}/{seq}` |
| 隐藏/删除消息 | `POST .../hide`、`POST .../delete` | Kafka `message.hide`、`message.delete` |
| 用户登录历史 | `GET /imUserLoginHistory/list` | `GET /internal/admin-query/tenants/{tenant}/user-login-logs` |
| 登录失败列表/详情 | `GET /imLoginFailureLog/list|view` | `GET /internal/admin-query/tenants/{tenant}/login-failure-logs[/{id}]` |
### 4.2 数据库抽查
以下 SQL 仅用于测试环境核验,查询条件必须带测试租户或测试数据标识:
```sql
-- IM 登录成功/退出审计
SELECT id, tenant_id, user_id, account_mask, login_type, event_type,
session_id, platform, ip, risk_level, login_at, logout_at
FROM im_user_login_logs
WHERE tenant_id = ? AND user_id = ?
ORDER BY id DESC LIMIT 20;
-- IM 登录失败审计
SELECT id, tenant_id, user_id, account_mask, failure_type, reason,
ip, platform, risk_level, risk_action, attempt_at
FROM im_login_failure_logs
WHERE tenant_id = ? AND ip = ?
ORDER BY id DESC LIMIT 20;
-- IM 消息后台处置结果
SELECT tenant_id, command_id, event_id, action, topic, seq_id,
status, reason, created_at, updated_at
FROM im_message_admin_actions
WHERE tenant_id = ? AND topic = ? AND seq_id = ?
ORDER BY id DESC;
```
核验要求:同一幂等键不得产生多条有效执行;失败必须保留错误状态和可追踪信息;表中不得出现密码、验证码、Token 或完整认证请求。
## 5. 非功能与异常测试
| 用例 ID | 优先级 | 操作步骤 | 预期结果 |
|---|---|---|---|
| NFT-001 | P1 | 列表准备 10 万级数据并执行常用筛选 | 首屏和翻页在项目约定时间内返回,无全表扫描导致的持续高负载 |
| NFT-002 | P1 | 并发 20 次概览、消息、登录审计查询 | 服务稳定,连接池无耗尽,租户数据不串读 |
| NFT-003 | P0 | 伪造 Internal API 请求、错误共享密钥或重放过期请求 | IM Server 拒绝未授权请求并记录安全日志,不返回业务数据 |
| NFT-004 | P0 | 在 Topic、关键词、原因、备注中输入 SQL/XSS 字符串 | 使用参数化查询;页面转义展示,不执行脚本,不污染导出文件 |
| NFT-005 | P1 | Admin 与 IM 短时网络中断后恢复 | 查询可恢复;异步命令状态可追踪,不丢命令、不重复执行 |
| NFT-006 | P1 | 检查 PC 常用分辨率及窄屏 | 搜索区、表格、弹窗和操作列无重叠;横向滚动可用,按钮文本不被截断为不可辨识状态 |
## 6. 上线验收门槛
满足以下条件方可通过本轮验收:
1. 所有 P0 用例通过,尤其是 `FRIEND-*`、`CURFEW-*`、消息处置、登录审计和租户隔离。
2. P1 无阻断主流程的问题;遗留问题有负责人、修复版本和回归日期。
3. Admin Server、IM Server、Kafka 和两侧数据库结果一致,不以页面提示代替最终状态核验。
4. 全局禁加好友和宵禁均通过“直接调用后端接口”的绕过测试。
5. 数据库、应用日志、操作日志和导出文件均通过敏感信息检查。
6. 其余未纳入本轮改造的页面必须完成基本回归,但不应据此宣称 20 个页面已全部完成重构。
## 7. 执行记录
| 字段 | 内容 |
|---|---|
| Admin Server 版本/提交 | `16cc456`(功能提交:`bb87d01`) |
| IM Server 版本/提交 | `b92154b`(功能提交:`6b20a63`) |
| 前端版本/构建时间 | |
| 测试环境 | `https://admin.118im.com` |
| 测试租户 | |
| 测试人员 | |
| 执行日期 | |
| P0 通过/失败/阻塞 | |
| P1 通过/失败/阻塞 | |
| 缺陷链接 | |
| 最终结论 | 通过 / 有条件通过 / 不通过 |

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