Compare commits
6 Commits
feat/chann
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 93df416954 | |||
| 0e8d3f61d4 | |||
| 0bf48fd449 | |||
| 8c4deb5be4 | |||
| 31d4a34ebb | |||
| 749856c455 |
51
.env.example
51
.env.example
@@ -1,32 +1,59 @@
|
|||||||
# MySQL
|
# ============================================================
|
||||||
|
# Sino-MCI 通用环境配置模板
|
||||||
|
#
|
||||||
|
# 推荐用法:
|
||||||
|
# cp .env.local .env # 本机开发,连接 localhost 基础设施
|
||||||
|
# cp .env.remote .env # 本机开发,连接远程基础设施(如 192.168.1.173)
|
||||||
|
#
|
||||||
|
# 注意:
|
||||||
|
# 1. .env 文件不会被提交到仓库(已在 .gitignore 中忽略)。
|
||||||
|
# 2. scripts/local-up.sh 启动 docker-compose 时,会自动把 host 覆盖为容器名,
|
||||||
|
# 因此无论 .env 中是 localhost 还是远程 IP,docker-compose 内部都能正确互联。
|
||||||
|
# 3. 若手动执行 docker compose,请确保当前 .env 中的 MYSQL_HOST/REDIS_HOST/
|
||||||
|
# RABBITMQ_HOST 指向可访问地址,或使用 local-up.sh 启动。
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# 基础设施端口与账号
|
||||||
|
# --------------------------------------------------
|
||||||
|
MYSQL_PORT=3306
|
||||||
MYSQL_ROOT_PASSWORD=root
|
MYSQL_ROOT_PASSWORD=root
|
||||||
MYSQL_DATABASE=sino_mci
|
MYSQL_DATABASE=sino_mci
|
||||||
MYSQL_USER=root
|
MYSQL_USER=root
|
||||||
MYSQL_PORT=3306
|
|
||||||
|
|
||||||
# Redis
|
|
||||||
REDIS_PORT=6379
|
REDIS_PORT=6379
|
||||||
|
|
||||||
# RabbitMQ
|
|
||||||
RABBITMQ_DEFAULT_USER=guest
|
RABBITMQ_DEFAULT_USER=guest
|
||||||
RABBITMQ_DEFAULT_PASS=guest
|
RABBITMQ_DEFAULT_PASS=guest
|
||||||
RABBITMQ_AMQP_PORT=5672
|
RABBITMQ_AMQP_PORT=5672
|
||||||
RABBITMQ_MGMT_PORT=15672
|
RABBITMQ_MGMT_PORT=15672
|
||||||
|
|
||||||
# SeaweedFS
|
|
||||||
SEAWEEDFS_MASTER_PORT=9333
|
SEAWEEDFS_MASTER_PORT=9333
|
||||||
SEAWEEDFS_S3_PORT=8888
|
SEAWEEDFS_S3_PORT=8888
|
||||||
|
|
||||||
# Application ports
|
# --------------------------------------------------
|
||||||
|
# 应用服务端口
|
||||||
|
# --------------------------------------------------
|
||||||
MCI_SERVER_PORT=8080
|
MCI_SERVER_PORT=8080
|
||||||
ADMIN_WEB_PORT=80
|
ADMIN_WEB_PORT=80
|
||||||
CHANNEL_SERVICE_PORT=8000
|
CHANNEL_SERVICE_PORT=8000
|
||||||
|
|
||||||
# JVM options for mci-server
|
# --------------------------------------------------
|
||||||
JAVA_OPTS=-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0
|
# Spring Boot / JVM
|
||||||
|
# --------------------------------------------------
|
||||||
# Spring profile
|
|
||||||
SPRING_PROFILES_ACTIVE=dev
|
SPRING_PROFILES_ACTIVE=dev
|
||||||
|
JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
|
||||||
|
|
||||||
# OpenAI API key (optional)
|
# --------------------------------------------------
|
||||||
OPENAI_API_KEY=your-openai-api-key-here
|
# Channel Service
|
||||||
|
# --------------------------------------------------
|
||||||
|
MAX_RETRY=5
|
||||||
|
BASE_RETRY_DELAY_SECONDS=5
|
||||||
|
EVENT_RETENTION_DAYS=10
|
||||||
|
EVENT_PUBLISH_INTERVAL_SECONDS=5
|
||||||
|
|
||||||
|
# --------------------------------------------------
|
||||||
|
# OpenAI(可选)
|
||||||
|
# --------------------------------------------------
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
OPENAI_BASE_URL=https://api.openai.com
|
||||||
|
|||||||
50
.env.local
Normal file
50
.env.local
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Sino-MCI 本地开发环境(本机启动后端/前端时加载)
|
||||||
|
# 使用方式:cp .env.local .env
|
||||||
|
# 说明:应用服务连接本机 localhost 上的基础设施
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 基础设施连接地址
|
||||||
|
MYSQL_HOST=localhost
|
||||||
|
REDIS_HOST=localhost
|
||||||
|
RABBITMQ_HOST=localhost
|
||||||
|
SEAWEEDFS_HOST=localhost
|
||||||
|
|
||||||
|
# 基础设施端口与账号
|
||||||
|
MYSQL_PORT=3306
|
||||||
|
MYSQL_ROOT_PASSWORD=root
|
||||||
|
MYSQL_DATABASE=sino_mci
|
||||||
|
MYSQL_USER=root
|
||||||
|
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
RABBITMQ_DEFAULT_USER=guest
|
||||||
|
RABBITMQ_DEFAULT_PASS=guest
|
||||||
|
RABBITMQ_AMQP_PORT=5672
|
||||||
|
RABBITMQ_MGMT_PORT=15672
|
||||||
|
|
||||||
|
SEAWEEDFS_MASTER_PORT=9333
|
||||||
|
SEAWEEDFS_S3_PORT=8888
|
||||||
|
|
||||||
|
# 应用服务端口
|
||||||
|
MCI_SERVER_PORT=8080
|
||||||
|
ADMIN_WEB_PORT=80
|
||||||
|
CHANNEL_SERVICE_PORT=8000
|
||||||
|
|
||||||
|
# 应用服务地址
|
||||||
|
MCI_SERVER_URL=http://localhost:8080/msg-platform
|
||||||
|
VITE_API_BASE_URL=http://localhost:8080
|
||||||
|
|
||||||
|
# Spring Boot / JVM
|
||||||
|
SPRING_PROFILES_ACTIVE=dev
|
||||||
|
JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
|
||||||
|
|
||||||
|
# Channel Service
|
||||||
|
MAX_RETRY=5
|
||||||
|
BASE_RETRY_DELAY_SECONDS=5
|
||||||
|
EVENT_RETENTION_DAYS=10
|
||||||
|
EVENT_PUBLISH_INTERVAL_SECONDS=5
|
||||||
|
|
||||||
|
# OpenAI(可选)
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
OPENAI_BASE_URL=https://api.openai.com
|
||||||
50
.env.remote
Normal file
50
.env.remote
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Sino-MCI 远程基础设施环境(本机启动后端/前端时加载)
|
||||||
|
# 使用方式:cp .env.remote .env
|
||||||
|
# 说明:只启动本机应用服务,连接远程 MySQL/Redis/RabbitMQ/SeaweedFS
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
# 基础设施连接地址
|
||||||
|
MYSQL_HOST=192.168.1.173
|
||||||
|
REDIS_HOST=192.168.1.173
|
||||||
|
RABBITMQ_HOST=192.168.1.173
|
||||||
|
SEAWEEDFS_HOST=192.168.1.173
|
||||||
|
|
||||||
|
# 基础设施端口与账号
|
||||||
|
MYSQL_PORT=3306
|
||||||
|
MYSQL_ROOT_PASSWORD=root
|
||||||
|
MYSQL_DATABASE=sino_mci
|
||||||
|
MYSQL_USER=root
|
||||||
|
|
||||||
|
REDIS_PORT=6379
|
||||||
|
|
||||||
|
RABBITMQ_DEFAULT_USER=guest
|
||||||
|
RABBITMQ_DEFAULT_PASS=guest
|
||||||
|
RABBITMQ_AMQP_PORT=5672
|
||||||
|
RABBITMQ_MGMT_PORT=15672
|
||||||
|
|
||||||
|
SEAWEEDFS_MASTER_PORT=9333
|
||||||
|
SEAWEEDFS_S3_PORT=8888
|
||||||
|
|
||||||
|
# 应用服务端口
|
||||||
|
MCI_SERVER_PORT=8080
|
||||||
|
ADMIN_WEB_PORT=80
|
||||||
|
CHANNEL_SERVICE_PORT=8000
|
||||||
|
|
||||||
|
# 应用服务地址
|
||||||
|
MCI_SERVER_URL=http://192.168.1.173:8080/msg-platform
|
||||||
|
VITE_API_BASE_URL=http://192.168.1.173:8080
|
||||||
|
|
||||||
|
# Spring Boot / JVM
|
||||||
|
SPRING_PROFILES_ACTIVE=dev
|
||||||
|
JAVA_OPTS="-XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0"
|
||||||
|
|
||||||
|
# Channel Service
|
||||||
|
MAX_RETRY=5
|
||||||
|
BASE_RETRY_DELAY_SECONDS=5
|
||||||
|
EVENT_RETENTION_DAYS=10
|
||||||
|
EVENT_PUBLISH_INTERVAL_SECONDS=5
|
||||||
|
|
||||||
|
# OpenAI(可选)
|
||||||
|
OPENAI_API_KEY=
|
||||||
|
OPENAI_BASE_URL=https://api.openai.com
|
||||||
108
AGENTS.md
108
AGENTS.md
@@ -88,7 +88,109 @@ docker exec -it mci-redis redis-cli
|
|||||||
docker compose -p sino-mci --profile app build mci-server
|
docker compose -p sino-mci --profile app build mci-server
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5. 环境变量
|
## 5. 环境变量与快速切换环境
|
||||||
|
|
||||||
- 复制 `.env.example` 为 `.env` 后再修改本地配置。
|
项目使用 `.env` 统一管理 MySQL、Redis、RabbitMQ、SeaweedFS 以及应用服务的连接地址。
|
||||||
- 默认 root 密码为 `root`,仅本地开发使用。
|
|
||||||
|
### 5.1 快速切换本地/远程基础设施
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /Users/marsal/Projects/sino-project/sino-mci
|
||||||
|
|
||||||
|
# 方案 A:连接本机 localhost 上的基础设施(默认)
|
||||||
|
cp .env.local .env
|
||||||
|
|
||||||
|
# 方案 B:连接远程基础设施(如 192.168.1.173)
|
||||||
|
cp .env.remote .env
|
||||||
|
```
|
||||||
|
|
||||||
|
`.env` 文件不会被提交(已加入 `.gitignore`),可以按需要随时切换。
|
||||||
|
|
||||||
|
### 5.2 本地启动后端/前端/Channel Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 后端
|
||||||
|
./scripts/run-local.sh backend
|
||||||
|
|
||||||
|
# 前端
|
||||||
|
./scripts/run-local.sh frontend
|
||||||
|
|
||||||
|
# Channel Service
|
||||||
|
./scripts/run-local.sh channel
|
||||||
|
```
|
||||||
|
|
||||||
|
`run-local.sh` 会自动加载项目根目录的 `.env`,并把 `MYSQL_HOST`、`REDIS_HOST`、`RABBITMQ_HOST`、`MCI_SERVER_URL`、`VITE_API_BASE_URL` 等变量注入到对应进程中。
|
||||||
|
|
||||||
|
### 5.3 Docker Compose 启动(不受 .env 中 host 影响)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 只启动基础设施
|
||||||
|
./scripts/local-up.sh
|
||||||
|
|
||||||
|
# 启动基础设施 + 应用服务
|
||||||
|
./scripts/local-up.sh --with-apps
|
||||||
|
```
|
||||||
|
|
||||||
|
`local-up.sh` 会自动把 host 覆盖为 Docker 容器名(`mci-mysql`、`mci-redis`、`mci-rabbitmq`、`mci-server`),因此无论 `.env` 中是 `localhost` 还是远程 IP,容器内部都能正确互联。
|
||||||
|
|
||||||
|
### 5.4 手动执行 docker compose 的注意事项
|
||||||
|
|
||||||
|
若直接运行 `docker compose` 命令,容器会使用当前 `.env` 中的 `MYSQL_HOST` 等变量。请确保 `.env` 中的 host 对容器可访问,或改用 `local-up.sh` 启动。
|
||||||
|
|
||||||
|
### 5.5 默认账号
|
||||||
|
|
||||||
|
- MySQL root 密码:`root`(仅本地/内网开发使用)
|
||||||
|
- 平台管理员:`superadmin` / `admin123`
|
||||||
|
|
||||||
|
### 5.6 新增环境/自定义地址
|
||||||
|
|
||||||
|
复制任意一份模板后修改:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.local .env
|
||||||
|
# 编辑 .env 中的 MYSQL_HOST、REDIS_HOST、RABBITMQ_HOST、MCI_SERVER_URL、VITE_API_BASE_URL
|
||||||
|
```
|
||||||
|
|
||||||
|
## 6. 本地调试(IDE)
|
||||||
|
|
||||||
|
### 6.1 后端 Spring Boot 调试
|
||||||
|
|
||||||
|
1. 切换环境:
|
||||||
|
```bash
|
||||||
|
cd /Users/marsal/Projects/sino-project/sino-mci
|
||||||
|
cp .env.local .env # 连接本地 docker-compose 基础设施
|
||||||
|
# 或 cp .env.remote .env # 连接远程基础设施(如 192.168.1.173)
|
||||||
|
```
|
||||||
|
|
||||||
|
2. 在 IntelliJ IDEA 中创建 Run Configuration:
|
||||||
|
- **Main class**: `com.sino.mci.server.MciServerApplication`
|
||||||
|
- **Active profiles**: `dev`
|
||||||
|
- **Environment variables**: 填入 `.env` 中的关键变量,例如:
|
||||||
|
```
|
||||||
|
MYSQL_HOST=localhost;REDIS_HOST=localhost;RABBITMQ_HOST=localhost;MYSQL_PORT=3306;MYSQL_ROOT_PASSWORD=root;MYSQL_DATABASE=sino_mci;MYSQL_USER=root;REDIS_PORT=6379;RABBITMQ_DEFAULT_USER=guest;RABBITMQ_DEFAULT_PASS=guest;RABBITMQ_AMQP_PORT=5672
|
||||||
|
```
|
||||||
|
|
||||||
|
> 也可安装 EnvFile / .env 插件让 IDE 自动加载 `.env`。
|
||||||
|
|
||||||
|
3. 启动 Debug,访问 `http://localhost:8080/msg-platform`。
|
||||||
|
|
||||||
|
### 6.2 前端 Vite 调试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd admin-web
|
||||||
|
npm run dev
|
||||||
|
```
|
||||||
|
|
||||||
|
代理目标由 `VITE_API_BASE_URL` 决定(定义在 `.env` 中):
|
||||||
|
- `.env.local`:指向 `http://localhost:8080`
|
||||||
|
- `.env.remote`:指向 `http://192.168.1.173:8080`
|
||||||
|
|
||||||
|
### 6.3 Channel Service 调试
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd channel-service
|
||||||
|
source .venv/bin/activate
|
||||||
|
python -m app.main
|
||||||
|
```
|
||||||
|
|
||||||
|
`MCI_SERVER_URL` 从 `.env` 加载,指向后端地址。
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import client from './client'
|
|||||||
export * from './channelSubject'
|
export * from './channelSubject'
|
||||||
export * from './channelAccount'
|
export * from './channelAccount'
|
||||||
|
|
||||||
|
export interface ChannelIdentityQuery {
|
||||||
|
keyword?: string
|
||||||
|
}
|
||||||
|
|
||||||
export function createConversation(data: any) {
|
export function createConversation(data: any) {
|
||||||
return client.post('/api/v1/conversation', data)
|
return client.post('/api/v1/conversation', data)
|
||||||
}
|
}
|
||||||
@@ -25,3 +29,15 @@ export function bindConversationAccount(conversationId: number, data: any) {
|
|||||||
export function bindConversationTags(conversationId: number, data: any) {
|
export function bindConversationTags(conversationId: number, data: any) {
|
||||||
return client.post(`/api/v1/conversation/${conversationId}/bind-tags`, data)
|
return client.post(`/api/v1/conversation/${conversationId}/bind-tags`, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function listChannelIdentities(params?: ChannelIdentityQuery) {
|
||||||
|
return client.get('/api/v1/channel-identity/list', { params })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listConversationParticipants(conversationId: number) {
|
||||||
|
return client.get(`/api/v1/conversation/${conversationId}/participants`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function listConversationTags(conversationId: number) {
|
||||||
|
return client.get(`/api/v1/conversation/${conversationId}/tags`)
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
<el-menu-item index="/channel/subject">渠道主体</el-menu-item>
|
<el-menu-item index="/channel/subject">渠道主体</el-menu-item>
|
||||||
<el-menu-item index="/channel/app">渠道应用</el-menu-item>
|
<el-menu-item index="/channel/app">渠道应用</el-menu-item>
|
||||||
<el-menu-item index="/channel/account">渠道账号</el-menu-item>
|
<el-menu-item index="/channel/account">渠道账号</el-menu-item>
|
||||||
|
<el-menu-item index="/channel/identity">渠道身份</el-menu-item>
|
||||||
</el-sub-menu>
|
</el-sub-menu>
|
||||||
|
|
||||||
<el-sub-menu index="/customer">
|
<el-sub-menu index="/customer">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Dashboard from '../views/Dashboard.vue'
|
|||||||
import ChannelSubject from '../views/ChannelSubject.vue'
|
import ChannelSubject from '../views/ChannelSubject.vue'
|
||||||
import ChannelApp from '../views/ChannelApp.vue'
|
import ChannelApp from '../views/ChannelApp.vue'
|
||||||
import ChannelAccount from '../views/ChannelAccount.vue'
|
import ChannelAccount from '../views/ChannelAccount.vue'
|
||||||
|
import ChannelIdentity from '../views/ChannelIdentity.vue'
|
||||||
import Person from '../views/Person.vue'
|
import Person from '../views/Person.vue'
|
||||||
import Conversation from '../views/Conversation.vue'
|
import Conversation from '../views/Conversation.vue'
|
||||||
import Tag from '../views/Tag.vue'
|
import Tag from '../views/Tag.vue'
|
||||||
@@ -30,6 +31,7 @@ const routes = [
|
|||||||
{ path: 'channel/subject', component: ChannelSubject, meta: { title: '渠道主体' } },
|
{ path: 'channel/subject', component: ChannelSubject, meta: { title: '渠道主体' } },
|
||||||
{ path: 'channel/app', component: ChannelApp, meta: { title: '渠道应用' } },
|
{ path: 'channel/app', component: ChannelApp, meta: { title: '渠道应用' } },
|
||||||
{ path: 'channel/account', component: ChannelAccount, meta: { title: '渠道账号' } },
|
{ path: 'channel/account', component: ChannelAccount, meta: { title: '渠道账号' } },
|
||||||
|
{ path: 'channel/identity', component: ChannelIdentity, meta: { title: '渠道身份' } },
|
||||||
{ path: 'customer/person', component: Person, meta: { title: '联系人' } },
|
{ path: 'customer/person', component: Person, meta: { title: '联系人' } },
|
||||||
{ path: 'customer/group', component: Conversation, meta: { title: '群聊' } },
|
{ path: 'customer/group', component: Conversation, meta: { title: '群聊' } },
|
||||||
{ path: 'customer/tag', component: Tag, meta: { title: '标签体系' } },
|
{ path: 'customer/tag', component: Tag, meta: { title: '标签体系' } },
|
||||||
|
|||||||
112
admin-web/src/views/ChannelIdentity.vue
Normal file
112
admin-web/src/views/ChannelIdentity.vue
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<PageCard>
|
||||||
|
<template #toolbar-left>
|
||||||
|
<el-input
|
||||||
|
v-model="searchKeyword"
|
||||||
|
placeholder="搜索渠道用户ID / 渠道用户名 / 联系人姓名"
|
||||||
|
clearable
|
||||||
|
style="width: 320px"
|
||||||
|
>
|
||||||
|
<template #prefix>
|
||||||
|
<el-icon><Search /></el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table :data="pagedIdentities" class="sino-table">
|
||||||
|
<el-table-column prop="id" label="ID" width="80" />
|
||||||
|
<el-table-column prop="channelType" label="渠道类型" width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ channelLabel(row.channelType) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="channelUserId" label="渠道用户ID" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="channelUserName" label="渠道用户名" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="subjectName" label="所属主体" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="personName" label="关联联系人" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="personType" label="人员类型" width="110">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatPersonType(row.personType) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="status" label="状态" width="120">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.status === 1 ? 'success' : 'info'" size="small">
|
||||||
|
{{ row.status === 1 ? '启用' : '禁用' }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<template #pagination>
|
||||||
|
<el-pagination
|
||||||
|
v-model:current-page="currentPage"
|
||||||
|
v-model:page-size="pageSize"
|
||||||
|
:total="filteredIdentities.length"
|
||||||
|
:page-sizes="[10, 20, 50]"
|
||||||
|
layout="total, sizes, prev, pager, next"
|
||||||
|
background
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</PageCard>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
|
import { Search } from '@element-plus/icons-vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { listChannelIdentities } from '@/api/channel'
|
||||||
|
import PageCard from '@/components/PageCard.vue'
|
||||||
|
|
||||||
|
const identities = ref<any[]>([])
|
||||||
|
const searchKeyword = ref('')
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const pageSize = ref(10)
|
||||||
|
|
||||||
|
const filteredIdentities = computed(() => {
|
||||||
|
const kw = searchKeyword.value.trim().toLowerCase()
|
||||||
|
if (!kw) return identities.value
|
||||||
|
return identities.value.filter((item) =>
|
||||||
|
[item.channelUserId, item.channelUserName, item.personName].some((v) =>
|
||||||
|
String(v || '').toLowerCase().includes(kw)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const pagedIdentities = computed(() => {
|
||||||
|
const start = (currentPage.value - 1) * pageSize.value
|
||||||
|
return filteredIdentities.value.slice(start, start + pageSize.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch([searchKeyword], () => {
|
||||||
|
currentPage.value = 1
|
||||||
|
})
|
||||||
|
|
||||||
|
function channelLabel(type: string | undefined) {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
wecom: '企微',
|
||||||
|
wechat_personal: '个微',
|
||||||
|
dingtalk: '钉钉',
|
||||||
|
feishu: '飞书',
|
||||||
|
ivr: 'IVR',
|
||||||
|
}
|
||||||
|
return map[type || ''] || type || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPersonType(type: number | undefined) {
|
||||||
|
if (type === 3) return '客户'
|
||||||
|
if (type == null) return '-'
|
||||||
|
return '内部'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadIdentities() {
|
||||||
|
try {
|
||||||
|
const res: any = await listChannelIdentities({ keyword: searchKeyword.value.trim() })
|
||||||
|
identities.value = res.data || []
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e.message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(loadIdentities)
|
||||||
|
</script>
|
||||||
@@ -151,6 +151,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
|
||||||
|
<div class="detail-section">
|
||||||
|
<div class="section-title">群成员</div>
|
||||||
|
<el-table :data="participants" class="sino-table" size="small" max-height="320">
|
||||||
|
<el-table-column prop="participantType" label="类型" width="90">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatParticipantType(row.participantType) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="channelUserId" label="渠道用户ID" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="personName" label="姓名" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="personType" label="人员类型" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
{{ formatParticipantPersonType(row.personType) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<el-divider />
|
||||||
|
|
||||||
|
<div class="detail-section">
|
||||||
|
<div class="section-title">已绑定标签</div>
|
||||||
|
<div v-if="boundTags.length" class="tag-list">
|
||||||
|
<el-tag v-for="t in boundTags" :key="t.id" class="tag-item" size="small">
|
||||||
|
{{ t.tagName }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<el-empty v-else description="暂无标签" :image-size="60" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="drawerVisible = false">取消</el-button>
|
<el-button @click="drawerVisible = false">取消</el-button>
|
||||||
<el-button type="danger" @click="handleDelete(currentConversation)">删除</el-button>
|
<el-button type="danger" @click="handleDelete(currentConversation)">删除</el-button>
|
||||||
@@ -219,6 +251,8 @@ import {
|
|||||||
createConversation,
|
createConversation,
|
||||||
deleteConversation,
|
deleteConversation,
|
||||||
listChannelAccounts,
|
listChannelAccounts,
|
||||||
|
listConversationParticipants,
|
||||||
|
listConversationTags,
|
||||||
listConversations,
|
listConversations,
|
||||||
updateConversation,
|
updateConversation,
|
||||||
} from '@/api/channel'
|
} from '@/api/channel'
|
||||||
@@ -248,6 +282,8 @@ const currentPage = ref(1)
|
|||||||
const pageSize = ref(10)
|
const pageSize = ref(10)
|
||||||
const drawerVisible = ref(false)
|
const drawerVisible = ref(false)
|
||||||
const currentConversation = ref<any>({})
|
const currentConversation = ref<any>({})
|
||||||
|
const participants = ref<any[]>([])
|
||||||
|
const boundTags = ref<any[]>([])
|
||||||
|
|
||||||
const filteredConversations = computed(() => {
|
const filteredConversations = computed(() => {
|
||||||
const q = searchQuery.value.trim().toLowerCase()
|
const q = searchQuery.value.trim().toLowerCase()
|
||||||
@@ -305,6 +341,8 @@ function openDrawer(row: any) {
|
|||||||
editingId.value = row.id
|
editingId.value = row.id
|
||||||
fillDialogForm(row)
|
fillDialogForm(row)
|
||||||
drawerVisible.value = true
|
drawerVisible.value = true
|
||||||
|
loadParticipants(row.id)
|
||||||
|
loadBoundTags(row.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const accountMap = computed(() => {
|
const accountMap = computed(() => {
|
||||||
@@ -334,6 +372,45 @@ function formatConversationType(type: number | undefined) {
|
|||||||
return type ?? '-'
|
return type ?? '-'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatParticipantType(type: number | undefined) {
|
||||||
|
if (type === 1) return '群主'
|
||||||
|
return '成员'
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatParticipantPersonType(type: number | undefined) {
|
||||||
|
if (type === 3) return '客户'
|
||||||
|
if (type == null) return '-'
|
||||||
|
return '内部'
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadParticipants(conversationId: number | undefined) {
|
||||||
|
if (!conversationId) {
|
||||||
|
participants.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res: any = await listConversationParticipants(conversationId)
|
||||||
|
participants.value = res.data || []
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e.message)
|
||||||
|
participants.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadBoundTags(conversationId: number | undefined) {
|
||||||
|
if (!conversationId) {
|
||||||
|
boundTags.value = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res: any = await listConversationTags(conversationId)
|
||||||
|
boundTags.value = res.data || []
|
||||||
|
} catch (e: any) {
|
||||||
|
ElMessage.error(e.message)
|
||||||
|
boundTags.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadConversations() {
|
async function loadConversations() {
|
||||||
try {
|
try {
|
||||||
const res: any = await listConversations()
|
const res: any = await listConversations()
|
||||||
@@ -504,4 +581,18 @@ onMounted(() => {
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-item {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/account.ts","./src/api/channel.ts","./src/api/channelaccount.ts","./src/api/channelsubject.ts","./src/api/client.ts","./src/api/crm.ts","./src/api/dashboard.ts","./src/api/flow.ts","./src/api/inbound.ts","./src/api/inboundflow.ts","./src/api/inboundwebhook.ts","./src/api/ivrflow.ts","./src/api/send.ts","./src/api/sendpolicy.ts","./src/api/sendrecord.ts","./src/api/synctask.ts","./src/router/index.ts","./src/stores/auth.ts","./src/app.vue","./src/components/layout.vue","./src/components/pagecard.vue","./src/views/channelaccount.vue","./src/views/channelapp.vue","./src/views/channelsubject.vue","./src/views/conversation.vue","./src/views/dashboard.vue","./src/views/flowtrace.vue","./src/views/inboundflow.vue","./src/views/inboundwebhook.vue","./src/views/ivrflow.vue","./src/views/login.vue","./src/views/messagetest.vue","./src/views/messages.vue","./src/views/person.vue","./src/views/sendpolicy.vue","./src/views/sendrecord.vue","./src/views/tag.vue","./src/views/tenantmanagement.vue","./src/views/usermanagement.vue"],"version":"6.0.3"}
|
{"root":["./src/main.ts","./src/vite-env.d.ts","./src/api/account.ts","./src/api/channel.ts","./src/api/channelaccount.ts","./src/api/channelsubject.ts","./src/api/client.ts","./src/api/crm.ts","./src/api/dashboard.ts","./src/api/flow.ts","./src/api/inbound.ts","./src/api/inboundflow.ts","./src/api/inboundwebhook.ts","./src/api/ivrflow.ts","./src/api/send.ts","./src/api/sendpolicy.ts","./src/api/sendrecord.ts","./src/api/synctask.ts","./src/router/index.ts","./src/stores/auth.ts","./src/app.vue","./src/components/layout.vue","./src/components/pagecard.vue","./src/views/channelaccount.vue","./src/views/channelapp.vue","./src/views/channelidentity.vue","./src/views/channelsubject.vue","./src/views/conversation.vue","./src/views/dashboard.vue","./src/views/flowtrace.vue","./src/views/inboundflow.vue","./src/views/inboundwebhook.vue","./src/views/ivrflow.vue","./src/views/login.vue","./src/views/messagetest.vue","./src/views/messages.vue","./src/views/person.vue","./src/views/sendpolicy.vue","./src/views/sendrecord.vue","./src/views/tag.vue","./src/views/tenantmanagement.vue","./src/views/usermanagement.vue"],"version":"6.0.3"}
|
||||||
@@ -1,9 +1,13 @@
|
|||||||
import { defineConfig } from 'vite'
|
import { defineConfig, loadEnv } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from '@vitejs/plugin-vue'
|
||||||
import { resolve } from 'path'
|
import { resolve } from 'path'
|
||||||
|
|
||||||
// https://vite.dev/config/
|
// https://vite.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig(({ mode }) => {
|
||||||
|
const env = loadEnv(mode, process.cwd(), '')
|
||||||
|
const apiBaseUrl = env.VITE_API_BASE_URL || 'http://localhost:8080'
|
||||||
|
|
||||||
|
return {
|
||||||
plugins: [vue()],
|
plugins: [vue()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
@@ -11,10 +15,10 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
port: 5173,
|
port: parseInt(env.VITE_ADMIN_WEB_PORT || '5173', 10),
|
||||||
proxy: {
|
proxy: {
|
||||||
'/msg-platform': {
|
'/msg-platform': {
|
||||||
target: 'http://localhost:8080',
|
target: apiBaseUrl,
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -22,4 +26,5 @@ export default defineConfig({
|
|||||||
build: {
|
build: {
|
||||||
outDir: 'dist',
|
outDir: 'dist',
|
||||||
},
|
},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -206,6 +206,7 @@ public class RealWeComSyncClient implements WeComSyncClient {
|
|||||||
members.add(WeComConversationMember.builder()
|
members.add(WeComConversationMember.builder()
|
||||||
.userId(member.getUserId())
|
.userId(member.getUserId())
|
||||||
.name(resolveMemberName(service, member))
|
.name(resolveMemberName(service, member))
|
||||||
|
.type(member.getType())
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ public class WeComConversationMember {
|
|||||||
|
|
||||||
private String userId;
|
private String userId;
|
||||||
private String name;
|
private String name;
|
||||||
|
private Integer type;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package com.sino.mci.channel.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.sino.mci.admin.security.AuthContext;
|
||||||
|
import com.sino.mci.channel.dto.ChannelIdentityResponse;
|
||||||
|
import com.sino.mci.channel.entity.ChannelSubject;
|
||||||
|
import com.sino.mci.channel.repository.ChannelSubjectMapper;
|
||||||
|
import com.sino.mci.crm.entity.ChannelIdentity;
|
||||||
|
import com.sino.mci.crm.entity.Person;
|
||||||
|
import com.sino.mci.crm.repository.ChannelIdentityMapper;
|
||||||
|
import com.sino.mci.crm.repository.PersonMapper;
|
||||||
|
import com.sino.mci.shared.web.ApiResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/v1/channel-identity")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ChannelIdentityController {
|
||||||
|
|
||||||
|
private final ChannelIdentityMapper channelIdentityMapper;
|
||||||
|
private final PersonMapper personMapper;
|
||||||
|
private final ChannelSubjectMapper channelSubjectMapper;
|
||||||
|
|
||||||
|
@GetMapping("/list")
|
||||||
|
public ApiResponse<List<ChannelIdentityResponse>> listChannelIdentities() {
|
||||||
|
String tenantCode = AuthContext.currentTenantCode();
|
||||||
|
List<ChannelIdentity> identities = channelIdentityMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ChannelIdentity>()
|
||||||
|
.eq(ChannelIdentity::getTenantCode, tenantCode)
|
||||||
|
.orderByDesc(ChannelIdentity::getId));
|
||||||
|
|
||||||
|
if (identities.isEmpty()) {
|
||||||
|
return ApiResponse.ok(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Long> personIds = identities.stream()
|
||||||
|
.map(ChannelIdentity::getPersonId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Set<Long> subjectIds = identities.stream()
|
||||||
|
.map(ChannelIdentity::getSubjectId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<Long, Person> personMap = personIds.isEmpty() ? Collections.emptyMap()
|
||||||
|
: personMapper.selectBatchIds(personIds).stream()
|
||||||
|
.collect(Collectors.toMap(Person::getId, p -> p));
|
||||||
|
Map<Long, ChannelSubject> subjectMap = subjectIds.isEmpty() ? Collections.emptyMap()
|
||||||
|
: channelSubjectMapper.selectBatchIds(subjectIds).stream()
|
||||||
|
.collect(Collectors.toMap(ChannelSubject::getId, s -> s));
|
||||||
|
|
||||||
|
List<ChannelIdentityResponse> responses = identities.stream().map(identity -> {
|
||||||
|
ChannelIdentityResponse response = new ChannelIdentityResponse();
|
||||||
|
response.setId(identity.getId());
|
||||||
|
response.setTenantCode(identity.getTenantCode());
|
||||||
|
response.setChannelType(identity.getChannelType());
|
||||||
|
response.setChannelUserId(identity.getChannelUserId());
|
||||||
|
response.setChannelUserName(identity.getChannelUserName());
|
||||||
|
response.setSubjectId(identity.getSubjectId());
|
||||||
|
ChannelSubject subject = identity.getSubjectId() == null ? null : subjectMap.get(identity.getSubjectId());
|
||||||
|
response.setSubjectName(subject == null ? null : subject.getSubjectName());
|
||||||
|
response.setPersonId(identity.getPersonId());
|
||||||
|
Person person = identity.getPersonId() == null ? null : personMap.get(identity.getPersonId());
|
||||||
|
response.setPersonName(person == null ? null : person.getPersonName());
|
||||||
|
response.setPersonType(person == null ? null : person.getPersonType());
|
||||||
|
response.setStatus(identity.getStatus());
|
||||||
|
return response;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
|
||||||
|
return ApiResponse.ok(responses);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,9 @@ package com.sino.mci.channel.controller;
|
|||||||
import com.sino.mci.admin.security.AuthContext;
|
import com.sino.mci.admin.security.AuthContext;
|
||||||
import com.sino.mci.channel.dto.BindConversationAccountRequest;
|
import com.sino.mci.channel.dto.BindConversationAccountRequest;
|
||||||
import com.sino.mci.channel.dto.BindConversationTagsRequest;
|
import com.sino.mci.channel.dto.BindConversationTagsRequest;
|
||||||
|
import com.sino.mci.channel.dto.ConversationParticipantResponse;
|
||||||
import com.sino.mci.channel.dto.CreateConversationRequest;
|
import com.sino.mci.channel.dto.CreateConversationRequest;
|
||||||
|
import com.sino.mci.channel.dto.TagResponse;
|
||||||
import com.sino.mci.channel.dto.UpdateConversationRequest;
|
import com.sino.mci.channel.dto.UpdateConversationRequest;
|
||||||
import com.sino.mci.channel.entity.Conversation;
|
import com.sino.mci.channel.entity.Conversation;
|
||||||
import com.sino.mci.channel.service.ConversationService;
|
import com.sino.mci.channel.service.ConversationService;
|
||||||
@@ -73,4 +75,18 @@ public class ConversationController {
|
|||||||
conversationService.bindTags(tenantCode, conversationId, request);
|
conversationService.bindTags(tenantCode, conversationId, request);
|
||||||
return ApiResponse.ok();
|
return ApiResponse.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{conversation_id}/participants")
|
||||||
|
public ApiResponse<List<ConversationParticipantResponse>> listParticipants(
|
||||||
|
@RequestHeader("X-Tenant-Code") String tenantCode,
|
||||||
|
@PathVariable("conversation_id") Long conversationId) {
|
||||||
|
return ApiResponse.ok(conversationService.listParticipants(tenantCode, conversationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/{conversation_id}/tags")
|
||||||
|
public ApiResponse<List<TagResponse>> listTags(
|
||||||
|
@RequestHeader("X-Tenant-Code") String tenantCode,
|
||||||
|
@PathVariable("conversation_id") Long conversationId) {
|
||||||
|
return ApiResponse.ok(conversationService.listTags(tenantCode, conversationId));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.sino.mci.channel.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ChannelIdentityResponse {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String tenantCode;
|
||||||
|
private String channelType;
|
||||||
|
private String channelUserId;
|
||||||
|
private String channelUserName;
|
||||||
|
private Long subjectId;
|
||||||
|
private String subjectName;
|
||||||
|
private Long personId;
|
||||||
|
private String personName;
|
||||||
|
private Integer personType;
|
||||||
|
private Integer status;
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package com.sino.mci.channel.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ConversationParticipantResponse {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private Integer participantType;
|
||||||
|
private LocalDateTime joinTime;
|
||||||
|
private Long channelIdentityId;
|
||||||
|
private String channelUserId;
|
||||||
|
private String channelUserName;
|
||||||
|
private Long personId;
|
||||||
|
private String personName;
|
||||||
|
private Integer personType;
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package com.sino.mci.channel.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TagResponse {
|
||||||
|
|
||||||
|
private Long id;
|
||||||
|
private String tenantCode;
|
||||||
|
private Long parentId;
|
||||||
|
private String tagPath;
|
||||||
|
private Integer level;
|
||||||
|
private String tagName;
|
||||||
|
private String tagSource;
|
||||||
|
private Integer status;
|
||||||
|
}
|
||||||
@@ -2,8 +2,12 @@ package com.sino.mci.channel.repository;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import com.sino.mci.channel.entity.ConversationParticipant;
|
import com.sino.mci.channel.entity.ConversationParticipant;
|
||||||
|
import org.apache.ibatis.annotations.Delete;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface ConversationParticipantMapper extends BaseMapper<ConversationParticipant> {
|
public interface ConversationParticipantMapper extends BaseMapper<ConversationParticipant> {
|
||||||
|
|
||||||
|
@Delete("DELETE FROM mci_conversation_participant WHERE conversation_id = #{conversationId}")
|
||||||
|
void physicalDeleteByConversationId(Long conversationId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,17 +3,25 @@ package com.sino.mci.channel.service;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.sino.mci.channel.dto.BindConversationAccountRequest;
|
import com.sino.mci.channel.dto.BindConversationAccountRequest;
|
||||||
import com.sino.mci.channel.dto.BindConversationTagsRequest;
|
import com.sino.mci.channel.dto.BindConversationTagsRequest;
|
||||||
|
import com.sino.mci.channel.dto.ConversationParticipantResponse;
|
||||||
import com.sino.mci.channel.dto.CreateConversationRequest;
|
import com.sino.mci.channel.dto.CreateConversationRequest;
|
||||||
|
import com.sino.mci.channel.dto.TagResponse;
|
||||||
import com.sino.mci.channel.dto.UpdateConversationRequest;
|
import com.sino.mci.channel.dto.UpdateConversationRequest;
|
||||||
import com.sino.mci.channel.entity.ChannelAccount;
|
import com.sino.mci.channel.entity.ChannelAccount;
|
||||||
import com.sino.mci.channel.entity.ChannelAccountConversation;
|
import com.sino.mci.channel.entity.ChannelAccountConversation;
|
||||||
import com.sino.mci.channel.entity.Conversation;
|
import com.sino.mci.channel.entity.Conversation;
|
||||||
|
import com.sino.mci.channel.entity.ConversationParticipant;
|
||||||
import com.sino.mci.channel.entity.ConversationTagBinding;
|
import com.sino.mci.channel.entity.ConversationTagBinding;
|
||||||
import com.sino.mci.channel.repository.ChannelAccountConversationMapper;
|
import com.sino.mci.channel.repository.ChannelAccountConversationMapper;
|
||||||
import com.sino.mci.channel.repository.ChannelAccountMapper;
|
import com.sino.mci.channel.repository.ChannelAccountMapper;
|
||||||
import com.sino.mci.channel.repository.ConversationMapper;
|
import com.sino.mci.channel.repository.ConversationMapper;
|
||||||
|
import com.sino.mci.channel.repository.ConversationParticipantMapper;
|
||||||
import com.sino.mci.channel.repository.ConversationTagBindingMapper;
|
import com.sino.mci.channel.repository.ConversationTagBindingMapper;
|
||||||
|
import com.sino.mci.crm.entity.ChannelIdentity;
|
||||||
|
import com.sino.mci.crm.entity.Person;
|
||||||
import com.sino.mci.crm.entity.Tag;
|
import com.sino.mci.crm.entity.Tag;
|
||||||
|
import com.sino.mci.crm.repository.ChannelIdentityMapper;
|
||||||
|
import com.sino.mci.crm.repository.PersonMapper;
|
||||||
import com.sino.mci.crm.repository.TagMapper;
|
import com.sino.mci.crm.repository.TagMapper;
|
||||||
import com.sino.mci.exception.BusinessException;
|
import com.sino.mci.exception.BusinessException;
|
||||||
import com.sino.mci.shared.util.JsonUtils;
|
import com.sino.mci.shared.util.JsonUtils;
|
||||||
@@ -22,7 +30,12 @@ import org.springframework.stereotype.Service;
|
|||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
import org.springframework.util.CollectionUtils;
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
@@ -34,6 +47,9 @@ public class ConversationService {
|
|||||||
private final ConversationTagBindingMapper conversationTagBindingMapper;
|
private final ConversationTagBindingMapper conversationTagBindingMapper;
|
||||||
private final TagMapper tagMapper;
|
private final TagMapper tagMapper;
|
||||||
private final ChannelAccountMapper accountMapper;
|
private final ChannelAccountMapper accountMapper;
|
||||||
|
private final ConversationParticipantMapper participantMapper;
|
||||||
|
private final ChannelIdentityMapper channelIdentityMapper;
|
||||||
|
private final PersonMapper personMapper;
|
||||||
|
|
||||||
public List<Conversation> listConversations(String tenantCode) {
|
public List<Conversation> listConversations(String tenantCode) {
|
||||||
return conversationMapper.selectList(
|
return conversationMapper.selectList(
|
||||||
@@ -160,4 +176,95 @@ public class ConversationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<ConversationParticipantResponse> listParticipants(String tenantCode, Long conversationId) {
|
||||||
|
getConversation(tenantCode, conversationId);
|
||||||
|
|
||||||
|
List<ConversationParticipant> participants = participantMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ConversationParticipant>()
|
||||||
|
.eq(ConversationParticipant::getConversationId, conversationId)
|
||||||
|
.orderByAsc(ConversationParticipant::getId));
|
||||||
|
|
||||||
|
if (participants.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Long> channelIdentityIds = participants.stream()
|
||||||
|
.map(ConversationParticipant::getChannelIdentityId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
Set<Long> personIds = participants.stream()
|
||||||
|
.map(ConversationParticipant::getPersonId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<Long, ChannelIdentity> identityMap = channelIdentityIds.isEmpty() ? Collections.emptyMap()
|
||||||
|
: channelIdentityMapper.selectBatchIds(channelIdentityIds).stream()
|
||||||
|
.collect(Collectors.toMap(ChannelIdentity::getId, Function.identity()));
|
||||||
|
Map<Long, Person> personMap = personIds.isEmpty() ? Collections.emptyMap()
|
||||||
|
: personMapper.selectBatchIds(personIds).stream()
|
||||||
|
.collect(Collectors.toMap(Person::getId, Function.identity()));
|
||||||
|
|
||||||
|
return participants.stream().map(participant -> {
|
||||||
|
ConversationParticipantResponse response = new ConversationParticipantResponse();
|
||||||
|
response.setId(participant.getId());
|
||||||
|
response.setParticipantType(participant.getParticipantType());
|
||||||
|
response.setJoinTime(participant.getJoinTime());
|
||||||
|
response.setChannelIdentityId(participant.getChannelIdentityId());
|
||||||
|
|
||||||
|
ChannelIdentity identity = participant.getChannelIdentityId() == null
|
||||||
|
? null : identityMap.get(participant.getChannelIdentityId());
|
||||||
|
response.setChannelUserId(identity == null ? null : identity.getChannelUserId());
|
||||||
|
response.setChannelUserName(identity == null ? null : identity.getChannelUserName());
|
||||||
|
|
||||||
|
Person person = participant.getPersonId() == null
|
||||||
|
? null : personMap.get(participant.getPersonId());
|
||||||
|
response.setPersonId(participant.getPersonId());
|
||||||
|
response.setPersonName(person == null ? null : person.getPersonName());
|
||||||
|
response.setPersonType(person == null ? null : person.getPersonType());
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TagResponse> listTags(String tenantCode, Long conversationId) {
|
||||||
|
getConversation(tenantCode, conversationId);
|
||||||
|
|
||||||
|
List<ConversationTagBinding> bindings = conversationTagBindingMapper.selectList(
|
||||||
|
new LambdaQueryWrapper<ConversationTagBinding>()
|
||||||
|
.eq(ConversationTagBinding::getConversationId, conversationId)
|
||||||
|
.orderByAsc(ConversationTagBinding::getId));
|
||||||
|
|
||||||
|
if (bindings.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<Long> tagIds = bindings.stream()
|
||||||
|
.map(ConversationTagBinding::getTagId)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
Map<Long, Tag> tagMap = tagMapper.selectBatchIds(tagIds).stream()
|
||||||
|
.collect(Collectors.toMap(Tag::getId, Function.identity()));
|
||||||
|
|
||||||
|
return bindings.stream()
|
||||||
|
.map(binding -> {
|
||||||
|
Tag tag = tagMap.get(binding.getTagId());
|
||||||
|
if (tag == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
TagResponse response = new TagResponse();
|
||||||
|
response.setId(tag.getId());
|
||||||
|
response.setTenantCode(tag.getTenantCode());
|
||||||
|
response.setParentId(tag.getParentId());
|
||||||
|
response.setTagPath(tag.getTagPath());
|
||||||
|
response.setLevel(tag.getLevel());
|
||||||
|
response.setTagName(tag.getTagName());
|
||||||
|
response.setTagSource(tag.getTagSource());
|
||||||
|
response.setStatus(tag.getStatus());
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,11 +135,10 @@ public class SyncDataPersister {
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
public void refreshParticipants(String tenantCode, Long subjectId, Conversation conversation, WeComConversation conv) {
|
public void refreshParticipants(String tenantCode, Long subjectId, Conversation conversation, WeComConversation conv) {
|
||||||
conversationParticipantMapper.delete(
|
conversationParticipantMapper.physicalDeleteByConversationId(conversation.getId());
|
||||||
new LambdaQueryWrapper<ConversationParticipant>()
|
|
||||||
.eq(ConversationParticipant::getConversationId, conversation.getId()));
|
|
||||||
|
|
||||||
for (WeComConversationMember member : conv.getMemberList()) {
|
for (WeComConversationMember member : conv.getMemberList()) {
|
||||||
|
log.debug("[SyncDataPersister] 群聊成员 userId={}, type={}", member.getUserId(), member.getType());
|
||||||
ChannelIdentity identity = findOrCreateIdentity(tenantCode, subjectId, member);
|
ChannelIdentity identity = findOrCreateIdentity(tenantCode, subjectId, member);
|
||||||
|
|
||||||
ConversationParticipant participant = new ConversationParticipant();
|
ConversationParticipant participant = new ConversationParticipant();
|
||||||
@@ -165,7 +164,13 @@ public class SyncDataPersister {
|
|||||||
.eq(ChannelIdentity::getChannelType, CHANNEL_WECOM)
|
.eq(ChannelIdentity::getChannelType, CHANNEL_WECOM)
|
||||||
.eq(ChannelIdentity::getChannelUserId, member.getUserId()));
|
.eq(ChannelIdentity::getChannelUserId, member.getUserId()));
|
||||||
|
|
||||||
|
int personType = resolveMemberPersonType(member.getType());
|
||||||
if (identity != null) {
|
if (identity != null) {
|
||||||
|
Person existPerson = personMapper.selectById(identity.getPersonId());
|
||||||
|
if (existPerson != null && !existPerson.getPersonType().equals(personType)) {
|
||||||
|
existPerson.setPersonType(personType);
|
||||||
|
personMapper.updateById(existPerson);
|
||||||
|
}
|
||||||
return identity;
|
return identity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,7 +179,7 @@ public class SyncDataPersister {
|
|||||||
person.setTenantCode(tenantCode);
|
person.setTenantCode(tenantCode);
|
||||||
person.setPersonCode(member.getUserId());
|
person.setPersonCode(member.getUserId());
|
||||||
person.setPersonName(displayName);
|
person.setPersonName(displayName);
|
||||||
person.setPersonType(PERSON_TYPE_CUSTOMER);
|
person.setPersonType(personType);
|
||||||
person.setStatus(1);
|
person.setStatus(1);
|
||||||
personMapper.insert(person);
|
personMapper.insert(person);
|
||||||
|
|
||||||
@@ -197,6 +202,11 @@ public class SyncDataPersister {
|
|||||||
return userId != null && !userId.isBlank() ? userId : "未知";
|
return userId != null && !userId.isBlank() ? userId : "未知";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int resolveMemberPersonType(Integer memberType) {
|
||||||
|
// 企微群聊成员类型:1=企业成员,2=外部联系人
|
||||||
|
return memberType != null && memberType == 1 ? PERSON_TYPE_INTERNAL : PERSON_TYPE_CUSTOMER;
|
||||||
|
}
|
||||||
|
|
||||||
private void ensureBinding(Long accountId, Long conversationId, int bindingType) {
|
private void ensureBinding(Long accountId, Long conversationId, int bindingType) {
|
||||||
ChannelAccountConversation existing = accountConversationMapper.selectOne(
|
ChannelAccountConversation existing = accountConversationMapper.selectOne(
|
||||||
new LambdaQueryWrapper<ChannelAccountConversation>()
|
new LambdaQueryWrapper<ChannelAccountConversation>()
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
spring:
|
spring:
|
||||||
datasource:
|
datasource:
|
||||||
url: jdbc:mysql://localhost:3306/sino_mci?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://${MYSQL_HOST:localhost}:${MYSQL_PORT:3306}/${MYSQL_DATABASE:sino_mci}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
|
||||||
username: root
|
username: ${MYSQL_USER:root}
|
||||||
password: root
|
password: ${MYSQL_ROOT_PASSWORD:root}
|
||||||
driver-class-name: com.mysql.cj.jdbc.Driver
|
driver-class-name: com.mysql.cj.jdbc.Driver
|
||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
host: localhost
|
host: ${REDIS_HOST:localhost}
|
||||||
port: 6379
|
port: ${REDIS_PORT:6379}
|
||||||
database: 0
|
database: 0
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
host: localhost
|
host: ${RABBITMQ_HOST:localhost}
|
||||||
port: 5672
|
port: ${RABBITMQ_AMQP_PORT:5672}
|
||||||
username: guest
|
username: ${RABBITMQ_DEFAULT_USER:guest}
|
||||||
password: guest
|
password: ${RABBITMQ_DEFAULT_PASS:guest}
|
||||||
ai:
|
ai:
|
||||||
openai:
|
openai:
|
||||||
api-key: ${OPENAI_API_KEY:}
|
api-key: ${OPENAI_API_KEY:}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.springframework.boot.test.context.SpringBootTest;
|
|||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.context.jdbc.Sql;
|
import org.springframework.test.context.jdbc.Sql;
|
||||||
import org.springframework.test.web.servlet.MockMvc;
|
import org.springframework.test.web.servlet.MockMvc;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
|||||||
|
|
||||||
@SpringBootTest
|
@SpringBootTest
|
||||||
@AutoConfigureMockMvc
|
@AutoConfigureMockMvc
|
||||||
|
@Transactional
|
||||||
@Sql(scripts = "/sql/test-channel-account.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
|
@Sql(scripts = "/sql/test-channel-account.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
|
||||||
class ChannelAccountEventControllerTest {
|
class ChannelAccountEventControllerTest {
|
||||||
|
|
||||||
|
|||||||
@@ -89,15 +89,17 @@ services:
|
|||||||
- "${MCI_SERVER_PORT:-8080}:8080"
|
- "${MCI_SERVER_PORT:-8080}:8080"
|
||||||
environment:
|
environment:
|
||||||
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-dev}
|
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE:-dev}
|
||||||
- SPRING_DATASOURCE_URL=jdbc:mysql://mci-mysql:3306/${MYSQL_DATABASE:-sino_mci}?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true
|
- MYSQL_HOST=${MYSQL_HOST:-mci-mysql}
|
||||||
- SPRING_DATASOURCE_USERNAME=${MYSQL_USER:-root}
|
- MYSQL_PORT=${MYSQL_PORT:-3306}
|
||||||
- SPRING_DATASOURCE_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}
|
- MYSQL_DATABASE=${MYSQL_DATABASE:-sino_mci}
|
||||||
- SPRING_DATA_REDIS_HOST=mci-redis
|
- MYSQL_USER=${MYSQL_USER:-root}
|
||||||
- SPRING_DATA_REDIS_PORT=6379
|
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD:-root}
|
||||||
- SPRING_RABBITMQ_HOST=mci-rabbitmq
|
- REDIS_HOST=${REDIS_HOST:-mci-redis}
|
||||||
- SPRING_RABBITMQ_PORT=5672
|
- REDIS_PORT=${REDIS_PORT:-6379}
|
||||||
- SPRING_RABBITMQ_USERNAME=${RABBITMQ_DEFAULT_USER:-guest}
|
- RABBITMQ_HOST=${RABBITMQ_HOST:-mci-rabbitmq}
|
||||||
- SPRING_RABBITMQ_PASSWORD=${RABBITMQ_DEFAULT_PASS:-guest}
|
- RABBITMQ_AMQP_PORT=${RABBITMQ_AMQP_PORT:-5672}
|
||||||
|
- RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER:-guest}
|
||||||
|
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS:-guest}
|
||||||
- JAVA_OPTS=${JAVA_OPTS:--XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0}
|
- JAVA_OPTS=${JAVA_OPTS:--XX:+UseContainerSupport -XX:MaxRAMPercentage=75.0}
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
networks:
|
networks:
|
||||||
@@ -141,12 +143,12 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${CHANNEL_SERVICE_PORT:-8000}:8000"
|
- "${CHANNEL_SERVICE_PORT:-8000}:8000"
|
||||||
environment:
|
environment:
|
||||||
- MCI_SERVER_URL=http://mci-server:8080/msg-platform
|
- MCI_SERVER_URL=${MCI_SERVER_URL:-http://mci-server:8080/msg-platform}
|
||||||
- EVENT_STORE_PATH=/app/data/events.db
|
- EVENT_STORE_PATH=/app/data/events.db
|
||||||
- MAX_RETRY=5
|
- MAX_RETRY=${MAX_RETRY:-5}
|
||||||
- BASE_RETRY_DELAY_SECONDS=5
|
- BASE_RETRY_DELAY_SECONDS=${BASE_RETRY_DELAY_SECONDS:-5}
|
||||||
- EVENT_RETENTION_DAYS=10
|
- EVENT_RETENTION_DAYS=${EVENT_RETENTION_DAYS:-10}
|
||||||
- EVENT_PUBLISH_INTERVAL_SECONDS=5
|
- EVENT_PUBLISH_INTERVAL_SECONDS=${EVENT_PUBLISH_INTERVAL_SECONDS:-5}
|
||||||
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
- OPENAI_API_KEY=${OPENAI_API_KEY:-}
|
||||||
volumes:
|
volumes:
|
||||||
- channel-service-data:/app/data
|
- channel-service-data:/app/data
|
||||||
|
|||||||
387
scripts/full-init-data.sql
Normal file
387
scripts/full-init-data.sql
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
-- MySQL dump 10.13 Distrib 8.0.36, for Linux (aarch64)
|
||||||
|
--
|
||||||
|
-- Host: localhost Database: sino_mci
|
||||||
|
-- ------------------------------------------------------
|
||||||
|
-- Server version 8.0.36
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!50503 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_channel_account`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_channel_account` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_account` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_channel_account` VALUES (1,'rescue',3,'wechat_personal',NULL,'wx-bot-01','测试号',0,NULL,0,0,0,NULL,1,'2026-07-10 16:02:35','2026-07-10 08:02:34','2026-07-10 08:02:34',0);
|
||||||
|
INSERT INTO `mci_channel_account` VALUES (748,'sino',3,'wecom',NULL,'wxid_tye8j2ccwplc12','中道',0,'',0,0,0,NULL,1,NULL,'2026-07-10 08:22:31','2026-07-10 08:22:31',0);
|
||||||
|
INSERT INTO `mci_channel_account` VALUES (750,'sino',1,'wecom',260,'xszs1','消息助手1',1,'',0,0,0,NULL,1,NULL,'2026-07-10 08:31:56','2026-07-10 08:31:56',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_account` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_channel_account_conversation`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_channel_account_conversation` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_account_conversation` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (250,420,254,1,1,1,0,'2026-07-07 16:42:06','2026-07-07 16:42:06');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (442,687,420,1,1,1,0,'2026-07-08 10:08:12','2026-07-08 10:08:12');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (477,726,447,1,0,1,0,'2026-07-10 02:47:38','2026-07-10 02:47:38');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (478,726,447,3,0,1,0,'2026-07-10 02:47:38','2026-07-10 02:47:38');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (483,735,450,1,0,1,0,'2026-07-10 11:54:51','2026-07-10 11:54:51');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (484,735,450,3,0,1,0,'2026-07-10 11:54:51','2026-07-10 11:54:51');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (485,736,451,1,0,1,0,'2026-07-10 11:56:11','2026-07-10 11:56:11');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (486,736,451,3,0,1,0,'2026-07-10 11:56:11','2026-07-10 11:56:11');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (487,739,452,1,0,1,0,'2026-07-10 11:56:13','2026-07-10 11:56:13');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (488,739,452,3,0,1,0,'2026-07-10 11:56:13','2026-07-10 11:56:13');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (495,745,457,1,0,1,0,'2026-07-10 07:13:37','2026-07-10 07:13:37');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (496,745,457,3,0,1,0,'2026-07-10 07:13:37','2026-07-10 07:13:37');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (500,750,457,1,0,1,0,'2026-07-13 01:33:56','2026-07-13 01:33:56');
|
||||||
|
INSERT INTO `mci_channel_account_conversation` VALUES (501,750,457,3,0,1,0,'2026-07-13 01:33:56','2026-07-13 01:33:56');
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_account_conversation` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_channel_event_record`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_channel_event_record` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_event_record` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_channel_event_record` VALUES ('evt-dup-001','wx-bot-01','heartbeat','2026-07-10 16:02:35');
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_event_record` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_channel_identity`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_channel_identity` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_identity` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (487,'system',545,'wecom','user-test-001','Test WeCom User',412,1,NULL,'2026-07-08 10:08:04','2026-07-08 10:08:04',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (532,'sino',592,'wecom','user001','张三',260,1,'{\"name\": \"张三\", \"type\": \"INTERNAL\", \"mobile\": \"13800138001\", \"userId\": \"user001\", \"departmentIds\": [1, 2]}','2026-07-10 02:47:30','2026-07-10 02:47:30',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (533,'sino',593,'wecom','wmexternal001','客户李四',260,1,'{\"name\": \"客户李四\", \"type\": \"EXTERNAL\", \"mobile\": \"13900139001\", \"userId\": \"wmexternal001\", \"departmentIds\": null}','2026-07-10 02:47:30','2026-07-10 02:47:30',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (542,'wecom-sync-df610999',602,'wecom','user001','张三',451,1,NULL,'2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (543,'wecom-sync-df610999',603,'wecom','wmexternal001','客户李四',451,1,NULL,'2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (544,'wecom-sync-62e48858',604,'wecom','user001','张三',455,1,NULL,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (545,'wecom-sync-62e48858',605,'wecom','wmexternal001','客户李四',455,1,NULL,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (546,'wecom-sync-201c6545',606,'wecom','wmexternal001','客户李四',456,1,'{\"name\": \"客户李四\", \"type\": \"EXTERNAL\", \"mobile\": \"13900139001\", \"userId\": \"wmexternal001\", \"departmentIds\": null}','2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (547,'wecom-sync-89d3c970',607,'wecom','wmexternal001','客户李四',457,1,'{\"name\": \"客户李四\", \"type\": \"EXTERNAL\", \"mobile\": \"13900139001\", \"userId\": \"wmexternal001\", \"departmentIds\": null}','2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (548,'wecom-sync-aa76552f',608,'wecom','user001','张三',458,1,NULL,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (549,'wecom-sync-aa76552f',609,'wecom','wmexternal001','客户李四',458,1,NULL,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (550,'wecom-sync-058b7154',610,'wecom','user001','张三',459,1,'{\"name\": \"张三\", \"type\": \"INTERNAL\", \"mobile\": \"13800138001\", \"userId\": \"user001\", \"departmentIds\": null}','2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (551,'wecom-sync-058b7154',611,'wecom','wmexternal001','客户李四',459,1,'{\"name\": \"客户李四\", \"type\": \"EXTERNAL\", \"mobile\": \"13900139001\", \"userId\": \"wmexternal001\", \"departmentIds\": null}','2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (552,'sino',612,'wecom','WangYuHang','WangYuHang',260,1,NULL,'2026-07-10 07:05:44','2026-07-10 07:05:44',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (553,'sino',613,'wecom','xszs1','xszs1',260,1,NULL,'2026-07-10 07:05:44','2026-07-10 07:05:44',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (554,'sino',614,'wecom','wmOTNXBwAAxIGXHoUglQNFeW4R2s1U0A','wmOTNXBwAAxIGXHoUglQNFeW4R2s1U0A',260,1,NULL,'2026-07-10 07:05:45','2026-07-10 07:05:45',0);
|
||||||
|
INSERT INTO `mci_channel_identity` VALUES (555,'sino',615,'wecom','wmOTNXBwAA47hZSVKXsWizrVyEghCmLA','wmOTNXBwAA47hZSVKXsWizrVyEghCmLA',260,1,NULL,'2026-07-10 07:05:45','2026-07-10 07:05:45',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_identity` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_channel_subject`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_channel_subject` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_subject` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (259,'system','wecom','zdqyewx','中道企业微信','wwa7cc4743271d6055','1000061','jlwpC5o68IbHWLguAX0rp6QNEbGEbwVf3tuHLJKV8GI',1,'',1,'2026-07-08 05:28:13','2026-07-08 05:28:13',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (260,'sino','wecom','zdqywx','中道企业微信','wwa7cc4743271d6055','1000061','jlwpC5o68IbHWLguAX0rp6QNEbGEbwVf3tuHLJKV8GI',1,'MIIEowIBAAKCAQEA16RGgb4bf4wbpcy8RBtxOfzo8W4wJ8AlrnSCJg6ctW3eyvTcMQxyecpQ0AhqOtOTytgaZ0778LNe8pKRufVNsSseT8xBaqiw/JXF17zFtOMErWXkJl/iKpktMozZnwT9HyLMC7oRMuAT55ikxNHUVjzgFHcDxlFldnxjvc+Wk+pNEctfoLaQjdUHe0RmR7trU8WtS21Nlj+qMU39fllolkuW+TRmJM15VY4e71v8IZmhdwQTCGHDpsxWMEoASCrWpSHjtOpmHfEyeDJe7SUVKdZDpD9UefWyx6ffLrAr774gwDoee9WPnn3+ukNVwarSpJ3a2+P6T258awPC8UO+QwIDAQABAoIBAAje8SeVMnxkvx5q9bO3jUjdZpfH+KMp/GLY2CHIqBmuk2O1/vjlkjAAaqKkNfqFiabmjO+DCEo2lslTzjrgJHoNVAnkVSyWE/HQF3twgJGYZTDc6C3L8/nco0dZE/q+scr+G3bACMUXArqcROrz1tBVYMJ2wuj9oDbnlxLP31nmTjBzhqgHkjjLOJM812s7R4eGLmFl7ZxfX15nq+lZDzYScGUws9y44ClTRto7p+iUrpOUZn84UOFSwlda2024+yV6IFEXHcyNdS1mWOz9aqFnfU/DdE4xAsgmrttKxlICDkjZ+cKMit5YsWb+XUKl+AVow1ONCog/bsrEM95P1sECgYEA/rPpp3c3N6qGb7RRttF6898ouXp2yGCEvEqeq9h2uHzpr1SSWz5StrGavHILiOKorMNSOiDVudysCiNU2tNnc9IVJGatrWJ5wTmNGY4hXu1WfPTDZB1QJCnD2zxYwC8bRVhYjrT7cM699F6wAesOJqqlM9viXXdZR3TXmG6eiQcCgYEA2L1vGQ8UBg1Uij/6B5LcsOwYNrRyb7is5oQFsskd4xPogeLRzNqijsaRX6/440VXlnsyyiCiDvOPneQz8b7NLcu2W3yhGZ5KdYf/EWYCOoa+MFjP335TbFKL/a6UChBc8SjOUpNbgUp5aty8tDeKL9VvFkIPd+EDEQytb2YvveUCgYEAjVgoNUAKS4D9Y+YMZsjvU4Cm5+9XFbHCV3+NeE4C7DSdtifXpHz8h9gdx6/+SLOH6X/nFMz97kvQyTt8Loec5IBULUQx44M/kAQxElp2mGhbU7K878T4oWjwAK0Hj3dUyfHSCzfSRXLlpUQapqXbz4dpcFL41ueRiv8DWdshbWcCgYB3TvAVL0OqgTQEVmtgN+vcSFqb4oEMFvC2g/5PCH63PYJD3YP37HKHfa8QqWGsWcN5RqASvBv5dwGbvL8LWCjCCN19RwG3hHcgc5hpD2oypXGUU486S+PIQThmkO9VCuTeq8PHmO5KIaDsvk3yNpCO/EKGUh8Jsodpnzzpkaq+MQKBgEJ2t76tRoJzCxbDQjdLoe4drldtHNkbMFXBwtrgdMc+0GdrKrRXoHqgD+iA+xsVYwYPfNlfxiYmZdcQ4yia24eIG8eMPMwzFAqUx8JFMX2wjrWZ6SEEl7lyOrPYhchIhU0zN/Q3fEu6x2dW35zLcEghRPzKTFPN02L5c15HBmqV',1,'2026-07-08 05:30:44','2026-07-08 05:30:44',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (412,'system','wecom','test-subject','Test Subject','test-corp',NULL,NULL,0,NULL,1,'2026-07-08 10:05:36','2026-07-08 10:05:36',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (451,'wecom-sync-df610999','wecom','sino','Sino','ww-sync-bfec74b8','1000002','secret-59a358be-7452-44b1-8f3b-a5544a4f9ab2',0,NULL,1,'2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (455,'wecom-sync-62e48858','wecom','sino','Sino','ww-sync-0b8db39b','1000002','secret-acd07024-42cb-48ab-9d13-6ead4d9d6555',0,NULL,1,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (456,'wecom-sync-201c6545','wecom','sino','Sino','ww-sync-85e02a42','1000002','secret-68b016d6-d593-4670-bfae-87b0f19346a3',0,NULL,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (457,'wecom-sync-89d3c970','wecom','sino','Sino','ww-sync-45495bd1','1000002','secret-246a3c6a-c168-4915-9025-4ea8d3e7706c',0,NULL,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (458,'wecom-sync-aa76552f','wecom','sino','Sino','ww-sync-0eb97c14','1000002','secret-516883a3-0fcb-4088-87c7-b727e336f494',0,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (459,'wecom-sync-058b7154','wecom','sino','Sino','ww-sync-75436607','1000002','secret-4adc676b-7324-46b1-9b27-a30b6821903e',0,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_channel_subject` VALUES (463,'apitest-1783655911','wecom','sino','Sino','ww-apittest','1000002','secret-apittest',0,NULL,1,'2026-07-10 03:58:33','2026-07-10 03:58:33',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_channel_subject` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_conversation`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_conversation` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_conversation` VALUES (254,'demo',2,'wechat_personal','test_group_001',NULL,NULL,'Test Group',2,NULL,NULL,NULL,NULL,NULL,NULL,1,'2026-07-07 16:41:59','2026-07-07 16:41:59',0);
|
||||||
|
INSERT INTO `mci_conversation` VALUES (420,'system',2,'wecom','test-room-001',687,412,'Test Group',2,NULL,NULL,NULL,NULL,NULL,NULL,1,'2026-07-08 10:07:55','2026-07-08 10:07:55',0);
|
||||||
|
INSERT INTO `mci_conversation` VALUES (447,'sino',2,'wecom','chat001',726,260,'测试客户群',1,NULL,NULL,NULL,NULL,NULL,NULL,1,'2026-07-10 02:47:38','2026-07-10 07:53:14',1);
|
||||||
|
INSERT INTO `mci_conversation` VALUES (450,'wecom-sync-df610999',2,'wecom','chat001',735,451,'测试客户群',1,NULL,100,200,300,'ORIGINAL',NULL,1,'2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_conversation` VALUES (451,'wecom-sync-62e48858',2,'wecom','chat001',736,455,'测试客户群',1,NULL,100,200,300,'ORIGINAL',NULL,1,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_conversation` VALUES (452,'wecom-sync-aa76552f',2,'wecom','chat001',739,458,'测试客户群',1,NULL,NULL,NULL,NULL,NULL,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_conversation` VALUES (457,'sino',2,'wecom','wrOTNXBwAAvXpuOtpOgwRwh7SDXqK_ew',750,260,'消息助手测试',1,NULL,NULL,NULL,NULL,NULL,NULL,1,'2026-07-10 07:13:37','2026-07-10 07:13:37',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_conversation_create_task`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_conversation_create_task` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation_create_task` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation_create_task` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_conversation_participant`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_conversation_participant` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation_participant` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (181,447,592,532,2,'2026-07-10 02:47:38','2026-07-10 02:47:38','2026-07-10 02:47:38',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (182,447,593,533,1,'2026-07-10 02:47:38','2026-07-10 02:47:38','2026-07-10 02:47:38',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (187,450,602,542,2,'2026-07-10 11:54:51','2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (188,450,603,543,1,'2026-07-10 11:54:51','2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (189,451,604,544,2,'2026-07-10 11:56:11','2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (190,451,605,545,1,'2026-07-10 11:56:11','2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (191,452,608,548,2,'2026-07-10 11:56:13','2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (192,452,609,549,1,'2026-07-10 11:56:13','2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (209,457,612,552,1,'2026-07-13 01:33:56','2026-07-13 01:33:56','2026-07-13 01:33:56',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (210,457,613,553,2,'2026-07-13 01:33:56','2026-07-13 01:33:56','2026-07-13 01:33:56',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (211,457,614,554,1,'2026-07-13 01:33:56','2026-07-13 01:33:56','2026-07-13 01:33:56',0);
|
||||||
|
INSERT INTO `mci_conversation_participant` VALUES (212,457,615,555,1,'2026-07-13 01:33:56','2026-07-13 01:33:56','2026-07-13 01:33:56',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation_participant` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_conversation_tag_binding`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_conversation_tag_binding` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation_tag_binding` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_conversation_tag_binding` VALUES (84,'system',143,420,'2026-07-09 01:27:04','2026-07-09 01:27:04',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_conversation_tag_binding` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_flow_definition`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_flow_definition` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_flow_definition` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mci_flow_definition` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_flow_execution_trace`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_flow_execution_trace` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_flow_execution_trace` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mci_flow_execution_trace` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_message_event`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_message_event` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_message_event` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mci_message_event` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_message_media`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_message_media` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_message_media` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mci_message_media` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_person`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_person` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_person` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_person` VALUES (545,'system','P001','Test User','13800138000',1,NULL,NULL,1,'2026-07-08 10:07:59','2026-07-08 10:07:59',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (592,'sino','user001','张三','13800138001',1,NULL,NULL,1,'2026-07-10 02:47:30','2026-07-10 07:52:37',1);
|
||||||
|
INSERT INTO `mci_person` VALUES (593,'sino','wmexternal001','客户李四','13900139001',3,NULL,NULL,1,'2026-07-10 02:47:30','2026-07-10 07:52:35',1);
|
||||||
|
INSERT INTO `mci_person` VALUES (602,'wecom-sync-df610999','user001','张三',NULL,3,NULL,NULL,1,'2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (603,'wecom-sync-df610999','wmexternal001','客户李四',NULL,3,NULL,NULL,1,'2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (604,'wecom-sync-62e48858','user001','张三',NULL,3,NULL,NULL,1,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (605,'wecom-sync-62e48858','wmexternal001','客户李四',NULL,3,NULL,NULL,1,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (606,'wecom-sync-201c6545','wmexternal001','客户李四','13900139001',3,NULL,NULL,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (607,'wecom-sync-89d3c970','wmexternal001','客户李四','13900139001',1,100,200,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (608,'wecom-sync-aa76552f','user001','张三',NULL,3,NULL,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (609,'wecom-sync-aa76552f','wmexternal001','客户李四',NULL,3,NULL,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (610,'wecom-sync-058b7154','user001','张三','13800138001',1,NULL,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (611,'wecom-sync-058b7154','wmexternal001','客户李四','13900139001',3,NULL,NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (612,'sino','WangYuHang','WangYuHang',NULL,1,NULL,NULL,1,'2026-07-10 07:05:44','2026-07-10 07:05:44',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (613,'sino','xszs1','xszs1',NULL,1,NULL,NULL,1,'2026-07-10 07:05:44','2026-07-10 07:05:44',0);
|
||||||
|
INSERT INTO `mci_person` VALUES (614,'sino','wmOTNXBwAAxIGXHoUglQNFeW4R2s1U0A','wmOTNXBwAAxIGXHoUglQNFeW4R2s1U0A',NULL,3,NULL,NULL,1,'2026-07-10 07:05:45','2026-07-10 07:52:28',1);
|
||||||
|
INSERT INTO `mci_person` VALUES (615,'sino','wmOTNXBwAA47hZSVKXsWizrVyEghCmLA','wmOTNXBwAA47hZSVKXsWizrVyEghCmLA',NULL,3,NULL,NULL,1,'2026-07-10 07:05:45','2026-07-10 07:52:25',1);
|
||||||
|
/*!40000 ALTER TABLE `mci_person` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_person_tag_binding`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_person_tag_binding` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_person_tag_binding` DISABLE KEYS */;
|
||||||
|
/*!40000 ALTER TABLE `mci_person_tag_binding` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_platform_role`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_platform_role` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_platform_role` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_platform_role` VALUES (1,'admin','租户管理员','[\"*\"]',1,'2026-07-07 10:58:32','2026-07-07 10:58:32',0);
|
||||||
|
INSERT INTO `mci_platform_role` VALUES (2,'operator','运营操作员','[\"send:write\", \"channel:read\", \"channel:write\", \"record:read\"]',1,'2026-07-07 10:58:32','2026-07-07 10:58:32',0);
|
||||||
|
INSERT INTO `mci_platform_role` VALUES (3,'viewer','只读用户','[\"record:read\", \"channel:read\"]',1,'2026-07-07 10:58:32','2026-07-07 10:58:32',0);
|
||||||
|
INSERT INTO `mci_platform_role` VALUES (4,'platform_admin','平台管理员','[\"tenant:manage\", \"user:manage\", \"*\"]',1,'2026-07-07 10:58:33','2026-07-07 10:58:33',0);
|
||||||
|
INSERT INTO `mci_platform_role` VALUES (5,'tenant_admin','租户管理员','[\"*\"]',1,'2026-07-07 10:58:33','2026-07-07 10:58:33',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_platform_role` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_policy_group`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_policy_group` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_policy_group` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_policy_group` VALUES (19,'demo','default','Default Policy','wechat_personal','[{\"type\": \"rate_limit\", \"limit\": 100, \"scope\": \"account\", \"window\": \"1d\"}, {\"type\": \"time_window\", \"allow\": [\"09:00-21:00\"]}]',1,'2026-07-07 16:40:44','2026-07-07 16:40:44',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_policy_group` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_send_record`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_send_record` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_send_record` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_send_record` VALUES (257,'SNDB85DD040DAE24A15A6F6CC287A502013','demo','conversation','{\"conversation_id\": 254}',NULL,254,NULL,420,'wechat_personal','{\"channel_type\": \"wechat_personal\"}','text','{\"type\":\"text\",\"body\":\"Hello from test\"}',NULL,2,0,0,NULL,'{\"simulated\": true}','2026-07-07 16:42:58','2026-07-07 16:42:58','2026-07-07 16:42:58','2026-07-07 16:42:58',0);
|
||||||
|
INSERT INTO `mci_send_record` VALUES (258,'SND33B7B5031752405FAA3F230220AE6D57','demo','conversation','{\"conversation_id\": 254}',NULL,254,NULL,420,'wechat_personal','{\"channel_type\": \"wechat_personal\"}','text','{\"type\":\"text\",\"body\":\"Hello from real send\"}',NULL,3,0,0,NULL,'{\"error\": \"all_accounts_failed\", \"attempts\": [{\"message\": \"文本内容为空\", \"success\": false, \"account_id\": 420}]}','2026-07-07 16:43:05','2026-07-07 16:43:06','2026-07-07 16:43:05','2026-07-07 16:43:05',0);
|
||||||
|
INSERT INTO `mci_send_record` VALUES (400,'SND1D39A51A90284155BB267F3B609D636E','system','conversation','{\"conversation_id\": 420}',NULL,420,NULL,687,'wecom','{\"strategy\": \"primary\", \"channel_type\": \"wecom\"}','text','{\"text\":\"hello from integration test\"}',NULL,2,0,0,NULL,'{\"simulated\": true}','2026-07-09 01:26:10','2026-07-09 01:26:10','2026-07-09 01:26:10','2026-07-09 01:26:10',0);
|
||||||
|
INSERT INTO `mci_send_record` VALUES (401,'SND32334309F6BB4A81A79FF6ADFF63B3A8','system','tag','{\"scope\": \"conversation\", \"tag_id\": 143}',NULL,420,NULL,687,'wecom','{\"strategy\": \"primary\", \"channel_type\": \"wecom\"}','text','{\"text\":\"hello tag target\"}',NULL,2,0,0,NULL,'{\"simulated\": true}','2026-07-09 01:27:10','2026-07-09 01:27:10','2026-07-09 01:27:10','2026-07-09 01:27:10',0);
|
||||||
|
INSERT INTO `mci_send_record` VALUES (402,'SND79FE8E0F6D16466BB8A1527D2C122F88','system','rule','{\"logic\": \"and\", \"rules\": [{\"field\": \"channel_type\", \"value\": \"wecom\", \"operator\": \"eq\"}]}',NULL,420,NULL,687,'wecom','{\"strategy\": \"primary\", \"channel_type\": \"wecom\"}','text','{\"text\":\"hello rule target\"}',NULL,2,0,0,NULL,'{\"simulated\": true}','2026-07-09 01:27:28','2026-07-09 01:27:28','2026-07-09 01:27:28','2026-07-09 01:27:28',0);
|
||||||
|
INSERT INTO `mci_send_record` VALUES (1001,'task-smoke','rescue','conversation',NULL,NULL,1,NULL,NULL,NULL,NULL,'text','{\"text\":\"hello\"}',NULL,2,0,0,NULL,'{\"success\": true, \"message_id\": \"mock-msg-8e1e05a82a0d4883\", \"text_preview\": \"hello smoke\", \"conversation_id\": \"chat-1\"}',NULL,'2026-07-10 04:33:02','2026-07-10 04:32:51','2026-07-10 04:32:51',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_send_record` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_sync_task`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_sync_task` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_sync_task` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (1,'wecom-sync-df610999','account_conversations','wecom',735,'sync-account',2,1,0,NULL,0,'2026-07-10 11:54:51','2026-07-10 11:54:51','2026-07-10 11:54:51',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (2,'wecom-sync-62e48858','account_conversations','wecom',736,'sync-account',2,1,0,NULL,0,'2026-07-10 11:56:11','2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (3,'wecom-sync-201c6545','account_contacts','wecom',737,'sync-account',2,1,0,NULL,0,'2026-07-10 11:56:12','2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (4,'wecom-sync-89d3c970','account_contacts','wecom',738,'sync-account',2,1,0,NULL,0,'2026-07-10 11:56:12','2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (5,'wecom-sync-aa76552f','account_conversations','wecom',739,'sync-account',2,1,0,NULL,0,'2026-07-10 11:56:13','2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (6,'wecom-sync-058b7154','enterprise_contacts','wecom',459,'sino',2,2,0,NULL,0,'2026-07-10 11:56:13','2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (7,'apitest-1783655911','enterprise_contacts','wecom',463,'sino',2,0,0,NULL,0,'2026-07-10 03:58:42','2026-07-10 03:58:33','2026-07-10 03:58:42',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (8,'apitest-1783655911','account_contacts','wecom',740,'zdqyewx',2,0,0,NULL,0,'2026-07-10 03:59:15','2026-07-10 03:59:14','2026-07-10 03:59:15',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (9,'apitest-1783655911','account_conversations','wecom',740,'zdqyewx',2,0,0,NULL,0,'2026-07-10 03:59:23','2026-07-10 03:59:23','2026-07-10 03:59:23',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (10,'apitest-1783655911','enterprise_contacts','wecom',463,'sino',2,0,0,NULL,0,'2026-07-10 04:00:28','2026-07-10 04:00:27','2026-07-10 04:00:28',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (11,'apitest-1783655911','account_contacts','wecom',740,'zdqyewx',2,0,0,NULL,0,'2026-07-10 04:01:49','2026-07-10 04:01:49','2026-07-10 04:01:49',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (12,'apitest-1783655911','account_conversations','wecom',740,'zdqyewx',2,0,0,NULL,0,'2026-07-10 04:01:54','2026-07-10 04:01:54','2026-07-10 04:01:54',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (13,'sino','account_conversations','wecom',743,'xszs1',2,0,0,NULL,0,'2026-07-10 05:17:37','2026-07-10 05:17:28','2026-07-10 05:17:37',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (14,'sino','account_conversations','wecom',743,'xszs1',2,0,0,NULL,0,'2026-07-10 05:17:53','2026-07-10 05:17:52','2026-07-10 05:17:53',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (15,'sino','account_conversations','wecom',743,'xszs1',2,0,0,NULL,0,'2026-07-10 05:18:09','2026-07-10 05:18:09','2026-07-10 05:18:09',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (16,'sino','account_conversations','wecom',743,'xszs1',2,0,0,NULL,0,'2026-07-10 05:31:25','2026-07-10 05:31:17','2026-07-10 05:31:25',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (17,'sino','account_conversations','wecom',743,'xszs1',2,0,0,NULL,0,'2026-07-10 05:36:24','2026-07-10 05:36:15','2026-07-10 05:36:24',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (18,'sino','account_conversations','wecom',745,'xszs1',3,0,0,'\n### Error updating database. Cause: java.sql.SQLException: Field \'person_name\' doesn\'t have a default value\n### The error may exist in com/sino/mci/crm/repository/PersonMapper.java (best guess)\n### The error may involve com.sino.mci.crm.repository.PersonMapper.insert-Inline\n### The error occurred while setting parameters\n### SQL: INSERT INTO mci_person ( tenant_code, person_code, person_type, status, create_time, update_time ) VALUES ( ?, ?, ?, ?, ?, ? )\n### Cause: java.sql.SQLException: Field \'person_name\' doesn\'t have a default value\n; Field \'person_name\' doesn\'t have a default value',0,'2026-07-10 07:02:30','2026-07-10 07:02:27','2026-07-10 07:02:30',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (19,'sino','account_conversations','wecom',745,'xszs1',2,1,0,NULL,0,'2026-07-10 07:05:45','2026-07-10 07:05:43','2026-07-10 07:05:45',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (20,'sino','account_conversations','wecom',745,'xszs1',2,1,0,NULL,0,'2026-07-10 07:07:46','2026-07-10 07:07:45','2026-07-10 07:07:46',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (21,'sino','account_conversations','wecom',745,'xszs1',2,1,0,NULL,0,'2026-07-10 07:13:37','2026-07-10 07:13:34','2026-07-10 07:13:37',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (22,'sino','account_contacts','wecom',745,'xszs1',2,0,0,NULL,0,'2026-07-10 07:52:52','2026-07-10 07:52:48','2026-07-10 07:52:52',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (23,'sino','account_conversations','wecom',745,'xszs1',2,1,0,NULL,0,'2026-07-10 07:53:02','2026-07-10 07:53:00','2026-07-10 07:53:02',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (24,'sino','account_conversations','wecom',750,'xszs1',3,0,0,'\n### Error updating database. Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'457-612-552-1\' for key \'mci_conversation_participant.uk_conv_participant\'\n### The error may exist in com/sino/mci/channel/repository/ConversationParticipantMapper.java (best guess)\n### The error may involve com.sino.mci.channel.repository.ConversationParticipantMapper.delete-Inline\n### The error occurred while setting parameters\n### SQL: UPDATE mci_conversation_participant SET deleted=1 WHERE deleted=0 AND (conversation_id = ?)\n### Cause: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry \'457-612-552-1\' for key \'mci_conversation_participant.uk_conv_participant\'\n; Duplicate entry \'457-612-552-1\' for key \'mci_conversation_participant.uk_conv_participant\'',0,'2026-07-13 01:31:22','2026-07-13 01:31:18','2026-07-13 01:31:22',0);
|
||||||
|
INSERT INTO `mci_sync_task` VALUES (25,'sino','account_conversations','wecom',750,'xszs1',2,1,0,NULL,0,'2026-07-13 01:33:56','2026-07-13 01:33:54','2026-07-13 01:33:56',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_sync_task` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_tag`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_tag` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_tag` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_tag` VALUES (99,'system',0,'测试标签',1,'测试标签','manual',1,'2026-07-08 04:12:30','2026-07-08 04:13:04',1);
|
||||||
|
INSERT INTO `mci_tag` VALUES (143,'system',0,'test-tag',1,'test-tag','manual',1,'2026-07-09 01:26:56','2026-07-09 01:26:56',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_tag` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_tenant`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_tenant` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_tenant` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_tenant` VALUES (1,'rescue','救援',NULL,NULL,1,'2026-07-10 08:02:34','2026-07-10 08:02:34',0,NULL);
|
||||||
|
INSERT INTO `mci_tenant` VALUES (1152,'system','系统默认租户',NULL,NULL,1,'2026-07-10 16:28:40','2026-07-10 16:28:40',0,NULL);
|
||||||
|
/*!40000 ALTER TABLE `mci_tenant` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_user`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_user` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_user` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_user` VALUES (606,'demo','admin','$2a$10$5lfY5FcT4oFndX67yxlK8.D6BHBm05qx8T39vG/6Ya47YDa1gYtIm','Admin',NULL,NULL,1,'2026-07-07 16:40:04','2026-07-08 02:49:37',1);
|
||||||
|
INSERT INTO `mci_user` VALUES (629,'system','superadmin','$2a$10$5TAes.7ToWDNNLenlNPcUeU4b6O4Nt.m9OXUpMp1wvo8JVEtCqJqu','平台管理员',NULL,NULL,1,'2026-07-08 01:11:15','2026-07-08 01:11:15',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (630,'testcorp2','admin','$2a$10$RO5ufe8i9OX4IsKuk.Y..ea5QfhWwrEJ5M1A/AtPV9WSA3WXgjdlu','admin',NULL,NULL,1,'2026-07-08 02:37:57','2026-07-08 02:49:45',1);
|
||||||
|
INSERT INTO `mci_user` VALUES (631,'system','testuser2','$2a$10$PFgXVrSzxdDlfxLGHRzDy.rsDYkZPqjEBv1eNehdTrYWJRWBQfdVa','Test User 2','13800138001',NULL,1,'2026-07-08 02:46:24','2026-07-08 02:46:24',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (632,'sino','admin','$2a$10$EmdeJwCOlqlB4I/51tYQ/eBWzdfJOuvqcCGpbSZCGDeYw1qVscvDK','admin','',NULL,1,'2026-07-08 02:49:25','2026-07-08 02:49:25',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (633,'testcorp3','admin','$2a$10$TZJUk8H13FYSwuP2V1CtHep9AALVJOkcMH4zjIOGT.7aNyqKWY/x2','admin',NULL,NULL,1,'2026-07-08 02:51:08','2026-07-08 02:51:08',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (634,'testcorp4','admin','$2a$10$yTkxh4GMQrtEtMcNNKo1pubkkVHJVSJjng0ii3ss4.Remsx2dIsDK','租户管理员','',NULL,1,'2026-07-08 02:53:36','2026-07-08 02:53:36',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1068,'wecom-sync-df610999','admin','$2a$10$ExPnfsSXRFsMEVkYYdT./OqYcOACG8OsR0a3NFRrHdZcscrgoVp5a','管理员','13800138000',NULL,1,'2026-07-10 11:54:50','2026-07-10 11:54:50',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1072,'wecom-sync-62e48858','admin','$2a$10$mMGc7LXV3blJbvnQcE..LuSP36JGpP7IsN1xgtqttg6QFE0UUAQ1C','管理员','13800138000',NULL,1,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1073,'wecom-sync-201c6545','admin','$2a$10$uxgOTttbTEcObvK3uTNDUeFW9hnLsqKG5amITCu5d5EcyHSkA4cp.','管理员','13800138000',NULL,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1074,'wecom-sync-89d3c970','admin','$2a$10$ZuqrWWLH0euEZPJ0TEkdfe86m3i3bIiw8rQwPTpICsZtMTQQemsXa','管理员','13800138000',NULL,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1075,'wecom-sync-aa76552f','admin','$2a$10$9PLDj/N1oVekljaVBhLiFeHV2xaQfa.bK3m5ZtO2GcR0hkEMy.7.e','管理员','13800138000',NULL,1,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1076,'wecom-sync-058b7154','admin','$2a$10$K2BqrnZP1rELF0Aq3w0Ll..6gYStSbHMFgH8r3oU6noEbHhz13vJu','管理员','13800138000',NULL,1,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_user` VALUES (1080,'apitest-1783655911','admin','$2a$10$sc4DOy4hUAzR.XoTjKpE/OaUx7BtUmt4FG.Q7rI6fyKtzd4ko./le','管理员','13800138000',NULL,1,'2026-07-10 03:58:32','2026-07-10 03:58:32',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_user` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Dumping data for table `mci_user_role`
|
||||||
|
--
|
||||||
|
|
||||||
|
LOCK TABLES `mci_user_role` WRITE;
|
||||||
|
/*!40000 ALTER TABLE `mci_user_role` DISABLE KEYS */;
|
||||||
|
INSERT INTO `mci_user_role` VALUES (606,606,1,'2026-07-07 16:40:04','2026-07-07 16:40:04',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (629,629,4,'2026-07-08 01:11:15','2026-07-08 01:11:15',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (630,630,5,'2026-07-08 02:37:57','2026-07-08 02:37:57',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (631,631,5,'2026-07-08 02:46:24','2026-07-08 02:46:24',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (632,632,1,'2026-07-08 02:49:25','2026-07-08 02:49:51',1);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (633,632,1,'2026-07-08 02:49:51','2026-07-08 02:49:51',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (634,633,5,'2026-07-08 02:51:08','2026-07-08 02:51:08',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (635,634,5,'2026-07-08 02:53:36','2026-07-08 02:55:39',1);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (636,634,5,'2026-07-08 02:55:40','2026-07-08 02:55:40',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1070,1068,4,'2026-07-10 11:54:50','2026-07-10 11:54:50',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1074,1072,4,'2026-07-10 11:56:11','2026-07-10 11:56:11',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1075,1073,4,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1076,1074,4,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1077,1075,4,'2026-07-10 11:56:12','2026-07-10 11:56:12',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1078,1076,4,'2026-07-10 11:56:13','2026-07-10 11:56:13',0);
|
||||||
|
INSERT INTO `mci_user_role` VALUES (1082,1080,4,'2026-07-10 03:58:32','2026-07-10 03:58:32',0);
|
||||||
|
/*!40000 ALTER TABLE `mci_user_role` ENABLE KEYS */;
|
||||||
|
UNLOCK TABLES;
|
||||||
|
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
|
||||||
|
|
||||||
|
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
|
||||||
|
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
|
-- Dump completed on 2026-07-13 2:24:28
|
||||||
54
scripts/init-data.sql
Normal file
54
scripts/init-data.sql
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
-- Sino-MCI 默认初始化数据
|
||||||
|
-- 默认登录:system / superadmin / admin123
|
||||||
|
-- 注意:本脚本不包含渠道主体、渠道应用、渠道账号等敏感配置,需部署后通过页面自行配置。
|
||||||
|
|
||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
SET UNIQUE_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 默认租户
|
||||||
|
-- ----------------------------
|
||||||
|
LOCK TABLES `mci_tenant` WRITE;
|
||||||
|
INSERT IGNORE INTO `mci_tenant` (`id`, `tenant_code`, `tenant_name`, `auth_config`, `inbound_webhook_url`, `status`, `create_time`, `update_time`, `deleted`, `ext_info`) VALUES
|
||||||
|
(1152, 'system', '系统默认租户', NULL, NULL, 1, NOW(), NOW(), 0, NULL);
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 默认角色
|
||||||
|
-- ----------------------------
|
||||||
|
LOCK TABLES `mci_platform_role` WRITE;
|
||||||
|
INSERT IGNORE INTO `mci_platform_role` (`id`, `role_code`, `role_name`, `permissions`, `status`, `create_time`, `update_time`, `deleted`) VALUES
|
||||||
|
(1, 'admin', '租户管理员', '["*"]', 1, NOW(), NOW(), 0),
|
||||||
|
(2, 'operator', '运营操作员', '["send:write", "channel:read", "channel:write", "record:read"]', 1, NOW(), NOW(), 0),
|
||||||
|
(3, 'viewer', '只读用户', '["record:read", "channel:read"]', 1, NOW(), NOW(), 0),
|
||||||
|
(4, 'platform_admin', '平台管理员', '["tenant:manage", "user:manage", "*"]', 1, NOW(), NOW(), 0),
|
||||||
|
(5, 'tenant_admin', '租户管理员', '["*"]', 1, NOW(), NOW(), 0);
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 默认全局管理员
|
||||||
|
-- ----------------------------
|
||||||
|
LOCK TABLES `mci_user` WRITE;
|
||||||
|
INSERT IGNORE INTO `mci_user` (`id`, `tenant_code`, `username`, `password`, `real_name`, `phone`, `email`, `status`, `create_time`, `update_time`, `deleted`) VALUES
|
||||||
|
(629, 'system', 'superadmin', '$2a$10$5TAes.7ToWDNNLenlNPcUeU4b6O4Nt.m9OXUpMp1wvo8JVEtCqJqu', '平台管理员', NULL, NULL, 1, NOW(), NOW(), 0);
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 用户角色绑定
|
||||||
|
-- ----------------------------
|
||||||
|
LOCK TABLES `mci_user_role` WRITE;
|
||||||
|
INSERT IGNORE INTO `mci_user_role` (`id`, `user_id`, `role_id`, `create_time`, `update_time`, `deleted`) VALUES
|
||||||
|
(629, 629, 4, NOW(), NOW(), 0);
|
||||||
|
UNLOCK TABLES;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- 重置自增,避免后续插入冲突
|
||||||
|
-- ----------------------------
|
||||||
|
ALTER TABLE `mci_tenant` AUTO_INCREMENT = 1153;
|
||||||
|
ALTER TABLE `mci_platform_role` AUTO_INCREMENT = 6;
|
||||||
|
ALTER TABLE `mci_user` AUTO_INCREMENT = 630;
|
||||||
|
ALTER TABLE `mci_user_role` AUTO_INCREMENT = 630;
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
SET UNIQUE_CHECKS = 1;
|
||||||
@@ -17,8 +17,22 @@ MCI_SERVER_PORT=${MCI_SERVER_PORT:-8080}
|
|||||||
ADMIN_WEB_PORT=${ADMIN_WEB_PORT:-80}
|
ADMIN_WEB_PORT=${ADMIN_WEB_PORT:-80}
|
||||||
CHANNEL_SERVICE_PORT=${CHANNEL_SERVICE_PORT:-8000}
|
CHANNEL_SERVICE_PORT=${CHANNEL_SERVICE_PORT:-8000}
|
||||||
|
|
||||||
|
# Docker Compose 内部连接必须使用容器名,覆盖 .env 中的 host 配置。
|
||||||
|
# 当用本脚本启动应用服务时,应用与基础设施在同一 Docker 网络。
|
||||||
|
dc() {
|
||||||
|
MYSQL_HOST=mci-mysql \
|
||||||
|
REDIS_HOST=mci-redis \
|
||||||
|
RABBITMQ_HOST=mci-rabbitmq \
|
||||||
|
SEAWEEDFS_HOST=mci-seaweedfs \
|
||||||
|
MCI_SERVER_URL=http://mci-server:8080/msg-platform \
|
||||||
|
docker compose "$@"
|
||||||
|
}
|
||||||
|
|
||||||
WITH_APPS=""
|
WITH_APPS=""
|
||||||
BUILD_FLAG=""
|
BUILD_FLAG=""
|
||||||
|
INIT_DATA=""
|
||||||
|
INIT_FULL_DATA=""
|
||||||
|
FORCE_INIT=""
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
@@ -29,6 +43,9 @@ Start local Docker infrastructure (and optionally application services).
|
|||||||
Options:
|
Options:
|
||||||
--with-apps Also start mci-server, admin-web and channel-service
|
--with-apps Also start mci-server, admin-web and channel-service
|
||||||
--build Force (re)build images before starting services
|
--build Force (re)build images before starting services
|
||||||
|
--init-data Import scripts/init-data.sql after MySQL is healthy
|
||||||
|
--init-full-data Import scripts/full-init-data.sql after MySQL is healthy
|
||||||
|
--force-init Skip empty check and import init SQL directly
|
||||||
-h, --help Show this help message and exit
|
-h, --help Show this help message and exit
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@@ -37,6 +54,9 @@ while [ $# -gt 0 ]; do
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
--with-apps) WITH_APPS=1 ;;
|
--with-apps) WITH_APPS=1 ;;
|
||||||
--build) BUILD_FLAG="--build" ;;
|
--build) BUILD_FLAG="--build" ;;
|
||||||
|
--init-data) INIT_DATA=1 ;;
|
||||||
|
--init-full-data) INIT_FULL_DATA=1 ;;
|
||||||
|
--force-init) FORCE_INIT=1 ;;
|
||||||
-h|--help) usage; exit 0 ;;
|
-h|--help) usage; exit 0 ;;
|
||||||
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
|
*) echo "Unknown option: $1" >&2; usage; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
@@ -51,7 +71,7 @@ wait_for_healthy() {
|
|||||||
|
|
||||||
echo "Waiting for $service to become healthy..."
|
echo "Waiting for $service to become healthy..."
|
||||||
while [ "$retry_count" -lt "$max_retries" ]; do
|
while [ "$retry_count" -lt "$max_retries" ]; do
|
||||||
container_name=$(docker compose --profile infra --profile app ps -q "$service" 2>/dev/null | head -n1 || true)
|
container_name=$(dc -p sino-mci --profile infra --profile app ps -q "$service" 2>/dev/null | head -n1 || true)
|
||||||
|
|
||||||
if [ -n "$container_name" ]; then
|
if [ -n "$container_name" ]; then
|
||||||
local status
|
local status
|
||||||
@@ -67,17 +87,44 @@ wait_for_healthy() {
|
|||||||
done
|
done
|
||||||
|
|
||||||
echo "$service did not become healthy within $((max_retries * 2)) seconds."
|
echo "$service did not become healthy within $((max_retries * 2)) seconds."
|
||||||
docker compose --profile infra --profile app logs --tail=50 "$service"
|
dc -p sino-mci --profile infra --profile app logs --tail=50 "$service"
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_database() {
|
||||||
|
local sql_file=$1
|
||||||
|
if [ ! -f "$sql_file" ]; then
|
||||||
|
echo "Init SQL file not found: $sql_file" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$FORCE_INIT" ]; then
|
||||||
|
local tenant_count
|
||||||
|
tenant_count=$(docker exec -i mci-mysql mysql -uroot -proot -N -s sino_mci -e "SELECT COUNT(*) FROM mci_tenant;" 2>/dev/null || echo "0")
|
||||||
|
if [ "$tenant_count" -gt 0 ]; then
|
||||||
|
echo "Database already contains $tenant_count tenant(s), skipping init. Use --force-init to override."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Initializing database with $sql_file ..."
|
||||||
|
docker exec -i mci-mysql mysql -uroot -proot sino_mci < "$sql_file"
|
||||||
|
echo "Database initialized from $sql_file."
|
||||||
|
}
|
||||||
|
|
||||||
echo "Starting infrastructure services..."
|
echo "Starting infrastructure services..."
|
||||||
docker compose --profile infra up -d $BUILD_FLAG
|
dc -p sino-mci --profile infra up -d $BUILD_FLAG
|
||||||
|
|
||||||
wait_for_healthy mysql 60
|
wait_for_healthy mysql 60
|
||||||
wait_for_healthy redis 30
|
wait_for_healthy redis 30
|
||||||
wait_for_healthy rabbitmq 60
|
wait_for_healthy rabbitmq 60
|
||||||
|
|
||||||
|
if [ -n "$INIT_DATA" ]; then
|
||||||
|
init_database "scripts/init-data.sql"
|
||||||
|
elif [ -n "$INIT_FULL_DATA" ]; then
|
||||||
|
init_database "scripts/full-init-data.sql"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Infrastructure is ready."
|
echo "Infrastructure is ready."
|
||||||
echo "MySQL: jdbc:mysql://localhost:${MYSQL_PORT}/${MYSQL_DATABASE:-sino_mci}"
|
echo "MySQL: jdbc:mysql://localhost:${MYSQL_PORT}/${MYSQL_DATABASE:-sino_mci}"
|
||||||
echo "Redis: localhost:${REDIS_PORT}"
|
echo "Redis: localhost:${REDIS_PORT}"
|
||||||
@@ -87,7 +134,7 @@ echo "SeaweedFS: http://localhost:${SEAWEEDFS_MASTER_PORT}"
|
|||||||
if [ -n "$WITH_APPS" ]; then
|
if [ -n "$WITH_APPS" ]; then
|
||||||
echo "Starting application services..."
|
echo "Starting application services..."
|
||||||
# Include infra profile so that depends_on conditions can resolve.
|
# Include infra profile so that depends_on conditions can resolve.
|
||||||
docker compose --profile infra --profile app up -d $BUILD_FLAG
|
dc -p sino-mci --profile infra --profile app up -d $BUILD_FLAG
|
||||||
|
|
||||||
wait_for_healthy mci-server 90
|
wait_for_healthy mci-server 90
|
||||||
wait_for_healthy channel-service 60
|
wait_for_healthy channel-service 60
|
||||||
|
|||||||
71
scripts/run-local.sh
Executable file
71
scripts/run-local.sh
Executable file
@@ -0,0 +1,71 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Sino-MCI 本地开发启动脚本
|
||||||
|
# 自动加载项目根目录 .env 文件中的环境变量,然后启动后端或前端。
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# ./scripts/run-local.sh backend
|
||||||
|
# ./scripts/run-local.sh frontend
|
||||||
|
# ./scripts/run-local.sh channel
|
||||||
|
#
|
||||||
|
# 切换环境:
|
||||||
|
# cp .env.local .env # 本地基础设施
|
||||||
|
# cp .env.remote .env # 远程基础设施(如 192.168.1.173)
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PROJECT_ROOT="$SCRIPT_DIR/.."
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
if [ ! -f .env ]; then
|
||||||
|
echo ".env file not found. Creating from .env.local ..."
|
||||||
|
cp .env.local .env
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 加载 .env(忽略注释与空行)
|
||||||
|
set -o allexport
|
||||||
|
# shellcheck source=/dev/null
|
||||||
|
source .env
|
||||||
|
set +o allexport
|
||||||
|
|
||||||
|
echo "Loaded environment from: $PROJECT_ROOT/.env"
|
||||||
|
echo " MYSQL_HOST=${MYSQL_HOST:-localhost}"
|
||||||
|
echo " REDIS_HOST=${REDIS_HOST:-localhost}"
|
||||||
|
echo " RABBITMQ_HOST=${RABBITMQ_HOST:-localhost}"
|
||||||
|
echo " MCI_SERVER_URL=${MCI_SERVER_URL:-http://localhost:8080/msg-platform}"
|
||||||
|
echo " VITE_API_BASE_URL=${VITE_API_BASE_URL:-http://localhost:8080}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
SERVICE=${1:-}
|
||||||
|
if [ -z "$SERVICE" ]; then
|
||||||
|
echo "Usage: $0 <backend|frontend|channel>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$SERVICE" in
|
||||||
|
backend|server)
|
||||||
|
cd backend/mci-server
|
||||||
|
echo "Starting mci-server with Maven..."
|
||||||
|
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev
|
||||||
|
;;
|
||||||
|
frontend|web)
|
||||||
|
cd admin-web
|
||||||
|
echo "Starting admin-web with Vite..."
|
||||||
|
npm run dev
|
||||||
|
;;
|
||||||
|
channel|channel-service)
|
||||||
|
cd channel-service
|
||||||
|
echo "Starting channel-service..."
|
||||||
|
if [ ! -d .venv ]; then
|
||||||
|
python3 -m venv .venv
|
||||||
|
fi
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt -q
|
||||||
|
python -m app.main
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown service: $SERVICE"
|
||||||
|
echo "Usage: $0 <backend|frontend|channel>"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user