@ -0,0 +1,24 @@ |
|||||
|
# 微信小程序配置 |
||||
|
WECHAT_APPID=wx3da6ea0adf91cf0d |
||||
|
WECHAT_APPSECRET=78fd81bce5a2968a8e7c607ae68c4c0b |
||||
|
WECHAT_TOKEN=your-random-token |
||||
|
|
||||
|
# MySQL数据库配置(请根据您的实际环境修改) |
||||
|
# 如果是首次使用,可能需要先在MySQL中创建wechat_app数据库 |
||||
|
DB_HOST=1.95.162.61 |
||||
|
DB_PORT=3306 |
||||
|
DB_DATABASE=wechat_app |
||||
|
# 请使用您实际的MySQL用户名 |
||||
|
DB_USER=root |
||||
|
# 请使用您实际的MySQL密码 |
||||
|
# 如果MySQL的root用户有密码,请在此处填写 |
||||
|
# 如果没有密码,请保留为空字符串(DB_PASSWORD="") |
||||
|
DB_PASSWORD=schl@2025 |
||||
|
|
||||
|
# 服务器配置 |
||||
|
PORT=3003 |
||||
|
# 日志配置 |
||||
|
LOG_LEVEL=debug |
||||
|
NODE_ENV=production |
||||
|
# 详细日志记录,用于问题排查 |
||||
|
ENABLE_DETAILED_LOGGING=true |
||||
@ -0,0 +1,23 @@ |
|||||
|
# 使用官方的Node.js 18镜像作为基础镜像 |
||||
|
FROM node:18-alpine |
||||
|
|
||||
|
# 设置工作目录 |
||||
|
WORKDIR /app |
||||
|
|
||||
|
# 复制package.json和package-lock.json到工作目录 |
||||
|
COPY server-example/package*.json ./ |
||||
|
|
||||
|
# 安装依赖 |
||||
|
RUN npm install --production |
||||
|
|
||||
|
# 复制整个项目到工作目录 |
||||
|
COPY . . |
||||
|
|
||||
|
# 设置环境变量 |
||||
|
ENV NODE_ENV=production |
||||
|
|
||||
|
# 暴露应用端口 |
||||
|
EXPOSE 3000 |
||||
|
|
||||
|
# 运行应用 |
||||
|
CMD ["npm", "start"] |
||||
@ -0,0 +1,254 @@ |
|||||
|
# 多项目统一部署方案 |
||||
|
|
||||
|
## 目录结构设计 |
||||
|
|
||||
|
在云服务器上创建以下目录结构: |
||||
|
|
||||
|
``` |
||||
|
/projects/ |
||||
|
├── project1/ |
||||
|
│ ├── source/ # 项目源代码 |
||||
|
│ ├── logs/ # 项目日志 |
||||
|
│ ├── docker/ # Docker相关文件 |
||||
|
│ └── docker-compose.yml |
||||
|
├── project2/ |
||||
|
│ ├── source/ |
||||
|
│ ├── logs/ |
||||
|
│ ├── docker/ |
||||
|
│ └── docker-compose.yml |
||||
|
├── project3/ |
||||
|
│ ├── source/ |
||||
|
│ ├── logs/ |
||||
|
│ ├── docker/ |
||||
|
│ └── docker-compose.yml |
||||
|
├── project4/ |
||||
|
│ ├── source/ |
||||
|
│ ├── logs/ |
||||
|
│ ├── docker/ |
||||
|
│ └── docker-compose.yml |
||||
|
└── scripts/ # 统一管理脚本 |
||||
|
├── deploy-all.sh # 一键部署所有项目 |
||||
|
├── update-all.sh # 一键更新所有项目 |
||||
|
├── restart-all.sh # 一键重启所有项目 |
||||
|
└── status-all.sh # 查看所有项目状态 |
||||
|
``` |
||||
|
|
||||
|
## 部署脚本 |
||||
|
|
||||
|
### 1. 创建目录结构脚本 |
||||
|
|
||||
|
```bash |
||||
|
#!/bin/bash |
||||
|
# create-project-structure.sh |
||||
|
|
||||
|
echo "创建多项目统一部署目录结构..." |
||||
|
|
||||
|
# 创建主目录 |
||||
|
mkdir -p /projects/{project1,project2,project3,project4}/{source,logs,docker} |
||||
|
mkdir -p /projects/scripts |
||||
|
|
||||
|
echo "目录结构创建完成!" |
||||
|
echo "目录结构:" |
||||
|
find /projects -type d | sort |
||||
|
``` |
||||
|
|
||||
|
### 2. 统一部署脚本 |
||||
|
|
||||
|
```bash |
||||
|
#!/bin/bash |
||||
|
# /projects/scripts/deploy-all.sh |
||||
|
|
||||
|
echo "开始部署所有项目..." |
||||
|
|
||||
|
# 项目列表 |
||||
|
projects=("project1" "project2" "project3" "project4") |
||||
|
|
||||
|
for project in "${projects[@]}"; do |
||||
|
echo "\n=== 部署 $project ===" |
||||
|
cd /projects/$project |
||||
|
|
||||
|
# 拉取最新代码 |
||||
|
echo "拉取最新代码..." |
||||
|
cd source |
||||
|
git pull origin BOSS |
||||
|
cd .. |
||||
|
|
||||
|
# 构建并启动容器 |
||||
|
echo "构建并启动容器..." |
||||
|
docker-compose down |
||||
|
docker-compose up -d --build |
||||
|
|
||||
|
echo "$project 部署完成!" |
||||
|
done |
||||
|
|
||||
|
echo "\n所有项目部署完成!" |
||||
|
``` |
||||
|
|
||||
|
### 3. 统一更新脚本 |
||||
|
|
||||
|
```bash |
||||
|
#!/bin/bash |
||||
|
# /projects/scripts/update-all.sh |
||||
|
|
||||
|
echo "开始更新所有项目..." |
||||
|
|
||||
|
# 项目列表 |
||||
|
projects=("project1" "project2" "project3" "project4") |
||||
|
|
||||
|
for project in "${projects[@]}"; do |
||||
|
echo "\n=== 更新 $project ===" |
||||
|
cd /projects/$project |
||||
|
|
||||
|
# 拉取最新代码 |
||||
|
echo "拉取最新代码..." |
||||
|
cd source |
||||
|
git pull origin BOSS |
||||
|
cd .. |
||||
|
|
||||
|
# 重新构建并启动容器 |
||||
|
echo "重新构建并启动容器..." |
||||
|
docker-compose down |
||||
|
docker-compose up -d --build |
||||
|
|
||||
|
echo "$project 更新完成!" |
||||
|
done |
||||
|
|
||||
|
echo "\n所有项目更新完成!" |
||||
|
``` |
||||
|
|
||||
|
### 4. 统一状态查看脚本 |
||||
|
|
||||
|
```bash |
||||
|
#!/bin/bash |
||||
|
# /projects/scripts/status-all.sh |
||||
|
|
||||
|
echo "查看所有项目状态..." |
||||
|
|
||||
|
# 项目列表 |
||||
|
projects=("project1" "project2" "project3" "project4") |
||||
|
|
||||
|
for project in "${projects[@]}"; do |
||||
|
echo "\n=== $project 状态 ===" |
||||
|
cd /projects/$project |
||||
|
docker-compose ps |
||||
|
|
||||
|
echo "\n$project 日志(最近10行):" |
||||
|
docker-compose logs --tail=10 |
||||
|
done |
||||
|
|
||||
|
echo "\n所有项目状态查看完成!" |
||||
|
``` |
||||
|
|
||||
|
### 5. 统一重启脚本 |
||||
|
|
||||
|
```bash |
||||
|
#!/bin/bash |
||||
|
# /projects/scripts/restart-all.sh |
||||
|
|
||||
|
echo "开始重启所有项目..." |
||||
|
|
||||
|
# 项目列表 |
||||
|
projects=("project1" "project2" "project3" "project4") |
||||
|
|
||||
|
for project in "${projects[@]}"; do |
||||
|
echo "\n=== 重启 $project ===" |
||||
|
cd /projects/$project |
||||
|
docker-compose restart |
||||
|
echo "$project 重启完成!" |
||||
|
done |
||||
|
|
||||
|
echo "\n所有项目重启完成!" |
||||
|
``` |
||||
|
|
||||
|
## 项目配置示例 |
||||
|
|
||||
|
### 以当前项目为例,创建docker-compose.yml |
||||
|
|
||||
|
```yaml |
||||
|
# /projects/project1/docker-compose.yml |
||||
|
version: '3.8' |
||||
|
|
||||
|
services: |
||||
|
app: |
||||
|
build: ./source/server-example |
||||
|
container_name: project1-server |
||||
|
restart: always |
||||
|
ports: |
||||
|
- "3000:3000" |
||||
|
environment: |
||||
|
- NODE_ENV=production |
||||
|
volumes: |
||||
|
- ./logs:/app/logs |
||||
|
- ./source/server-example/uploads:/app/uploads |
||||
|
depends_on: |
||||
|
- db |
||||
|
|
||||
|
db: |
||||
|
image: mysql:8.0 |
||||
|
container_name: project1-db |
||||
|
restart: always |
||||
|
ports: |
||||
|
- "3306:3306" |
||||
|
environment: |
||||
|
- MYSQL_ROOT_PASSWORD=rootpassword |
||||
|
- MYSQL_DATABASE=wechat_miniprogram |
||||
|
- MYSQL_USER=wechat_user |
||||
|
- MYSQL_PASSWORD=wechat_password |
||||
|
volumes: |
||||
|
- db_data:/var/lib/mysql |
||||
|
|
||||
|
volumes: |
||||
|
db_data: |
||||
|
``` |
||||
|
|
||||
|
## 使用说明 |
||||
|
|
||||
|
1. **创建目录结构**: |
||||
|
```bash |
||||
|
chmod +x create-project-structure.sh |
||||
|
./create-project-structure.sh |
||||
|
``` |
||||
|
|
||||
|
2. **部署项目**: |
||||
|
- 将每个项目的源代码克隆到对应的`/projects/{projectN}/source/`目录 |
||||
|
- 为每个项目创建docker-compose.yml文件 |
||||
|
- 执行统一部署脚本: |
||||
|
```bash |
||||
|
chmod +x /projects/scripts/*.sh |
||||
|
/projects/scripts/deploy-all.sh |
||||
|
``` |
||||
|
|
||||
|
3. **管理项目**: |
||||
|
- 更新所有项目:`/projects/scripts/update-all.sh` |
||||
|
- 查看所有项目状态:`/projects/scripts/status-all.sh` |
||||
|
- 重启所有项目:`/projects/scripts/restart-all.sh` |
||||
|
|
||||
|
## 运维最佳实践 |
||||
|
|
||||
|
1. **日志管理**: |
||||
|
- 所有项目的日志统一存储在`/projects/{projectN}/logs/`目录 |
||||
|
- 可以使用ELK等工具进行统一日志收集和分析 |
||||
|
|
||||
|
2. **备份策略**: |
||||
|
- 定期备份数据库: |
||||
|
```bash |
||||
|
docker exec -t project1-db mysqldump -u wechat_user -p wechat_miniprogram > /projects/backups/project1_db_$(date +%Y%m%d_%H%M%S).sql |
||||
|
``` |
||||
|
- 定期备份源代码和配置文件 |
||||
|
|
||||
|
3. **监控告警**: |
||||
|
- 使用Prometheus和Grafana监控容器和应用状态 |
||||
|
- 设置告警规则,及时发现问题 |
||||
|
|
||||
|
4. **安全措施**: |
||||
|
- 定期更新容器镜像和依赖 |
||||
|
- 配置防火墙,只开放必要的端口 |
||||
|
- 定期检查系统安全漏洞 |
||||
|
|
||||
|
## 注意事项 |
||||
|
|
||||
|
1. 确保每个项目使用不同的端口,避免端口冲突 |
||||
|
2. 为每个项目的数据库设置强密码 |
||||
|
3. 定期清理无用的容器和镜像:`docker system prune -f` |
||||
|
4. 监控磁盘空间使用情况,及时清理日志文件 |
||||
|
5. 考虑使用CI/CD工具(如Jenkins、GitLab CI)实现自动化部署 |
||||
@ -0,0 +1,34 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
# 清理脚本 - 清除微信小程序后端服务部署的所有资源 |
||||
|
|
||||
|
echo "开始清理部署的资源..." |
||||
|
|
||||
|
# 进入项目目录 |
||||
|
cd /opt/project_app |
||||
|
|
||||
|
# 停止并移除所有相关容器 |
||||
|
echo "停止并移除容器..." |
||||
|
docker-compose down -v |
||||
|
|
||||
|
# 清理未使用的镜像 |
||||
|
echo "清理Docker镜像..." |
||||
|
docker image prune -f |
||||
|
|
||||
|
# 清理未使用的卷 |
||||
|
echo "清理Docker卷..." |
||||
|
docker volume prune -f |
||||
|
|
||||
|
# 清理未使用的网络 |
||||
|
echo "清理Docker网络..." |
||||
|
docker network prune -f |
||||
|
|
||||
|
# 清理临时文件 |
||||
|
echo "清理临时文件..." |
||||
|
rm -rf /tmp/project_app_temp 2>/dev/null || true |
||||
|
|
||||
|
# 清理构建缓存 |
||||
|
echo "清理Docker构建缓存..." |
||||
|
docker builder prune -f |
||||
|
|
||||
|
echo "清理完成!所有部署的资源已清除。" |
||||
@ -0,0 +1,186 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
# 部署脚本 - 微信小程序后端服务 |
||||
|
|
||||
|
echo "开始部署微信小程序后端服务..." |
||||
|
|
||||
|
# 检查是否安装了Docker |
||||
|
echo "检查Docker是否安装..." |
||||
|
if ! command -v docker &> /dev/null; then |
||||
|
echo "错误: Docker未安装,请先安装Docker" |
||||
|
exit 1 |
||||
|
fi |
||||
|
|
||||
|
# 配置Docker国内镜像源加速 |
||||
|
echo "配置Docker国内镜像源加速..." |
||||
|
mkdir -p /etc/docker |
||||
|
cat > /etc/docker/daemon.json << EOF |
||||
|
{ |
||||
|
"registry-mirrors": [ |
||||
|
"https://registry.cn-hangzhou.aliyuncs.com", |
||||
|
"https://docker.mirrors.ustc.edu.cn", |
||||
|
"https://mirror.baidubce.com", |
||||
|
"https://hub-mirror.c.163.com", |
||||
|
"https://reg-mirror.qiniu.com" |
||||
|
] |
||||
|
} |
||||
|
EOF |
||||
|
|
||||
|
# 重启Docker服务使配置生效 |
||||
|
systemctl daemon-reload 2>/dev/null || true |
||||
|
systemctl restart docker 2>/dev/null || true |
||||
|
sleep 5 |
||||
|
|
||||
|
# 检查并更新Docker Compose和Buildx |
||||
|
echo "检查并更新Docker工具..." |
||||
|
|
||||
|
# 检查是否安装了Docker Compose |
||||
|
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then |
||||
|
echo "Docker Compose未安装,正在安装..." |
||||
|
# 使用Daocloud镜像加速安装 |
||||
|
curl -L "https://get.daocloud.io/docker/compose/releases/download/latest/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose |
||||
|
chmod +x /usr/local/bin/docker-compose |
||||
|
fi |
||||
|
|
||||
|
# 安装或更新Docker Buildx |
||||
|
echo "检查并安装Docker Buildx..." |
||||
|
|
||||
|
# 检查Buildx是否已安装 |
||||
|
if ! docker buildx version &> /dev/null; then |
||||
|
echo "Buildx未安装,正在安装..." |
||||
|
|
||||
|
# 1. 获取系统架构 |
||||
|
ARCH=$(uname -m) |
||||
|
if [ "$ARCH" = "x86_64" ]; then |
||||
|
BUILDX_ARCH="amd64" |
||||
|
elif [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then |
||||
|
BUILDX_ARCH="arm64" |
||||
|
else |
||||
|
echo "错误: 不支持的系统架构 $ARCH" |
||||
|
echo "将直接使用docker build替代docker-compose build..." |
||||
|
export USE_ALTERNATE_BUILD="true" |
||||
|
fi |
||||
|
|
||||
|
if [ -z "$USE_ALTERNATE_BUILD" ]; then |
||||
|
# 2. 下载最新版本的Buildx |
||||
|
BUILDX_VERSION=$(curl -s https://api.github.com/repos/docker/buildx/releases/latest | grep -o '"tag_name": "v[^"]*"' | sed 's/"tag_name": "v//' | sed 's/"//') |
||||
|
echo "正在下载Buildx版本 $BUILDX_VERSION ($BUILDX_ARCH)..." |
||||
|
|
||||
|
# 3. 创建插件目录 |
||||
|
mkdir -p ~/.docker/cli-plugins |
||||
|
|
||||
|
# 4. 下载并安装Buildx |
||||
|
curl -L "https://github.com/docker/buildx/releases/download/v$BUILDX_VERSION/buildx-v$BUILDX_VERSION.linux-$BUILDX_ARCH" -o ~/.docker/cli-plugins/docker-buildx |
||||
|
|
||||
|
# 5. 添加执行权限 |
||||
|
chmod +x ~/.docker/cli-plugins/docker-buildx |
||||
|
|
||||
|
# 6. 验证安装 |
||||
|
if docker buildx version &> /dev/null; then |
||||
|
echo "Buildx安装成功!" |
||||
|
else |
||||
|
echo "警告: Buildx安装失败,将直接使用docker build替代docker-compose build..." |
||||
|
export USE_ALTERNATE_BUILD="true" |
||||
|
fi |
||||
|
fi |
||||
|
else |
||||
|
# Buildx已安装,检查版本 |
||||
|
buildx_version=$(docker buildx version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+' | head -1 || echo "0.0") |
||||
|
echo "当前Buildx版本: $buildx_version" |
||||
|
|
||||
|
if [[ "$buildx_version" < "0.17" ]]; then |
||||
|
echo "Buildx版本低于0.17,将直接使用docker build替代docker-compose build..." |
||||
|
export USE_ALTERNATE_BUILD="true" |
||||
|
fi |
||||
|
fi |
||||
|
|
||||
|
if [[ "$USE_ALTERNATE_BUILD" == "true" ]]; then |
||||
|
echo "将使用docker build直接构建镜像..." |
||||
|
fi |
||||
|
|
||||
|
echo "Docker工具检查完成" |
||||
|
|
||||
|
# 创建项目目录 |
||||
|
echo "创建项目目录..." |
||||
|
mkdir -p /opt/project_app/logs /opt/project_app/uploads /opt/project_app/mysql-data |
||||
|
|
||||
|
# 处理代码仓库或更新代码 |
||||
|
echo "处理代码仓库..." |
||||
|
REPO_URL="http://8.137.125.67:4000/Swt29/Project_app.git" |
||||
|
BRANCH="BOSS" |
||||
|
|
||||
|
# 测试仓库连接 |
||||
|
if ! git ls-remote $REPO_URL &> /dev/null; then |
||||
|
echo "警告: 无法连接到Gitea仓库 $REPO_URL (端口4000拒绝连接)" |
||||
|
echo "请检查网络连接和仓库地址是否正确" |
||||
|
echo "如果仓库不可用,将使用本地已有代码继续部署..." |
||||
|
else |
||||
|
if [ ! -d /opt/project_app/.git ]; then |
||||
|
# 如果目录不存在.git文件夹,检查目录是否为空 |
||||
|
if [ "$(ls -A /opt/project_app)" ]; then |
||||
|
# 目录不为空,创建临时目录克隆后复制文件 |
||||
|
echo "目标目录已存在且不为空,使用临时目录克隆代码..." |
||||
|
git clone -b $BRANCH $REPO_URL /tmp/project_app_temp |
||||
|
if [ $? -eq 0 ]; then |
||||
|
# 复制.git文件夹和所有文件 |
||||
|
cp -r /tmp/project_app_temp/. /opt/project_app/ |
||||
|
# 清理临时目录 |
||||
|
rm -rf /tmp/project_app_temp |
||||
|
else |
||||
|
echo "警告: 克隆仓库失败,将使用本地已有代码继续部署..." |
||||
|
fi |
||||
|
else |
||||
|
# 目录为空,直接克隆仓库 |
||||
|
git clone -b $BRANCH $REPO_URL /opt/project_app |
||||
|
fi |
||||
|
cd /opt/project_app |
||||
|
else |
||||
|
# 如果目录已存在.git文件夹,则更新代码 |
||||
|
cd /opt/project_app |
||||
|
# 使用fast-forward方式更新,避免分支冲突 |
||||
|
git fetch origin $BRANCH |
||||
|
git merge --ff-only FETCH_HEAD || { |
||||
|
echo "分支冲突,重置本地分支到远程最新版本..." |
||||
|
git reset --hard origin/$BRANCH |
||||
|
} |
||||
|
fi |
||||
|
|
||||
|
# 确保有必要的配置文件 |
||||
|
echo "确保配置文件存在..." |
||||
|
git fetch origin $BRANCH |
||||
|
git checkout origin/$BRANCH -- docker-compose.yml Dockerfile 2>/dev/null || true |
||||
|
fi |
||||
|
|
||||
|
# 检查.env文件是否存在 |
||||
|
echo "检查环境变量配置..." |
||||
|
if [ ! -f .env ]; then |
||||
|
echo "警告: .env文件不存在,请手动配置环境变量" |
||||
|
echo "可以参考.env.example文件创建.env文件" |
||||
|
else |
||||
|
echo ".env文件已存在,跳过配置步骤" |
||||
|
fi |
||||
|
|
||||
|
# 构建Docker镜像 |
||||
|
echo "构建Docker镜像..." |
||||
|
if [[ "$USE_ALTERNATE_BUILD" == "true" ]]; then |
||||
|
# 使用docker build替代docker-compose build |
||||
|
echo "使用docker build直接构建镜像..." |
||||
|
docker build -t project_app-app . --no-cache |
||||
|
if [ $? -eq 0 ]; then |
||||
|
echo "镜像构建成功!" |
||||
|
else |
||||
|
echo "错误: 镜像构建失败!" |
||||
|
exit 1 |
||||
|
fi |
||||
|
else |
||||
|
# 使用正常的docker-compose build |
||||
|
docker-compose build |
||||
|
fi |
||||
|
|
||||
|
# 启动服务 |
||||
|
echo "启动服务..." |
||||
|
docker-compose up -d |
||||
|
|
||||
|
echo "部署完成!服务已启动并在后台运行。" |
||||
|
echo "使用以下命令查看服务状态:docker-compose ps" |
||||
|
echo "使用以下命令查看日志:docker-compose logs -f" |
||||
@ -0,0 +1,28 @@ |
|||||
|
services: |
||||
|
app: |
||||
|
build: . |
||||
|
container_name: mini-program-server |
||||
|
restart: always |
||||
|
ports: |
||||
|
- "3000:3000" |
||||
|
volumes: |
||||
|
- /opt/project_app/logs:/app/server-example/logs |
||||
|
- /opt/project_app/uploads:/app/server-example/uploads |
||||
|
environment: |
||||
|
- NODE_ENV=production |
||||
|
depends_on: |
||||
|
- mysql |
||||
|
|
||||
|
mysql: |
||||
|
image: mysql:8.0 |
||||
|
container_name: mini-program-mysql |
||||
|
restart: always |
||||
|
ports: |
||||
|
- "3306:3306" |
||||
|
volumes: |
||||
|
- /opt/project_app/mysql-data:/var/lib/mysql |
||||
|
environment: |
||||
|
- MYSQL_ROOT_PASSWORD=your_root_password |
||||
|
- MYSQL_DATABASE=mini_program |
||||
|
- MYSQL_USER=mini_program_user |
||||
|
- MYSQL_PASSWORD=your_user_password |
||||
|
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 118 KiB |
|
Before Width: | Height: | Size: 108 KiB After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 251 KiB |
@ -0,0 +1,104 @@ |
|||||
|
# .env文件配置说明 |
||||
|
|
||||
|
## 1. 微信小程序配置 |
||||
|
|
||||
|
### WECHAT_APPID |
||||
|
- **含义**:微信小程序的唯一标识 |
||||
|
- **获取方式**:登录微信公众平台 -> 开发管理 -> 开发设置 -> 开发者ID(AppID) |
||||
|
- **当前值**:`wx3da6ea0adf91cf0d`(已配置,无需修改) |
||||
|
|
||||
|
### WECHAT_APPSECRET |
||||
|
- **含义**:微信小程序的应用密钥,用于接口调用 |
||||
|
- **获取方式**:登录微信公众平台 -> 开发管理 -> 开发设置 -> 开发者密码(AppSecret)(需要管理员扫码获取) |
||||
|
- **当前值**:`78fd81bce5a2968a8e7c607ae68c4c0b`(已配置,无需修改) |
||||
|
|
||||
|
### WECHAT_TOKEN |
||||
|
- **含义**:用于微信服务器验证的随机字符串,确保安全性 |
||||
|
- **设置方式**:可以自定义一个复杂的随机字符串(建议包含大小写字母、数字和特殊字符) |
||||
|
- **示例**:`abcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*` |
||||
|
- **当前值**:`your-random-token`(需要修改) |
||||
|
|
||||
|
## 2. MySQL数据库配置 |
||||
|
|
||||
|
### DB_HOST |
||||
|
- **含义**:MySQL数据库服务器的IP地址或域名 |
||||
|
- **设置方式**: |
||||
|
- 如果数据库和应用部署在同一台服务器,使用`localhost`或`127.0.0.1` |
||||
|
- 如果使用Docker Compose部署,使用服务名称(如`mysql`) |
||||
|
- 如果是远程数据库,使用数据库服务器的IP地址 |
||||
|
- **当前值**:`1.95.162.61`(已配置,无需修改) |
||||
|
|
||||
|
### DB_PORT |
||||
|
- **含义**:MySQL数据库服务的端口号 |
||||
|
- **默认值**:MySQL默认端口为`3306` |
||||
|
- **当前值**:`3306`(已配置,无需修改) |
||||
|
|
||||
|
### DB_DATABASE |
||||
|
- **含义**:要使用的数据库名称 |
||||
|
- **设置方式**:确保该数据库已在MySQL中创建 |
||||
|
- **当前值**:`wechat_app`(已配置,无需修改) |
||||
|
|
||||
|
### DB_USER |
||||
|
- **含义**:MySQL数据库的用户名 |
||||
|
- **设置方式**:使用具有该数据库访问权限的用户名 |
||||
|
- **当前值**:`root`(已配置,无需修改) |
||||
|
|
||||
|
### DB_PASSWORD |
||||
|
- **含义**:MySQL数据库用户的密码 |
||||
|
- **设置方式**: |
||||
|
- 如果用户有密码,填写实际密码 |
||||
|
- 如果没有密码,保留为空字符串(`DB_PASSWORD=""`) |
||||
|
- **当前值**:`schl@2025`(已配置,无需修改) |
||||
|
|
||||
|
## 3. 服务器配置 |
||||
|
|
||||
|
### PORT |
||||
|
- **含义**:Node.js服务器监听的端口号 |
||||
|
- **设置方式**:选择一个未被占用的端口(建议使用1024以上的端口) |
||||
|
- **当前值**:`3003`(已配置,无需修改) |
||||
|
|
||||
|
### LOG_LEVEL |
||||
|
- **含义**:日志记录的级别 |
||||
|
- **可选值**:`debug`, `info`, `warn`, `error` |
||||
|
- **建议**:开发环境使用`debug`,生产环境使用`info`或`warn` |
||||
|
- **当前值**:`debug`(已配置,建议生产环境修改为`info`) |
||||
|
|
||||
|
### NODE_ENV |
||||
|
- **含义**:Node.js运行环境 |
||||
|
- **可选值**:`development`(开发环境), `production`(生产环境) |
||||
|
- **当前值**:`production`(已配置,无需修改) |
||||
|
|
||||
|
### ENABLE_DETAILED_LOGGING |
||||
|
- **含义**:是否启用详细日志记录(用于问题排查) |
||||
|
- **可选值**:`true`, `false` |
||||
|
- **建议**:开发环境使用`true`,生产环境使用`false`以提高性能 |
||||
|
- **当前值**:`true`(已配置,建议生产环境修改为`false`) |
||||
|
|
||||
|
## 配置注意事项 |
||||
|
|
||||
|
1. **不要泄露敏感信息**:.env文件包含AppSecret、数据库密码等敏感信息,不要将其提交到代码仓库 |
||||
|
|
||||
|
2. **备份配置**:定期备份配置文件,以防丢失 |
||||
|
|
||||
|
3. **Docker部署时的特殊配置**: |
||||
|
- 如果使用Docker Compose部署,将`DB_HOST`改为`mysql`(与docker-compose.yml中的服务名称一致) |
||||
|
- 确保数据库端口在Docker Compose中正确映射 |
||||
|
|
||||
|
4. **环境一致性**:确保开发环境和生产环境的配置保持一致(除了敏感信息和环境特定配置) |
||||
|
|
||||
|
5. **配置验证**:配置完成后,启动服务前可以先测试数据库连接是否正常 |
||||
|
|
||||
|
## 修改示例 |
||||
|
|
||||
|
如果要修改配置,直接编辑.env文件即可: |
||||
|
|
||||
|
```bash |
||||
|
# 修改微信token |
||||
|
WECHAT_TOKEN=my-new-secure-token-2025 |
||||
|
|
||||
|
# 修改日志级别为info |
||||
|
LOG_LEVEL=info |
||||
|
|
||||
|
# 关闭详细日志 |
||||
|
ENABLE_DETAILED_LOGGING=false |
||||
|
``` |
||||
@ -0,0 +1,94 @@ |
|||||
|
# 微信小程序后端服务Docker部署指南 |
||||
|
|
||||
|
## 环境准备 |
||||
|
|
||||
|
- 安装Docker和Docker Compose |
||||
|
- 确保云服务器端口3000和3306对外开放 |
||||
|
|
||||
|
## 部署步骤 |
||||
|
|
||||
|
### 1. 克隆仓库 |
||||
|
|
||||
|
```bash |
||||
|
git clone http://8.137.125.67:4000/Swt29/Project_app.git |
||||
|
cd Project_app/server-example |
||||
|
``` |
||||
|
|
||||
|
### 2. 配置环境变量 |
||||
|
|
||||
|
复制并编辑.env文件: |
||||
|
|
||||
|
```bash |
||||
|
cp .env.example.mysql .env |
||||
|
# 编辑.env文件,配置数据库连接等信息 |
||||
|
``` |
||||
|
|
||||
|
### 3. 构建和启动容器 |
||||
|
|
||||
|
```bash |
||||
|
docker-compose up -d |
||||
|
``` |
||||
|
|
||||
|
### 4. 查看容器状态 |
||||
|
|
||||
|
```bash |
||||
|
docker-compose ps |
||||
|
``` |
||||
|
|
||||
|
### 5. 查看日志 |
||||
|
|
||||
|
```bash |
||||
|
docker-compose logs -f |
||||
|
``` |
||||
|
|
||||
|
## 代码更新和部署 |
||||
|
|
||||
|
### 1. 拉取最新代码 |
||||
|
|
||||
|
```bash |
||||
|
git pull origin BOSS |
||||
|
``` |
||||
|
|
||||
|
### 2. 重新构建和启动容器 |
||||
|
|
||||
|
```bash |
||||
|
docker-compose down |
||||
|
docker-compose up -d --build |
||||
|
``` |
||||
|
|
||||
|
## 数据库管理 |
||||
|
|
||||
|
### 进入数据库容器 |
||||
|
|
||||
|
```bash |
||||
|
docker exec -it wechat-miniprogram-db mysql -u wechat_user -p |
||||
|
``` |
||||
|
|
||||
|
### 备份数据库 |
||||
|
|
||||
|
```bash |
||||
|
docker exec -t wechat-miniprogram-db mysqldump -u wechat_user -p wechat_miniprogram > backup.sql |
||||
|
``` |
||||
|
|
||||
|
## 常见问题 |
||||
|
|
||||
|
### 端口冲突 |
||||
|
|
||||
|
如果端口3000或3306已被占用,可以修改docker-compose.yml文件中的端口映射: |
||||
|
|
||||
|
```yaml |
||||
|
ports: |
||||
|
- "8080:3000" # 将宿主机8080端口映射到容器3000端口 |
||||
|
``` |
||||
|
|
||||
|
### 数据库连接失败 |
||||
|
|
||||
|
检查.env文件中的数据库配置是否与docker-compose.yml中的配置一致。 |
||||
|
|
||||
|
### 容器启动失败 |
||||
|
|
||||
|
查看日志以获取详细错误信息: |
||||
|
|
||||
|
```bash |
||||
|
docker-compose logs -f app |
||||
|
``` |
||||
@ -0,0 +1,23 @@ |
|||||
|
# 使用Node.js官方镜像作为基础镜像 |
||||
|
FROM node:18-alpine |
||||
|
|
||||
|
# 设置工作目录 |
||||
|
WORKDIR /app |
||||
|
|
||||
|
# 复制package.json和package-lock.json |
||||
|
COPY package*.json ./ |
||||
|
|
||||
|
# 安装依赖 |
||||
|
RUN npm install --production |
||||
|
|
||||
|
# 复制应用代码 |
||||
|
COPY . . |
||||
|
|
||||
|
# 设置环境变量 |
||||
|
ENV NODE_ENV=production |
||||
|
|
||||
|
# 暴露端口 |
||||
|
EXPOSE 3000 |
||||
|
|
||||
|
# 启动命令 |
||||
|
CMD ["npm", "start"] |
||||
@ -1 +0,0 @@ |
|||||
PATH=C:\Program Files (x86)\Razer Chroma SDK\bin;C:\Program Files\Razer Chroma SDK\bin;C:\Program Files\Java\jdk1.8.0_202\bin;C:\Program Files\Java\jdk1.8.0_202;C:\Program Files\Common Files\Oracle\Java\javapath;D:\vm\bin\;C:\Program Files (x86)\Razer Chroma SDK\bin;D:\apache-tomcat-8.0.32\bin;C:\Program Files\Razer Chroma SDK\bin;C:\Program Files (x86)\Razer\ChromaBroadcast\bin;C:\Program Files\Razer\ChromaBroadcast\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Windows\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\Microsoft SQL Server\Client SDK\ODBC\170\Tools\Binn\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\System32\OpenSSH\;D:\ruanjian\Git\cmd;C:\Program Files (x86)\Microsoft SQL Server\160\DTS\Binn\;C:\Program Files (x86)\Windows Kits\10\Windows Performance Toolkit\;C:\Users\18477\AppData\Local\nvm;C:\nvm4w\nodejs;C:\Users\18477\AppData\Roaming\MySQL;C:\Program Files\MySQL\MySQL Server 8.0\bin;C:\WINDOWS\system32\config\systemprofile\AppData\Local\Microsoft\WindowsApps;C:\Program Files\dotnet\;C:\Program Files\MySQL\MySQL Shell 8.0\bin\;C:\Program Files\Java\jdk1.8.0_202\bin;D:\python\Scripts\;D:\python\;C:\Users\18477\AppData\Local\Microsoft\WindowsApps;C:\Users\18477\.dotnet\tools;D:\pycharm\PyCharm 2024.3.2\bin;;D:\VS Code\bin;C:\Program Files\JetBrains\IntelliJ IDEA 2025.2.3\bin;C:\Users\18477\AppData\Local\nvm;C:\nvm4w\nodejs; |
|
||||
@ -1,41 +0,0 @@ |
|||||
const { Sequelize } = require('sequelize'); |
|
||||
require('dotenv').config(); |
|
||||
|
|
||||
// 创建数据库连接
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
timezone: '+08:00' |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 添加department字段到usermanagements表
|
|
||||
async function addDepartmentColumn() { |
|
||||
try { |
|
||||
// 连接数据库
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 使用queryInterface添加字段
|
|
||||
await sequelize.getQueryInterface().addColumn('usermanagements', 'department', { |
|
||||
type: Sequelize.STRING(255), |
|
||||
defaultValue: null, |
|
||||
comment: '部门信息' |
|
||||
}); |
|
||||
|
|
||||
console.log('✅ 成功添加department字段到usermanagements表'); |
|
||||
} catch (error) { |
|
||||
console.error('❌ 添加字段失败:', error.message); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行函数
|
|
||||
addDepartmentColumn(); |
|
||||
@ -1,70 +0,0 @@ |
|||||
// 检查chat_online_status表的当前状态
|
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
|
|
||||
// 数据库配置 - 与server-mysql.js保持一致
|
|
||||
const sequelize = new Sequelize('chat', 'root', '', { |
|
||||
host: 'localhost', |
|
||||
port: 3306, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
define: { |
|
||||
freezeTableName: true, |
|
||||
timestamps: false |
|
||||
}, |
|
||||
logging: console.log |
|
||||
}); |
|
||||
|
|
||||
async function checkOnlineStatus() { |
|
||||
try { |
|
||||
console.log('正在连接数据库...'); |
|
||||
await sequelize.authenticate(); |
|
||||
console.log('数据库连接成功'); |
|
||||
|
|
||||
// 查询所有客服的在线状态
|
|
||||
console.log('\n查询chat_online_status表中所有客服(type=2)的记录:'); |
|
||||
const [managerStatuses] = await sequelize.query( |
|
||||
'SELECT * FROM chat_online_status WHERE type = 2 ORDER BY userId, type', |
|
||||
{ type: Sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (managerStatuses.length === 0) { |
|
||||
console.log('没有找到客服的在线状态记录'); |
|
||||
} else { |
|
||||
console.log(`找到 ${managerStatuses.length} 条客服在线状态记录:`); |
|
||||
managerStatuses.forEach(record => { |
|
||||
console.log(`- userId: ${record.userId || 'NULL'}, type: ${record.type}, is_online: ${record.is_online}, connection_id: ${record.connection_id}, last_heartbeat: ${record.last_heartbeat}, updated_at: ${record.updated_at}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 查询表中所有记录(包括用户和客服)以了解整体情况
|
|
||||
console.log('\n查询chat_online_status表中所有记录:'); |
|
||||
const [allStatuses] = await sequelize.query( |
|
||||
'SELECT * FROM chat_online_status ORDER BY type, userId', |
|
||||
{ type: Sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
console.log(`总共 ${allStatuses.length} 条记录`); |
|
||||
|
|
||||
// 检查是否存在userId为NULL的记录
|
|
||||
const nullUserIdRecords = allStatuses.filter(record => record.userId === null); |
|
||||
if (nullUserIdRecords.length > 0) { |
|
||||
console.log(`\n发现 ${nullUserIdRecords.length} 条userId为NULL的记录:`); |
|
||||
nullUserIdRecords.forEach(record => { |
|
||||
console.log(`- userId: NULL, type: ${record.type}, is_online: ${record.is_online}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('检查在线状态时出错:', error); |
|
||||
} finally { |
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行检查
|
|
||||
checkOnlineStatus(); |
|
||||
@ -1,133 +0,0 @@ |
|||||
// 查询用户入驻相关数据
|
|
||||
require('dotenv').config(); |
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
|
|
||||
// 创建数据库连接
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
timezone: '+08:00' |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
async function checkSettlementData() { |
|
||||
try { |
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功\n'); |
|
||||
|
|
||||
// 1. 查询所有用户的基本信息和入驻状态
|
|
||||
console.log('=== 用户入驻状态检查 ===\n'); |
|
||||
|
|
||||
const users = await sequelize.query(` |
|
||||
SELECT |
|
||||
id, userId, openid, name, phoneNumber, type, |
|
||||
collaborationid, cooperation, company, |
|
||||
province, city, district, detailedaddress, |
|
||||
businesslicenseurl, proofurl, brandurl, |
|
||||
partnerstatus, reasonforfailure, |
|
||||
created_at, updated_at |
|
||||
FROM users |
|
||||
ORDER BY updated_at DESC |
|
||||
LIMIT 50 |
|
||||
`, { type: Sequelize.QueryTypes.SELECT });
|
|
||||
|
|
||||
console.log(`找到 ${users.length} 个用户记录\n`); |
|
||||
|
|
||||
// 2. 按入驻状态分组统计
|
|
||||
const statusCount = {}; |
|
||||
users.forEach(u => { |
|
||||
const status = u.partnerstatus || '未申请'; |
|
||||
statusCount[status] = (statusCount[status] || 0) + 1; |
|
||||
}); |
|
||||
|
|
||||
console.log('📊 入驻状态统计:'); |
|
||||
Object.entries(statusCount).forEach(([status, count]) => { |
|
||||
console.log(` ${status}: ${count} 人`); |
|
||||
}); |
|
||||
console.log(); |
|
||||
|
|
||||
// 3. 显示每个用户的详细信息
|
|
||||
console.log('👥 用户详细信息:'); |
|
||||
console.log('='.repeat(100)); |
|
||||
|
|
||||
users.forEach((user, index) => { |
|
||||
console.log(`\n【用户 ${index + 1}】`); |
|
||||
console.log(` ID: ${user.id}`); |
|
||||
console.log(` userId: ${user.userId}`); |
|
||||
console.log(` openid: ${user.openid ? user.openid.substring(0, 20) + '...' : '空'}`); |
|
||||
console.log(` 姓名: ${user.name || '空'}`); |
|
||||
console.log(` 手机号: ${user.phoneNumber || '空'}`); |
|
||||
console.log(` 用户类型: ${user.type || '空'}`); |
|
||||
console.log(` ───── 入驻信息 ─────`); |
|
||||
console.log(` 合作商身份(collaborationid): ${user.collaborationid || '空'}`); |
|
||||
console.log(` 合作模式(cooperation): ${user.cooperation || '空'}`); |
|
||||
console.log(` 公司名称(company): ${user.company || '空'}`); |
|
||||
console.log(` 省份: ${user.province || '空'}`); |
|
||||
console.log(` 城市: ${user.city || '空'}`); |
|
||||
console.log(` 区县: ${user.district || '空'}`); |
|
||||
console.log(` 详细地址: ${user.detailedaddress || '空'}`); |
|
||||
console.log(` 营业执照: ${user.businesslicenseurl ? '已上传' : '空'}`); |
|
||||
console.log(` 证明材料: ${user.proofurl ? '已上传' : '空'}`); |
|
||||
console.log(` 品牌授权: ${user.brandurl ? '已上传' : '空'}`); |
|
||||
console.log(` ───── 审核状态 ─────`); |
|
||||
console.log(` 入驻状态(partnerstatus): ${user.partnerstatus || '空'}`); |
|
||||
console.log(` 失败原因: ${user.reasonforfailure || '空'}`); |
|
||||
console.log(` 创建时间: ${user.created_at}`); |
|
||||
console.log(` 更新时间: ${user.updated_at}`); |
|
||||
}); |
|
||||
|
|
||||
// 4. 特别检查有openid但partnerstatus为空的记录
|
|
||||
console.log('\n\n=== 重点检查:已登录但未提交入驻的用户 ===\n'); |
|
||||
|
|
||||
const notApplied = users.filter(u => u.openid && !u.partnerstatus); |
|
||||
console.log(`有 ${notApplied.length} 个用户已登录但未提交入驻申请`); |
|
||||
|
|
||||
if (notApplied.length > 0) { |
|
||||
console.log('\n这些用户的openid:'); |
|
||||
notApplied.forEach(u => { |
|
||||
console.log(` - userId: ${u.userId}, openid: ${u.openid.substring(0, 30)}...`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 5. 检查是否有审核中的用户
|
|
||||
console.log('\n\n=== 审核中的用户 ===\n'); |
|
||||
const underReview = users.filter(u => u.partnerstatus === 'underreview'); |
|
||||
console.log(`有 ${underReview.length} 个用户正在审核中`); |
|
||||
|
|
||||
if (underReview.length > 0) { |
|
||||
underReview.forEach(u => { |
|
||||
console.log(` - userId: ${u.userId}, 公司: ${u.company || '空'}, 身份: ${u.collaborationid || '空'}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 6. 检查users表的字段结构
|
|
||||
console.log('\n\n=== users表字段结构 ===\n'); |
|
||||
const tableStructure = await sequelize.query('DESCRIBE users', { type: Sequelize.QueryTypes.SELECT }); |
|
||||
const settlementFields = [ |
|
||||
'collaborationid', 'cooperation', 'company', 'province', 'city', 'district', |
|
||||
'detailedaddress', 'businesslicenseurl', 'proofurl', 'brandurl', |
|
||||
'partnerstatus', 'reasonforfailure' |
|
||||
]; |
|
||||
|
|
||||
console.log('入驻相关字段:'); |
|
||||
tableStructure.forEach(field => { |
|
||||
if (settlementFields.includes(field.Field)) { |
|
||||
console.log(` ${field.Field}: ${field.Type} ${field.Null === 'NO' ? 'NOT NULL' : ''} Default: ${field.Default || '无'}`); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 查询失败:', error.message); |
|
||||
console.error(error.stack); |
|
||||
} finally { |
|
||||
await sequelize.close(); |
|
||||
console.log('\n✅ 查询完成'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
checkSettlementData(); |
|
||||
@ -1,107 +0,0 @@ |
|||||
// 清理临时userId数据脚本
|
|
||||
// 此脚本用于删除chat_conversations和chat_messages表中所有临时userId的数据
|
|
||||
// 临时userId格式:temp_1765852836234
|
|
||||
|
|
||||
const mysql = require('mysql2/promise'); |
|
||||
|
|
||||
async function cleanupTempUserIds() { |
|
||||
let connection; |
|
||||
|
|
||||
try { |
|
||||
// 创建数据库连接
|
|
||||
connection = await mysql.createConnection({ |
|
||||
host: '1.95.162.61', |
|
||||
port: 3306, |
|
||||
user: 'root', |
|
||||
password: 'schl@2025', |
|
||||
database: 'wechat_app' |
|
||||
}); |
|
||||
|
|
||||
console.log('✓ 数据库连接成功'); |
|
||||
|
|
||||
// ============== 清理chat_conversations表中的临时userId数据 ==============
|
|
||||
console.log('\n=== 清理chat_conversations表中的临时userId数据 ==='); |
|
||||
|
|
||||
// 1. 统计需要删除的临时userId会话记录
|
|
||||
const [convTempCount] = await connection.execute( |
|
||||
'SELECT COUNT(*) as count FROM chat_conversations WHERE userId LIKE ? OR managerId LIKE ?', |
|
||||
['temp_%', 'temp_%'] |
|
||||
); |
|
||||
console.log(`找到 ${convTempCount[0].count} 条临时userId的会话记录`); |
|
||||
|
|
||||
// 2. 删除临时userId的会话记录
|
|
||||
if (convTempCount[0].count > 0) { |
|
||||
const [convResult] = await connection.execute( |
|
||||
'DELETE FROM chat_conversations WHERE userId LIKE ? OR managerId LIKE ?', |
|
||||
['temp_%', 'temp_%'] |
|
||||
); |
|
||||
console.log(`成功删除chat_conversations表中的 ${convResult.affectedRows} 条临时userId记录`); |
|
||||
} |
|
||||
|
|
||||
// ============== 清理chat_messages表中的临时userId数据 ==============
|
|
||||
console.log('\n=== 清理chat_messages表中的临时userId数据 ==='); |
|
||||
|
|
||||
// 1. 统计需要删除的临时userId消息记录
|
|
||||
const [msgTempCount] = await connection.execute( |
|
||||
'SELECT COUNT(*) as count FROM chat_messages WHERE sender_id LIKE ? OR receiver_id LIKE ?', |
|
||||
['temp_%', 'temp_%'] |
|
||||
); |
|
||||
console.log(`找到 ${msgTempCount[0].count} 条临时userId的消息记录`); |
|
||||
|
|
||||
// 2. 删除临时userId的消息记录
|
|
||||
if (msgTempCount[0].count > 0) { |
|
||||
const [msgResult] = await connection.execute( |
|
||||
'DELETE FROM chat_messages WHERE sender_id LIKE ? OR receiver_id LIKE ?', |
|
||||
['temp_%', 'temp_%'] |
|
||||
); |
|
||||
console.log(`成功删除chat_messages表中的 ${msgResult.affectedRows} 条临时userId记录`); |
|
||||
} |
|
||||
|
|
||||
// ============== 验证清理结果 ==============
|
|
||||
console.log('\n=== 验证清理结果 ==='); |
|
||||
|
|
||||
// 再次检查chat_conversations表中是否还有临时userId记录
|
|
||||
const [convVerify] = await connection.execute( |
|
||||
'SELECT COUNT(*) as count FROM chat_conversations WHERE userId LIKE ? OR managerId LIKE ?', |
|
||||
['temp_%', 'temp_%'] |
|
||||
); |
|
||||
console.log(`chat_conversations表中剩余临时userId记录数: ${convVerify[0].count}`); |
|
||||
|
|
||||
// 再次检查chat_messages表中是否还有临时userId记录
|
|
||||
const [msgVerify] = await connection.execute( |
|
||||
'SELECT COUNT(*) as count FROM chat_messages WHERE sender_id LIKE ? OR receiver_id LIKE ?', |
|
||||
['temp_%', 'temp_%'] |
|
||||
); |
|
||||
console.log(`chat_messages表中剩余临时userId记录数: ${msgVerify[0].count}`); |
|
||||
|
|
||||
// ============== 检查剩余的有效数据 ==============
|
|
||||
console.log('\n=== 检查剩余的有效数据 ==='); |
|
||||
|
|
||||
// 检查chat_conversations表中的有效记录数
|
|
||||
const [convValid] = await connection.execute( |
|
||||
'SELECT COUNT(*) as count FROM chat_conversations' |
|
||||
); |
|
||||
console.log(`chat_conversations表中有效会话记录数: ${convValid[0].count}`); |
|
||||
|
|
||||
// 检查chat_messages表中的有效记录数
|
|
||||
const [msgValid] = await connection.execute( |
|
||||
'SELECT COUNT(*) as count FROM chat_messages' |
|
||||
); |
|
||||
console.log(`chat_messages表中有效消息记录数: ${msgValid[0].count}`); |
|
||||
|
|
||||
console.log('\n✅ 清理完成!所有临时userId数据已被删除。'); |
|
||||
console.log('✅ 现在数据库中只保留了真实userId的数据。'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 清理过程中发生错误:', error); |
|
||||
} finally { |
|
||||
if (connection) { |
|
||||
await connection.end(); |
|
||||
console.log('✅ 数据库连接已关闭'); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行清理操作
|
|
||||
console.log('========== 开始清理临时userId数据 =========='); |
|
||||
cleanupTempUserIds().catch(console.error); |
|
||||
@ -1,29 +0,0 @@ |
|||||
const { Sequelize } = require('sequelize'); |
|
||||
const sequelize = new Sequelize('wechat_app', 'root', 'schl@2025', { |
|
||||
host: '1.95.162.61', |
|
||||
port: 3306, |
|
||||
dialect: 'mysql', |
|
||||
logging: false |
|
||||
}); |
|
||||
|
|
||||
async function cleanup() { |
|
||||
try { |
|
||||
// 删除测试会话相关的消息
|
|
||||
await sequelize.query('DELETE FROM chat_messages WHERE conversation_id = ?', { |
|
||||
replacements: ['conv_1765767582602'] |
|
||||
}); |
|
||||
|
|
||||
// 删除测试会话
|
|
||||
await sequelize.query('DELETE FROM chat_conversations WHERE conversation_id = ?', { |
|
||||
replacements: ['conv_1765767582602'] |
|
||||
}); |
|
||||
|
|
||||
console.log('测试会话记录已删除'); |
|
||||
} catch (e) { |
|
||||
console.error('删除错误:', e.message); |
|
||||
} finally { |
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
cleanup(); |
|
||||
@ -1,143 +0,0 @@ |
|||||
// 毛重字段(grossWeight)处理逻辑完整修复脚本
|
|
||||
// 此脚本用于统一所有API接口对grossWeight字段的处理逻辑
|
|
||||
|
|
||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 定义配置
|
|
||||
const config = { |
|
||||
serverFilePath: path.join(__dirname, 'server-mysql.js'), |
|
||||
backupFilePath: path.join(__dirname, 'server-mysql.js.bak.final-fix-' + Date.now()), |
|
||||
logFilePath: path.join(__dirname, 'final-fix-gross-weight-log.txt') |
|
||||
}; |
|
||||
|
|
||||
// 日志函数
|
|
||||
function log(message) { |
|
||||
const timestamp = new Date().toISOString(); |
|
||||
const logMessage = '[' + timestamp + '] ' + message; |
|
||||
console.log(logMessage); |
|
||||
try { |
|
||||
fs.appendFileSync(config.logFilePath, logMessage + '\n'); |
|
||||
} catch (e) { |
|
||||
console.error('写入日志文件失败:', e.message); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 读取文件内容
|
|
||||
function readFile(filePath) { |
|
||||
try { |
|
||||
return fs.readFileSync(filePath, 'utf8'); |
|
||||
} catch (error) { |
|
||||
log('读取文件失败: ' + error.message); |
|
||||
throw error; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 写入文件内容
|
|
||||
function writeFile(filePath, content) { |
|
||||
try { |
|
||||
fs.writeFileSync(filePath, content, 'utf8'); |
|
||||
log('文件已成功写入: ' + filePath); |
|
||||
} catch (error) { |
|
||||
log('写入文件失败: ' + error.message); |
|
||||
throw error; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 创建备份文件
|
|
||||
function createBackup() { |
|
||||
try { |
|
||||
const content = readFile(config.serverFilePath); |
|
||||
writeFile(config.backupFilePath, content); |
|
||||
log('已创建备份文件: ' + config.backupFilePath); |
|
||||
} catch (error) { |
|
||||
log('创建备份文件失败: ' + error.message); |
|
||||
throw error; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 主函数
|
|
||||
function main() { |
|
||||
log('===== 开始执行毛重字段处理逻辑完整修复 ====='); |
|
||||
|
|
||||
try { |
|
||||
// 创建备份
|
|
||||
createBackup(); |
|
||||
|
|
||||
// 读取文件内容
|
|
||||
let content = readFile(config.serverFilePath); |
|
||||
|
|
||||
// 修复1: 统一中间件中的毛重处理逻辑,确保所有空值都设为5
|
|
||||
const searchPatterns = [ |
|
||||
'product.grossWeight = 0;', |
|
||||
'product.grossWeight = 0; // 空值设置为0' |
|
||||
]; |
|
||||
|
|
||||
let fixesApplied = 0; |
|
||||
searchPatterns.forEach(pattern => { |
|
||||
if (content.includes(pattern)) { |
|
||||
const originalCount = (content.match(new RegExp(pattern, 'g')) || []).length; |
|
||||
content = content.replace(new RegExp(pattern, 'g'), 'product.grossWeight = 5; // 空值设置为5'); |
|
||||
const fixedCount = (content.match(/product\.grossWeight = 5;/g) || []).length - originalCount; |
|
||||
fixesApplied += fixedCount; |
|
||||
log('修复中间件中的毛重默认值: 替换了' + fixedCount + '处'); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
if (fixesApplied > 0) { |
|
||||
log('修复1完成: 已统一所有中间件中的毛重默认值为5'); |
|
||||
} else { |
|
||||
log('修复1跳过: 所有中间件中的毛重默认值已经是5'); |
|
||||
} |
|
||||
|
|
||||
// 修复2: 在商品上传接口添加毛重处理逻辑
|
|
||||
const uploadApiSearch = 'app.post(\'/api/products/upload\', async (req, res) => {'; |
|
||||
if (content.includes(uploadApiSearch)) { |
|
||||
// 查找上传接口的位置
|
|
||||
const uploadApiStart = content.indexOf(uploadApiSearch); |
|
||||
const uploadApiEnd = content.indexOf('});', uploadApiStart) + 3; |
|
||||
const uploadApiContent = content.substring(uploadApiStart, uploadApiEnd); |
|
||||
|
|
||||
// 检查是否已经包含毛重处理逻辑
|
|
||||
if (uploadApiContent.includes('grossWeight') && uploadApiContent.includes('parseFloat')) { |
|
||||
log('修复2跳过: 商品上传接口已经包含毛重处理逻辑'); |
|
||||
} else { |
|
||||
// 查找商品数据处理的位置(在try块内)
|
|
||||
const tryBlockStart = uploadApiContent.indexOf('try {'); |
|
||||
const tryBlockEnd = uploadApiContent.lastIndexOf('} catch'); |
|
||||
|
|
||||
if (tryBlockStart !== -1 && tryBlockEnd !== -1) { |
|
||||
// 在try块开始处添加毛重处理逻辑
|
|
||||
const tryBlockContent = uploadApiContent.substring(tryBlockStart, tryBlockEnd); |
|
||||
const weightHandlingCode = `try {\n // 修复毛重字段处理逻辑\n if (req.body && req.body.productData) {\n let processedGrossWeight = 5; // 默认值为5\n if (req.body.productData.grossWeight !== null && req.body.productData.grossWeight !== undefined && req.body.productData.grossWeight !== \'\') {\n const numValue = parseFloat(req.body.productData.grossWeight);\n if (!isNaN(numValue) && isFinite(numValue)) {\n processedGrossWeight = numValue;\n }\n }\n req.body.productData.grossWeight = processedGrossWeight;\n console.log(\'修复后 - 毛重值处理: 原始值=\' + (req.body.productData.grossWeight || \'undefined\') + ', 处理后=\' + processedGrossWeight);\n }`; |
|
||||
|
|
||||
// 替换原代码
|
|
||||
const fixedUploadApiContent = uploadApiContent.replace(tryBlockContent, weightHandlingCode); |
|
||||
content = content.replace(uploadApiContent, fixedUploadApiContent); |
|
||||
log('修复2完成: 在商品上传接口添加了毛重处理逻辑'); |
|
||||
} else { |
|
||||
log('修复2失败: 无法在商品上传接口中找到try-catch块'); |
|
||||
} |
|
||||
} |
|
||||
} else { |
|
||||
log('修复2跳过: 未找到商品上传接口'); |
|
||||
} |
|
||||
|
|
||||
// 写入修复后的内容
|
|
||||
writeFile(config.serverFilePath, content); |
|
||||
|
|
||||
log('===== 毛重字段处理逻辑完整修复完成 ====='); |
|
||||
log('修复内容总结:'); |
|
||||
log('1. 统一了所有中间件中的毛重默认值为5'); |
|
||||
log('2. 在商品上传接口中添加了毛重处理逻辑,将空值设为5,有效数字转换为float类型'); |
|
||||
log('3. 创建了备份文件,以便需要时恢复'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
log('修复过程中发生错误: ' + error.message); |
|
||||
log('===== 毛重字段处理逻辑完整修复失败 ====='); |
|
||||
process.exit(1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行主函数
|
|
||||
main(); |
|
||||
@ -1,123 +0,0 @@ |
|||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 读取server-mysql.js文件内容
|
|
||||
function readServerFile() { |
|
||||
return fs.readFileSync(path.join(__dirname, 'server-mysql.js'), 'utf8'); |
|
||||
} |
|
||||
|
|
||||
// 验证毛重字段处理逻辑
|
|
||||
function verifyGrossWeightHandling() { |
|
||||
try { |
|
||||
const fileContent = readServerFile(); |
|
||||
|
|
||||
// 初始化验证结果
|
|
||||
const verificationResult = { |
|
||||
totalIssues: 0, |
|
||||
successPoints: 0, |
|
||||
issues: [], |
|
||||
successDetails: [] |
|
||||
}; |
|
||||
|
|
||||
// 检查响应中间件中的/products/list接口
|
|
||||
const listMiddlewarePattern = /data\.products\s*=\s*data\.products\.map\(product\s*=>\s*\{[\s\S]*?product\.grossWeight\s*=\s*([^;]+);/; |
|
||||
const listMiddlewareMatch = fileContent.match(listMiddlewarePattern); |
|
||||
|
|
||||
if (listMiddlewareMatch && listMiddlewareMatch[1].includes('0')) { |
|
||||
verificationResult.successPoints++; |
|
||||
verificationResult.successDetails.push('✓ 响应中间件(/products/list)已正确设置空毛重默认值为0'); |
|
||||
} else { |
|
||||
verificationResult.totalIssues++; |
|
||||
verificationResult.issues.push('✗ 响应中间件(/products/list)未正确设置空毛重默认值'); |
|
||||
} |
|
||||
|
|
||||
// 检查响应中间件中的/data接口
|
|
||||
const dataMiddlewarePattern = /data\.data\.products\s*=\s*data\.data\.products\.map\(product\s*=>\s*\{[\s\S]*?product\.grossWeight\s*=\s*([^;]+);/; |
|
||||
const dataMiddlewareMatch = fileContent.match(dataMiddlewarePattern); |
|
||||
|
|
||||
if (dataMiddlewareMatch && dataMiddlewareMatch[1].includes('0')) { |
|
||||
verificationResult.successPoints++; |
|
||||
verificationResult.successDetails.push('✓ 响应中间件(/data)已正确设置空毛重默认值为0'); |
|
||||
} else { |
|
||||
verificationResult.totalIssues++; |
|
||||
verificationResult.issues.push('✗ 响应中间件(/data)未正确设置空毛重默认值'); |
|
||||
} |
|
||||
|
|
||||
// 检查商品上传接口
|
|
||||
const uploadApiPattern = /app\.post\('\/api\/products\/upload',[\s\S]*?let\s+processedGrossWeight\s*=\s*(\d+)/; |
|
||||
const uploadApiMatch = fileContent.match(uploadApiPattern); |
|
||||
|
|
||||
if (uploadApiMatch && uploadApiMatch[1] === '0') { |
|
||||
verificationResult.successPoints++; |
|
||||
verificationResult.successDetails.push('✓ 商品上传接口已正确设置空毛重默认值为0'); |
|
||||
} else { |
|
||||
verificationResult.totalIssues++; |
|
||||
verificationResult.issues.push('✗ 商品上传接口未正确设置空毛重默认值'); |
|
||||
} |
|
||||
|
|
||||
// 检查编辑商品API
|
|
||||
const editApiPattern = /parsedValue:\s*product\.grossWeight\s*===\s*''\s*\|\|\s*product\.grossWeight\s*===\s*null\s*\|\|\s*product\.grossWeight\s*===\s*undefined\s*\?\s*(\d+)/; |
|
||||
const editApiMatch = fileContent.match(editApiPattern); |
|
||||
|
|
||||
if (editApiMatch && editApiMatch[1] === '0') { |
|
||||
verificationResult.successPoints++; |
|
||||
verificationResult.successDetails.push('✓ 编辑商品API已正确设置空毛重默认值为0'); |
|
||||
} else { |
|
||||
verificationResult.totalIssues++; |
|
||||
verificationResult.issues.push('✗ 编辑商品API未正确设置空毛重默认值'); |
|
||||
} |
|
||||
|
|
||||
// 检查是否还有设置为5的地方
|
|
||||
const remaining5Pattern = /grossWeight\s*=\s*5/g; |
|
||||
const remaining5Matches = fileContent.match(remaining5Pattern); |
|
||||
|
|
||||
if (remaining5Matches && remaining5Matches.length > 0) { |
|
||||
verificationResult.totalIssues += remaining5Matches.length; |
|
||||
verificationResult.issues.push(`✗ 发现${remaining5Matches.length}处仍将毛重设置为5的地方`); |
|
||||
} else { |
|
||||
verificationResult.successPoints++; |
|
||||
verificationResult.successDetails.push('✓ 未发现仍将毛重设置为5的残留代码'); |
|
||||
} |
|
||||
|
|
||||
// 检查是否正确实现了空值返回0的逻辑
|
|
||||
const emptyValueHandlingPattern = /product\.grossWeight\s*===\s*null\s*\|\|\s*product\.grossWeight\s*===\s*undefined\s*\|\|\s*product\.grossWeight\s*===\s*''\s*\?\s*0/g; |
|
||||
const emptyValueMatches = fileContent.match(emptyValueHandlingPattern); |
|
||||
|
|
||||
if (emptyValueMatches && emptyValueMatches.length > 0) { |
|
||||
verificationResult.successPoints++; |
|
||||
verificationResult.successDetails.push(`✓ 发现${emptyValueMatches.length}处正确实现了空值返回0的逻辑`); |
|
||||
} |
|
||||
|
|
||||
// 输出验证结果
|
|
||||
console.log('\n======== 毛重字段处理逻辑全面验证结果 ========'); |
|
||||
console.log('\n成功项:'); |
|
||||
verificationResult.successDetails.forEach(detail => console.log(detail)); |
|
||||
|
|
||||
console.log('\n问题项:'); |
|
||||
if (verificationResult.issues.length === 0) { |
|
||||
console.log('✓ 未发现任何问题'); |
|
||||
} else { |
|
||||
verificationResult.issues.forEach(issue => console.log(issue)); |
|
||||
} |
|
||||
|
|
||||
console.log('\n总体评估:'); |
|
||||
if (verificationResult.totalIssues === 0) { |
|
||||
console.log('✅ 验证成功: 所有毛重字段处理逻辑已正确实现'); |
|
||||
console.log(' 已满足要求: 空值时小程序和数据库均返回0,非空值返回实际值'); |
|
||||
} else { |
|
||||
console.log(`❌ 验证失败: 发现${verificationResult.totalIssues}个问题需要修复`); |
|
||||
} |
|
||||
|
|
||||
console.log('=============================================='); |
|
||||
|
|
||||
// 设置退出码
|
|
||||
process.exit(verificationResult.totalIssues > 0 ? 1 : 0); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('验证过程中发生错误:', error); |
|
||||
process.exit(1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行验证
|
|
||||
verifyGrossWeightHandling(); |
|
||||
@ -1,155 +0,0 @@ |
|||||
const { Sequelize } = require('sequelize'); |
|
||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 读取环境变量
|
|
||||
const envPath = path.join(__dirname, '.env'); |
|
||||
if (fs.existsSync(envPath)) { |
|
||||
const envContent = fs.readFileSync(envPath, 'utf8'); |
|
||||
const envVars = envContent.split('\n').filter(line => line.trim() && !line.startsWith('#')); |
|
||||
envVars.forEach(line => { |
|
||||
const [key, value] = line.split('=').map(part => part.trim()); |
|
||||
process.env[key] = value; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_NAME || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD || '', |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
logging: false, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 定义模型 - 简化版
|
|
||||
const User = sequelize.define('User', { |
|
||||
userId: { |
|
||||
type: sequelize.Sequelize.STRING(100), |
|
||||
primaryKey: true, |
|
||||
allowNull: false |
|
||||
}, |
|
||||
nickName: sequelize.Sequelize.STRING(100), |
|
||||
phoneNumber: sequelize.Sequelize.STRING(20) |
|
||||
}, { |
|
||||
tableName: 'users', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
const Contact = sequelize.define('Contact', { |
|
||||
id: { |
|
||||
type: sequelize.Sequelize.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: sequelize.Sequelize.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
nickName: sequelize.Sequelize.STRING(100), |
|
||||
phoneNumber: sequelize.Sequelize.STRING(20) |
|
||||
}, { |
|
||||
tableName: 'contacts', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
const UserManagement = sequelize.define('UserManagement', { |
|
||||
id: { |
|
||||
type: sequelize.Sequelize.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: sequelize.Sequelize.STRING(100), |
|
||||
allowNull: false |
|
||||
} |
|
||||
}, { |
|
||||
tableName: 'usermanagements', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// 修复函数
|
|
||||
async function fixMissingAssociations() { |
|
||||
try { |
|
||||
console.log('========================================'); |
|
||||
console.log('开始修复用户关联表记录'); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
// 连接数据库
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 获取所有用户
|
|
||||
const users = await User.findAll(); |
|
||||
console.log(`📊 共找到 ${users.length} 个用户记录`); |
|
||||
|
|
||||
let contactsCreated = 0; |
|
||||
let managementsCreated = 0; |
|
||||
|
|
||||
// 为每个用户检查并创建关联记录
|
|
||||
for (let i = 0; i < users.length; i++) { |
|
||||
const user = users[i]; |
|
||||
console.log(`\n🔄 处理用户 ${i + 1}/${users.length}: ${user.userId}`); |
|
||||
|
|
||||
// 检查并创建联系人记录
|
|
||||
try { |
|
||||
const existingContact = await Contact.findOne({ |
|
||||
where: { userId: user.userId } |
|
||||
}); |
|
||||
|
|
||||
if (!existingContact) { |
|
||||
await Contact.create({ |
|
||||
userId: user.userId, |
|
||||
nickName: user.nickName || '默认联系人', |
|
||||
phoneNumber: user.phoneNumber || '' |
|
||||
}); |
|
||||
console.log('✅ 创建了联系人记录'); |
|
||||
contactsCreated++; |
|
||||
} else { |
|
||||
console.log('✅ 联系人记录已存在'); |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.error('❌ 创建联系人记录失败:', error.message); |
|
||||
} |
|
||||
|
|
||||
// 检查并创建用户管理记录
|
|
||||
try { |
|
||||
const existingManagement = await UserManagement.findOne({ |
|
||||
where: { userId: user.userId } |
|
||||
}); |
|
||||
|
|
||||
if (!existingManagement) { |
|
||||
await UserManagement.create({ |
|
||||
userId: user.userId |
|
||||
}); |
|
||||
console.log('✅ 创建了用户管理记录'); |
|
||||
managementsCreated++; |
|
||||
} else { |
|
||||
console.log('✅ 用户管理记录已存在'); |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.error('❌ 创建用户管理记录失败:', error.message); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
console.log('\n========================================'); |
|
||||
console.log('修复完成!'); |
|
||||
console.log(`📈 共创建了 ${contactsCreated} 条联系人记录`); |
|
||||
console.log(`📈 共创建了 ${managementsCreated} 条用户管理记录`); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 修复过程中发生错误:', error); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 运行修复
|
|
||||
fixMissingAssociations(); |
|
||||
@ -1,356 +0,0 @@ |
|||||
// 注意:此文件是MongoDB版本的扩展实现,已被禁用
|
|
||||
// 数据库扩展 - 用于连接userlogin数据库并关联表
|
|
||||
const { Sequelize, DataTypes, Model } = require('sequelize'); |
|
||||
const path = require('path'); |
|
||||
require('dotenv').config({ path: path.resolve(__dirname, '.env') }); |
|
||||
|
|
||||
// 注意:不再直接导入User模型以避免循环依赖
|
|
||||
// User模型将通过setupAssociations函数的参数传入
|
|
||||
let User = null; |
|
||||
|
|
||||
// 创建到userlogin数据库的连接
|
|
||||
const sequelizeUserLogin = new Sequelize( |
|
||||
process.env.DB_DATABASE_USERLOGIN || 'userlogin', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 测试userlogin数据库连接
|
|
||||
async function testUserLoginDbConnection() { |
|
||||
try { |
|
||||
await sequelizeUserLogin.authenticate(); |
|
||||
console.log('userlogin数据库连接成功'); |
|
||||
} catch (error) { |
|
||||
console.error('userlogin数据库连接失败:', error); |
|
||||
console.error('请注意:如果不需要使用userlogin数据库,可以忽略此错误'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 定义userlogin数据库中的表模型
|
|
||||
|
|
||||
// contact表模型
|
|
||||
class Contact extends Model { } |
|
||||
Contact.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
name: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
phone: { |
|
||||
type: DataTypes.STRING(20), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
email: { |
|
||||
type: DataTypes.STRING(100) |
|
||||
}, |
|
||||
address: { |
|
||||
type: DataTypes.TEXT |
|
||||
}, |
|
||||
created_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize: sequelizeUserLogin, |
|
||||
modelName: 'Contact', |
|
||||
tableName: 'contact', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// enterprise表模型
|
|
||||
class Enterprise extends Model { } |
|
||||
Enterprise.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
enterpriseName: { |
|
||||
type: DataTypes.STRING(255), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
businessLicense: { |
|
||||
type: DataTypes.STRING(255) |
|
||||
}, |
|
||||
address: { |
|
||||
type: DataTypes.TEXT |
|
||||
}, |
|
||||
contactPerson: { |
|
||||
type: DataTypes.STRING(100) |
|
||||
}, |
|
||||
contactPhone: { |
|
||||
type: DataTypes.STRING(20) |
|
||||
}, |
|
||||
created_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize: sequelizeUserLogin, |
|
||||
modelName: 'Enterprise', |
|
||||
tableName: 'enterprise', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// managers表模型
|
|
||||
class Manager extends Model { } |
|
||||
Manager.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
managerName: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
managerPhone: { |
|
||||
type: DataTypes.STRING(20), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
role: { |
|
||||
type: DataTypes.STRING(50), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
created_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize: sequelizeUserLogin, |
|
||||
modelName: 'Manager', |
|
||||
tableName: 'managers', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// publicseademand表模型
|
|
||||
class PublicSeaDemand extends Model { } |
|
||||
PublicSeaDemand.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
demandType: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
description: { |
|
||||
type: DataTypes.TEXT |
|
||||
}, |
|
||||
status: { |
|
||||
type: DataTypes.STRING(50), |
|
||||
defaultValue: 'pending' |
|
||||
}, |
|
||||
created_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize: sequelizeUserLogin, |
|
||||
modelName: 'PublicSeaDemand', |
|
||||
tableName: 'publicseademand', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// rootdb表模型
|
|
||||
class RootDb extends Model { } |
|
||||
RootDb.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
dataKey: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
dataValue: { |
|
||||
type: DataTypes.TEXT |
|
||||
}, |
|
||||
created_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize: sequelizeUserLogin, |
|
||||
modelName: 'RootDb', |
|
||||
tableName: 'rootdb', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// login表模型
|
|
||||
class Login extends Model { } |
|
||||
Login.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
loginTime: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
}, |
|
||||
loginIp: { |
|
||||
type: DataTypes.STRING(50) |
|
||||
}, |
|
||||
deviceInfo: { |
|
||||
type: DataTypes.TEXT |
|
||||
}, |
|
||||
status: { |
|
||||
type: DataTypes.STRING(20), |
|
||||
defaultValue: 'success' |
|
||||
} |
|
||||
}, { |
|
||||
sequelize: sequelizeUserLogin, |
|
||||
modelName: 'Login', |
|
||||
tableName: 'login', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// 设置模型关联关系
|
|
||||
function setupAssociations(mainUserModel) { |
|
||||
// 确保使用传入的User模型,不再依赖默认的User变量
|
|
||||
if (!mainUserModel) { |
|
||||
console.error('User模型未提供,无法设置关联关系'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
try { |
|
||||
// 关联User与Contact(一对多)
|
|
||||
// 使用唯一的别名userContacts以避免可能的冲突
|
|
||||
mainUserModel.hasMany(Contact, { |
|
||||
foreignKey: 'userId', |
|
||||
sourceKey: 'userId', |
|
||||
as: 'userContacts' |
|
||||
}); |
|
||||
|
|
||||
// 反向关联Contact与User
|
|
||||
Contact.belongsTo(mainUserModel, { |
|
||||
foreignKey: 'userId', |
|
||||
targetKey: 'userId', |
|
||||
as: 'user' |
|
||||
}); |
|
||||
|
|
||||
// 关联User与Enterprise(一对多)
|
|
||||
mainUserModel.hasMany(Enterprise, { |
|
||||
foreignKey: 'userId', |
|
||||
sourceKey: 'userId', |
|
||||
as: 'userEnterprises' |
|
||||
}); |
|
||||
|
|
||||
// 反向关联Enterprise与User
|
|
||||
Enterprise.belongsTo(mainUserModel, { |
|
||||
foreignKey: 'userId', |
|
||||
targetKey: 'userId', |
|
||||
as: 'user' |
|
||||
}); |
|
||||
|
|
||||
// 关联User与Manager(一对多)
|
|
||||
mainUserModel.hasMany(Manager, { |
|
||||
foreignKey: 'userId', |
|
||||
sourceKey: 'userId', |
|
||||
as: 'userManagers' |
|
||||
}); |
|
||||
|
|
||||
// 反向关联Manager与User
|
|
||||
Manager.belongsTo(mainUserModel, { |
|
||||
foreignKey: 'userId', |
|
||||
targetKey: 'userId', |
|
||||
as: 'user' |
|
||||
}); |
|
||||
|
|
||||
// 关联User与PublicSeaDemand(一对多)
|
|
||||
mainUserModel.hasMany(PublicSeaDemand, { |
|
||||
foreignKey: 'userId', |
|
||||
sourceKey: 'userId', |
|
||||
as: 'userPublicSeaDemands' |
|
||||
}); |
|
||||
|
|
||||
// 反向关联PublicSeaDemand与User
|
|
||||
PublicSeaDemand.belongsTo(mainUserModel, { |
|
||||
foreignKey: 'userId', |
|
||||
targetKey: 'userId', |
|
||||
as: 'user' |
|
||||
}); |
|
||||
|
|
||||
// 关联User与RootDb(一对多)
|
|
||||
mainUserModel.hasMany(RootDb, { |
|
||||
foreignKey: 'userId', |
|
||||
sourceKey: 'userId', |
|
||||
as: 'userRootDbs' |
|
||||
}); |
|
||||
|
|
||||
// 反向关联RootDb与User
|
|
||||
RootDb.belongsTo(mainUserModel, { |
|
||||
foreignKey: 'userId', |
|
||||
targetKey: 'userId', |
|
||||
as: 'user' |
|
||||
}); |
|
||||
|
|
||||
// 关联User与Login(一对多)
|
|
||||
mainUserModel.hasMany(Login, { |
|
||||
foreignKey: 'userId', |
|
||||
sourceKey: 'userId', |
|
||||
as: 'userLoginRecords' |
|
||||
}); |
|
||||
|
|
||||
console.log('已设置wechat_app数据库的User模型与userlogin数据库表的关联关系'); |
|
||||
} catch (error) { |
|
||||
console.error('设置模型关联关系时出错:', error); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 导出所有模型和连接
|
|
||||
module.exports = { |
|
||||
sequelizeUserLogin, |
|
||||
testUserLoginDbConnection, |
|
||||
Contact, |
|
||||
Enterprise, |
|
||||
Manager, |
|
||||
PublicSeaDemand, |
|
||||
RootDb, |
|
||||
Login, |
|
||||
setupAssociations, |
|
||||
User // 导出User模型(可能是实际的模型或临时模型)
|
|
||||
}; |
|
||||
@ -1,116 +0,0 @@ |
|||||
// WebSocket调试脚本 - 专注于连接和认证
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
// 服务器地址
|
|
||||
const SERVER_URL = 'ws://localhost:3003'; |
|
||||
const customerServiceId = '22'; // 刘杨的ID
|
|
||||
|
|
||||
console.log('=== WebSocket认证详细调试 ==='); |
|
||||
console.log(`连接服务器: ${SERVER_URL}`); |
|
||||
|
|
||||
// 创建WebSocket连接
|
|
||||
const ws = new WebSocket(SERVER_URL, { |
|
||||
perMessageDeflate: false, |
|
||||
headers: { |
|
||||
'User-Agent': 'Debug-Client', |
|
||||
'Connection': 'Upgrade' |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 连接事件
|
|
||||
ws.on('open', () => { |
|
||||
console.log('✅ WebSocket连接已建立'); |
|
||||
|
|
||||
// 延迟100ms发送认证消息,确保连接完全就绪
|
|
||||
setTimeout(() => { |
|
||||
// 使用正确的认证格式 - 必须有type: 'auth'
|
|
||||
const authMessage = { |
|
||||
type: 'auth', // 必须是'auth'才能被服务器识别为认证消息
|
|
||||
managerId: customerServiceId, |
|
||||
userType: 'manager' // 用户类型使用不同的字段名
|
|
||||
}; |
|
||||
|
|
||||
const messageString = JSON.stringify(authMessage); |
|
||||
console.log('📤 发送认证消息:', messageString); |
|
||||
console.log(' 消息长度:', messageString.length, '字节'); |
|
||||
|
|
||||
try { |
|
||||
const sent = ws.send(messageString); |
|
||||
console.log(' 发送结果:', sent ? '成功放入发送队列' : '发送失败'); |
|
||||
} catch (e) { |
|
||||
console.error(' 发送时异常:', e.message); |
|
||||
} |
|
||||
}, 100); |
|
||||
}); |
|
||||
|
|
||||
// 接收消息事件
|
|
||||
ws.on('message', (data) => { |
|
||||
console.log('📥 收到服务器消息:'); |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log(' 消息内容:', JSON.stringify(message, null, 2)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('🎉 认证成功!'); |
|
||||
} else if (message.type === 'auth_error') { |
|
||||
console.log('❌ 认证失败:', message.message); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error(' 解析消息失败:', e.message); |
|
||||
console.log(' 原始消息:', data.toString()); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 关闭事件
|
|
||||
ws.on('close', (code, reason) => { |
|
||||
console.log('❌ WebSocket连接已关闭'); |
|
||||
console.log(' 关闭代码:', code); |
|
||||
console.log(' 关闭原因:', reason.toString()); |
|
||||
|
|
||||
// 常见关闭代码说明
|
|
||||
if (code === 1000) console.log(' 说明: 正常关闭'); |
|
||||
if (code === 1001) console.log(' 说明: 终端离开'); |
|
||||
if (code === 1006) console.log(' 说明: 连接意外关闭'); |
|
||||
if (code === 1011) console.log(' 说明: 服务器内部错误'); |
|
||||
}); |
|
||||
|
|
||||
// 错误事件
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('❌ WebSocket错误:'); |
|
||||
console.error(' 错误类型:', error.name); |
|
||||
console.error(' 错误消息:', error.message); |
|
||||
console.error(' 错误堆栈:', error.stack); |
|
||||
}); |
|
||||
|
|
||||
// 发送缓冲区事件
|
|
||||
ws.on('drain', () => { |
|
||||
console.log('🗑️ 发送缓冲区已清空'); |
|
||||
}); |
|
||||
|
|
||||
// 连接超时处理
|
|
||||
setTimeout(() => { |
|
||||
if (ws.readyState === WebSocket.OPEN) { |
|
||||
console.log('⏰ 10秒超时,关闭连接'); |
|
||||
ws.close(); |
|
||||
} |
|
||||
}, 10000); |
|
||||
|
|
||||
// 定期检查连接状态
|
|
||||
let checkInterval = setInterval(() => { |
|
||||
const state = { |
|
||||
0: 'CONNECTING', |
|
||||
1: 'OPEN', |
|
||||
2: 'CLOSING', |
|
||||
3: 'CLOSED' |
|
||||
}[ws.readyState]; |
|
||||
|
|
||||
console.log(`🔄 连接状态: ${state}`); |
|
||||
|
|
||||
if (ws.readyState === WebSocket.CLOSED) { |
|
||||
clearInterval(checkInterval); |
|
||||
console.log('\n=== 调试结束 ==='); |
|
||||
} |
|
||||
}, 1000); |
|
||||
|
|
||||
console.log('=== 开始调试 ==='); |
|
||||
console.log('按Ctrl+C停止调试'); |
|
||||
@ -1,193 +0,0 @@ |
|||||
// 完整聊天流程调试脚本
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
const WS_URL = 'ws://localhost:3003'; |
|
||||
const TEST_MANAGER_ID = '22'; |
|
||||
const TEST_CUSTOMER_ID = 'test_customer_1'; |
|
||||
let customerWs = null; |
|
||||
let managerWs = null; |
|
||||
let currentConversationId = null; |
|
||||
|
|
||||
// 启动调试
|
|
||||
async function startDebug() { |
|
||||
console.log('=== 启动完整聊天流程调试 ==='); |
|
||||
|
|
||||
try { |
|
||||
// 连接客服WebSocket
|
|
||||
await connectManager(); |
|
||||
|
|
||||
// 连接客户WebSocket
|
|
||||
await connectCustomer(); |
|
||||
|
|
||||
// 等待连接稳定
|
|
||||
await new Promise(resolve => setTimeout(resolve, 2000)); |
|
||||
|
|
||||
// 客户发送消息
|
|
||||
await customerSendMessage(); |
|
||||
|
|
||||
// 等待客服收到消息
|
|
||||
await new Promise(resolve => setTimeout(resolve, 3000)); |
|
||||
|
|
||||
// 如果会话ID有效,客服回复消息
|
|
||||
if (currentConversationId) { |
|
||||
await managerReplyMessage(currentConversationId); |
|
||||
} |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 调试过程中出现错误:', error); |
|
||||
} finally { |
|
||||
console.log('=== 调试结束 ==='); |
|
||||
if (customerWs && customerWs.readyState === WebSocket.OPEN) { |
|
||||
customerWs.close(); |
|
||||
} |
|
||||
if (managerWs && managerWs.readyState === WebSocket.OPEN) { |
|
||||
managerWs.close(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 连接客服WebSocket
|
|
||||
function connectManager() { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
managerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
managerWs.on('open', () => { |
|
||||
console.log('✅ 客服WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
userType: 'manager' |
|
||||
}); |
|
||||
managerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客服收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客服认证成功'); |
|
||||
resolve(); |
|
||||
} else if (message.type === 'new_message' && message.payload) { |
|
||||
// 记录会话ID
|
|
||||
if (message.payload.conversationId) { |
|
||||
currentConversationId = message.payload.conversationId; |
|
||||
console.log(`📝 获取到会话ID: ${currentConversationId}`); |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('error', (error) => { |
|
||||
console.error('❌ 客服WebSocket错误:', error); |
|
||||
reject(error); |
|
||||
}); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
reject(new Error('客服连接或认证超时')); |
|
||||
}, 5000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 连接客户WebSocket
|
|
||||
function connectCustomer() { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
customerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
customerWs.on('open', () => { |
|
||||
console.log('✅ 客户WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
userId: TEST_CUSTOMER_ID, |
|
||||
userType: 'user' |
|
||||
}); |
|
||||
customerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
customerWs.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客户收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客户认证成功'); |
|
||||
resolve(); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
customerWs.on('error', (error) => { |
|
||||
console.error('❌ 客户WebSocket错误:', error); |
|
||||
reject(error); |
|
||||
}); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
reject(new Error('客户连接或认证超时')); |
|
||||
}, 5000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 客户发送消息
|
|
||||
function customerSendMessage() { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
const messageId = 'test_customer_' + Date.now(); |
|
||||
const message = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: messageId, |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
content: '你好,我是测试客户,有问题咨询', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('📤 客户发送消息:', JSON.stringify(message)); |
|
||||
customerWs.send(JSON.stringify(message)); |
|
||||
|
|
||||
// 等待发送确认
|
|
||||
const messageHandler = (data) => { |
|
||||
const response = JSON.parse(data.toString()); |
|
||||
if (response.type === 'message_sent' && response.payload.messageId === messageId) { |
|
||||
customerWs.off('message', messageHandler); |
|
||||
console.log('✅ 客户消息发送成功'); |
|
||||
resolve(); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
customerWs.on('message', messageHandler); |
|
||||
|
|
||||
setTimeout(() => { |
|
||||
customerWs.off('message', messageHandler); |
|
||||
reject(new Error('客户消息发送超时')); |
|
||||
}, 5000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 客服回复消息
|
|
||||
function managerReplyMessage(conversationId) { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
const messageId = 'test_manager_' + Date.now(); |
|
||||
// 尝试使用更简单的格式,只包含最基本的字段
|
|
||||
// 参考客户发送消息的格式,但使用conversationId而不是managerId
|
|
||||
const replyMessage = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
content: '您好,我是客服,请问有什么可以帮助您的?', // 必须字段
|
|
||||
conversationId: conversationId, // 必须字段,确定会话
|
|
||||
contentType: 1 // 必须字段
|
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('📤 客服发送回复消息:', JSON.stringify(replyMessage)); |
|
||||
managerWs.send(JSON.stringify(replyMessage)); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
resolve(); |
|
||||
}, 3000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 启动调试
|
|
||||
startDebug(); |
|
||||
@ -1,112 +0,0 @@ |
|||||
// 最终调试脚本
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
const WS_URL = 'ws://localhost:3003'; |
|
||||
const TEST_MANAGER_ID = '22'; |
|
||||
const TEST_CONVERSATION_ID = '4fa4b92f-df20-40ae-94b9-f906753a4cfd'; |
|
||||
let managerWs = null; |
|
||||
let startTime = null; |
|
||||
|
|
||||
console.log('=== 启动最终调试 ==='); |
|
||||
console.log(`测试会话ID: ${TEST_CONVERSATION_ID}`); |
|
||||
|
|
||||
// 连接客服WebSocket
|
|
||||
managerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
managerWs.on('open', () => { |
|
||||
console.log('✅ 客服WebSocket连接已建立'); |
|
||||
startTime = Date.now(); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
userType: 'manager' |
|
||||
}); |
|
||||
console.log('📤 客服发送认证消息:', authMsg); |
|
||||
managerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log(`📥 客服收到消息 (${Date.now() - startTime}ms):`, JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客服认证成功'); |
|
||||
|
|
||||
// 等待1秒后发送测试消息
|
|
||||
setTimeout(() => { |
|
||||
sendTestMessage(); |
|
||||
}, 1000); |
|
||||
} |
|
||||
|
|
||||
if (message.type === 'error') { |
|
||||
console.error('❌ 接收到错误消息:', message.message); |
|
||||
|
|
||||
// 如果收到错误,尝试发送替代格式
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n🔄 尝试替代格式...'); |
|
||||
sendAlternativeFormat(); |
|
||||
}, 2000); |
|
||||
} |
|
||||
|
|
||||
if (message.type === 'message_sent') { |
|
||||
console.log('✅ 消息发送成功!'); |
|
||||
|
|
||||
// 5秒后关闭连接
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n=== 调试成功完成 ==='); |
|
||||
managerWs.close(); |
|
||||
}, 5000); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('error', (error) => { |
|
||||
console.error('❌ 客服WebSocket错误:', error); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('close', () => { |
|
||||
console.log('❌ 客服WebSocket连接已关闭'); |
|
||||
}); |
|
||||
|
|
||||
// 发送测试消息
|
|
||||
function sendTestMessage() { |
|
||||
const messageId = 'test_manager_' + Date.now(); |
|
||||
const testMessage = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: messageId, |
|
||||
conversationId: TEST_CONVERSATION_ID, |
|
||||
content: '测试消息:这是客服发送的测试消息', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('\n📤 客服发送消息:', JSON.stringify(testMessage)); |
|
||||
managerWs.send(JSON.stringify(testMessage)); |
|
||||
} |
|
||||
|
|
||||
// 发送替代格式消息
|
|
||||
function sendAlternativeFormat() { |
|
||||
const messageId = 'test_manager_alt_' + Date.now(); |
|
||||
const alternativeMessage = { |
|
||||
type: 'chat_message', |
|
||||
messageId: messageId, |
|
||||
conversationId: TEST_CONVERSATION_ID, |
|
||||
content: '测试替代格式:不使用payload包装', |
|
||||
contentType: 1 |
|
||||
}; |
|
||||
|
|
||||
console.log('📤 客服发送替代格式消息:', JSON.stringify(alternativeMessage)); |
|
||||
managerWs.send(JSON.stringify(alternativeMessage)); |
|
||||
|
|
||||
// 5秒后关闭连接
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n=== 调试结束 ==='); |
|
||||
managerWs.close(); |
|
||||
}, 5000); |
|
||||
} |
|
||||
@ -1,215 +0,0 @@ |
|||||
// 完整流程调试脚本
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
const WS_URL = 'ws://localhost:3003'; |
|
||||
const TEST_MANAGER_ID = '22'; |
|
||||
let managerWs = null; |
|
||||
let userWs = null; |
|
||||
let newConversationId = null; |
|
||||
let testUserId = 'test_customer_' + Date.now(); |
|
||||
|
|
||||
console.log('=== 启动完整流程调试 ==='); |
|
||||
console.log(`测试用户ID: ${testUserId}`); |
|
||||
|
|
||||
// 步骤1: 连接客服并认证
|
|
||||
function connectManager() { |
|
||||
return new Promise((resolve) => { |
|
||||
managerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
managerWs.on('open', () => { |
|
||||
console.log('✅ 客服WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
userType: 'manager' |
|
||||
}); |
|
||||
console.log('📤 客服发送认证消息:', authMsg); |
|
||||
managerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客服收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客服认证成功'); |
|
||||
resolve(); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 客服解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 步骤2: 连接用户并认证
|
|
||||
function connectUser() { |
|
||||
return new Promise((resolve) => { |
|
||||
userWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
userWs.on('open', () => { |
|
||||
console.log('✅ 客户WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
userId: testUserId, |
|
||||
userType: 'user' |
|
||||
}); |
|
||||
console.log('📤 客户发送认证消息:', authMsg); |
|
||||
userWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
userWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客户收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客户认证成功'); |
|
||||
resolve(); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 客户解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 步骤3: 用户发送消息创建新会话
|
|
||||
function userSendMessage() { |
|
||||
return new Promise((resolve) => { |
|
||||
const messageId = 'test_user_' + Date.now(); |
|
||||
const userMessage = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: messageId, |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
content: '你好,我是测试客户,我想咨询一个问题', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('\n📤 客户发送消息:', JSON.stringify(userMessage)); |
|
||||
userWs.send(JSON.stringify(userMessage)); |
|
||||
|
|
||||
// 监听消息发送成功确认
|
|
||||
const userMessageHandler = (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
if (message.type === 'message_sent') { |
|
||||
console.log('✅ 客户消息发送成功确认'); |
|
||||
newConversationId = message.payload.conversationId; |
|
||||
console.log('📝 新创建的会话ID:', newConversationId); |
|
||||
userWs.removeListener('message', userMessageHandler); |
|
||||
resolve(); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 解析用户消息响应失败:', e); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
userWs.addListener('message', userMessageHandler); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 步骤4: 客服使用新会话ID回复消息
|
|
||||
function managerReplyMessage() { |
|
||||
return new Promise((resolve) => { |
|
||||
if (!newConversationId) { |
|
||||
console.error('❌ 没有获取到会话ID'); |
|
||||
resolve(false); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
const messageId = 'test_manager_' + Date.now(); |
|
||||
const replyMessage = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: messageId, |
|
||||
conversationId: newConversationId, |
|
||||
content: '您好,我是客服,请问有什么可以帮助您的?', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('\n📤 客服发送回复消息:', JSON.stringify(replyMessage)); |
|
||||
managerWs.send(JSON.stringify(replyMessage)); |
|
||||
|
|
||||
// 监听回复消息的结果
|
|
||||
const managerMessageHandler = (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客服收到回复结果:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'error') { |
|
||||
console.error('❌ 客服回复失败:', message.message); |
|
||||
managerWs.removeListener('message', managerMessageHandler); |
|
||||
resolve(false); |
|
||||
} else if (message.type === 'message_sent') { |
|
||||
console.log('✅ 客服回复发送成功!'); |
|
||||
managerWs.removeListener('message', managerMessageHandler); |
|
||||
resolve(true); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 解析客服消息响应失败:', e); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
managerWs.addListener('message', managerMessageHandler); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 主函数
|
|
||||
async function main() { |
|
||||
try { |
|
||||
// 连接客服
|
|
||||
await connectManager(); |
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)); |
|
||||
|
|
||||
// 连接用户
|
|
||||
await connectUser(); |
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)); |
|
||||
|
|
||||
// 用户发送消息创建会话
|
|
||||
await userSendMessage(); |
|
||||
await new Promise(resolve => setTimeout(resolve, 2000)); |
|
||||
|
|
||||
// 客服回复消息
|
|
||||
const success = await managerReplyMessage(); |
|
||||
|
|
||||
if (!success) { |
|
||||
console.log('\n🔄 尝试替代格式...'); |
|
||||
// 尝试不使用payload包装
|
|
||||
const messageId = 'test_manager_alt_' + Date.now(); |
|
||||
const alternativeMessage = { |
|
||||
type: 'chat_message', |
|
||||
messageId: messageId, |
|
||||
conversationId: newConversationId, |
|
||||
content: '测试替代格式:不使用payload包装', |
|
||||
contentType: 1 |
|
||||
}; |
|
||||
|
|
||||
console.log('📤 客服发送替代格式消息:', JSON.stringify(alternativeMessage)); |
|
||||
managerWs.send(JSON.stringify(alternativeMessage)); |
|
||||
} |
|
||||
|
|
||||
// 等待5秒后关闭连接
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n=== 调试结束 ==='); |
|
||||
if (managerWs) managerWs.close(); |
|
||||
if (userWs) userWs.close(); |
|
||||
}, 5000); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 调试过程中出错:', error); |
|
||||
if (managerWs) managerWs.close(); |
|
||||
if (userWs) userWs.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 启动调试
|
|
||||
main(); |
|
||||
@ -1,86 +0,0 @@ |
|||||
// 服务器日志调试脚本
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
const WS_URL = 'ws://localhost:3003'; |
|
||||
const TEST_MANAGER_ID = '22'; |
|
||||
let managerWs = null; |
|
||||
|
|
||||
console.log('=== 启动服务器日志调试 ==='); |
|
||||
|
|
||||
// 连接客服WebSocket
|
|
||||
managerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
managerWs.on('open', () => { |
|
||||
console.log('✅ 客服WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
userType: 'manager' |
|
||||
}); |
|
||||
console.log('📤 客服发送认证消息'); |
|
||||
managerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客服收到消息类型:', message.type); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客服认证成功'); |
|
||||
|
|
||||
// 等待2秒后发送测试消息
|
|
||||
setTimeout(() => { |
|
||||
sendTestMessage(); |
|
||||
}, 2000); |
|
||||
} |
|
||||
|
|
||||
if (message.type === 'error') { |
|
||||
console.error('❌ 错误消息:', message.message); |
|
||||
} |
|
||||
|
|
||||
if (message.type === 'message_sent') { |
|
||||
console.log('✅ 消息发送成功!'); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('error', (error) => { |
|
||||
console.error('❌ WebSocket错误:', error); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('close', () => { |
|
||||
console.log('❌ WebSocket连接已关闭'); |
|
||||
}); |
|
||||
|
|
||||
// 发送测试消息
|
|
||||
function sendTestMessage() { |
|
||||
// 使用一个固定的会话ID进行测试
|
|
||||
const conversationId = '4fa4b92f-df20-40ae-94b9-f906753a4cfd'; |
|
||||
const messageId = 'test_debug_' + Date.now(); |
|
||||
|
|
||||
console.log(`\n📤 发送测试消息 - 会话ID: ${conversationId}, 消息ID: ${messageId}`); |
|
||||
|
|
||||
const testMessage = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: messageId, |
|
||||
conversationId: conversationId, |
|
||||
content: '服务器日志调试消息', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
managerWs.send(JSON.stringify(testMessage)); |
|
||||
|
|
||||
// 5秒后退出
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n=== 调试结束 ==='); |
|
||||
managerWs.close(); |
|
||||
process.exit(0); |
|
||||
}, 5000); |
|
||||
} |
|
||||
@ -1,111 +0,0 @@ |
|||||
// 极简调试脚本
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
const WS_URL = 'ws://localhost:3003'; |
|
||||
const TEST_MANAGER_ID = '22'; |
|
||||
let managerWs = null; |
|
||||
|
|
||||
// 启动调试
|
|
||||
function startDebug() { |
|
||||
console.log('=== 启动极简调试 ==='); |
|
||||
|
|
||||
// 连接客服WebSocket
|
|
||||
managerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
managerWs.on('open', () => { |
|
||||
console.log('✅ 客服WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
userType: 'manager' |
|
||||
}); |
|
||||
managerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客服认证成功'); |
|
||||
|
|
||||
// 等待2秒后发送测试消息
|
|
||||
setTimeout(() => { |
|
||||
// 尝试发送测试消息,使用不同的格式
|
|
||||
sendTestMessage(); |
|
||||
}, 2000); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('error', (error) => { |
|
||||
console.error('❌ WebSocket错误:', error); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('close', () => { |
|
||||
console.log('❌ WebSocket连接已关闭'); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 发送测试消息
|
|
||||
function sendTestMessage() { |
|
||||
console.log('\n🔄 测试不同的消息格式...'); |
|
||||
|
|
||||
// 测试格式1: 不使用payload包装
|
|
||||
const format1 = { |
|
||||
type: 'chat_message', |
|
||||
conversationId: '4fa4b92f-df20-40ae-94b9-f906753a4cfd', |
|
||||
content: '测试格式1: 不使用payload包装', |
|
||||
contentType: 1 |
|
||||
}; |
|
||||
|
|
||||
console.log('\n📤 发送格式1:', JSON.stringify(format1)); |
|
||||
managerWs.send(JSON.stringify(format1)); |
|
||||
|
|
||||
// 等待1秒后发送下一个格式
|
|
||||
setTimeout(() => { |
|
||||
// 测试格式2: 使用payload包装,但字段名改为驼峰式
|
|
||||
const format2 = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
conversationId: '4fa4b92f-df20-40ae-94b9-f906753a4cfd', |
|
||||
content: '测试格式2: 使用payload包装', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('\n📤 发送格式2:', JSON.stringify(format2)); |
|
||||
managerWs.send(JSON.stringify(format2)); |
|
||||
|
|
||||
// 等待1秒后发送下一个格式
|
|
||||
setTimeout(() => { |
|
||||
// 测试格式3: 使用payload包装,添加messageId
|
|
||||
const format3 = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: 'test_' + Date.now(), |
|
||||
conversationId: '4fa4b92f-df20-40ae-94b9-f906753a4cfd', |
|
||||
content: '测试格式3: 添加messageId', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('\n📤 发送格式3:', JSON.stringify(format3)); |
|
||||
managerWs.send(JSON.stringify(format3)); |
|
||||
|
|
||||
// 等待5秒后关闭连接
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n=== 调试结束 ==='); |
|
||||
managerWs.close(); |
|
||||
}, 5000); |
|
||||
}, 1000); |
|
||||
}, 1000); |
|
||||
} |
|
||||
|
|
||||
// 启动调试
|
|
||||
startDebug(); |
|
||||
@ -1,191 +0,0 @@ |
|||||
// 详细调试脚本,带更多日志记录
|
|
||||
const WebSocket = require('ws'); |
|
||||
|
|
||||
const WS_URL = 'ws://localhost:3003'; |
|
||||
const TEST_MANAGER_ID = '22'; |
|
||||
const TEST_CONVERSATION_ID = '4fa4b92f-df20-40ae-94b9-f906753a4cfd'; // 使用已知的会话ID
|
|
||||
let managerWs = null; |
|
||||
let userWs = null; |
|
||||
|
|
||||
console.log('=== 启动详细调试 ==='); |
|
||||
console.log(`测试会话ID: ${TEST_CONVERSATION_ID}`); |
|
||||
|
|
||||
// 连接客服WebSocket
|
|
||||
function connectManager() { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
managerWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
managerWs.on('open', () => { |
|
||||
console.log('✅ 客服WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
managerId: TEST_MANAGER_ID, |
|
||||
userType: 'manager' |
|
||||
}); |
|
||||
console.log('📤 客服发送认证消息:', authMsg); |
|
||||
managerWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客服收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客服认证成功'); |
|
||||
resolve(); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 客服解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('error', (error) => { |
|
||||
console.error('❌ 客服WebSocket错误:', error); |
|
||||
reject(error); |
|
||||
}); |
|
||||
|
|
||||
managerWs.on('close', () => { |
|
||||
console.log('❌ 客服WebSocket连接已关闭'); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 连接用户WebSocket
|
|
||||
function connectUser() { |
|
||||
return new Promise((resolve, reject) => { |
|
||||
const userId = 'test_customer_' + Date.now(); |
|
||||
userWs = new WebSocket(WS_URL); |
|
||||
|
|
||||
userWs.on('open', () => { |
|
||||
console.log('✅ 客户WebSocket连接已建立'); |
|
||||
|
|
||||
// 发送认证消息
|
|
||||
const authMsg = JSON.stringify({ |
|
||||
type: 'auth', |
|
||||
userId: userId, |
|
||||
userType: 'user' |
|
||||
}); |
|
||||
console.log('📤 客户发送认证消息:', authMsg); |
|
||||
userWs.send(authMsg); |
|
||||
}); |
|
||||
|
|
||||
userWs.on('message', (data) => { |
|
||||
try { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('📥 客户收到消息:', JSON.stringify(message)); |
|
||||
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 客户认证成功'); |
|
||||
resolve(); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 客户解析消息失败:', e); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
userWs.on('error', (error) => { |
|
||||
console.error('❌ 客户WebSocket错误:', error); |
|
||||
reject(error); |
|
||||
}); |
|
||||
|
|
||||
userWs.on('close', () => { |
|
||||
console.log('❌ 客户WebSocket连接已关闭'); |
|
||||
}); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 发送测试消息
|
|
||||
function sendTestMessage() { |
|
||||
return new Promise((resolve) => { |
|
||||
console.log('\n🔄 客服发送测试消息...'); |
|
||||
|
|
||||
const messageId = 'test_manager_' + Date.now(); |
|
||||
const testMessage = { |
|
||||
type: 'chat_message', |
|
||||
payload: { |
|
||||
messageId: messageId, |
|
||||
conversationId: TEST_CONVERSATION_ID, |
|
||||
content: '测试消息:这是客服发送的测试消息', |
|
||||
contentType: 1 |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('📤 客服发送消息:', JSON.stringify(testMessage)); |
|
||||
managerWs.send(JSON.stringify(testMessage)); |
|
||||
|
|
||||
// 监听错误响应
|
|
||||
const originalOnMessage = managerWs.onmessage; |
|
||||
managerWs.onmessage = (event) => { |
|
||||
originalOnMessage(event); |
|
||||
try { |
|
||||
const message = JSON.parse(event.data.toString()); |
|
||||
if (message.type === 'error') { |
|
||||
console.error('❌ 接收到错误消息:', message.message); |
|
||||
resolve(false); |
|
||||
} else if (message.type === 'message_sent') { |
|
||||
console.log('✅ 消息发送成功确认'); |
|
||||
resolve(true); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.error('❌ 解析响应消息失败:', e); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 5秒后超时
|
|
||||
setTimeout(() => { |
|
||||
console.log('⌛ 消息发送超时'); |
|
||||
resolve(false); |
|
||||
}, 5000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 主函数
|
|
||||
async function main() { |
|
||||
try { |
|
||||
// 连接客服
|
|
||||
await connectManager(); |
|
||||
await new Promise(resolve => setTimeout(resolve, 1000)); |
|
||||
|
|
||||
// 连接用户(可选)
|
|
||||
// await connectUser();
|
|
||||
// await new Promise(resolve => setTimeout(resolve, 1000));
|
|
||||
|
|
||||
// 发送测试消息
|
|
||||
const success = await sendTestMessage(); |
|
||||
|
|
||||
if (!success) { |
|
||||
console.log('\n🔄 尝试另一种格式...'); |
|
||||
|
|
||||
// 尝试不使用payload包装
|
|
||||
const messageId = 'test_manager_alt_' + Date.now(); |
|
||||
const alternativeMessage = { |
|
||||
type: 'chat_message', |
|
||||
messageId: messageId, |
|
||||
conversationId: TEST_CONVERSATION_ID, |
|
||||
content: '测试替代格式:不使用payload包装', |
|
||||
contentType: 1 |
|
||||
}; |
|
||||
|
|
||||
console.log('📤 客服发送替代格式消息:', JSON.stringify(alternativeMessage)); |
|
||||
managerWs.send(JSON.stringify(alternativeMessage)); |
|
||||
} |
|
||||
|
|
||||
// 等待5秒后关闭连接
|
|
||||
setTimeout(() => { |
|
||||
console.log('\n=== 调试结束 ==='); |
|
||||
if (managerWs) managerWs.close(); |
|
||||
if (userWs) userWs.close(); |
|
||||
}, 5000); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 调试过程中出错:', error); |
|
||||
if (managerWs) managerWs.close(); |
|
||||
if (userWs) userWs.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 启动调试
|
|
||||
main(); |
|
||||
@ -1,175 +0,0 @@ |
|||||
// 直接连接数据库检查productQuantity字段的脚本
|
|
||||
const Sequelize = require('sequelize'); |
|
||||
const mysql = require('mysql2/promise'); |
|
||||
|
|
||||
// 数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
'minishop', // 数据库名
|
|
||||
'root', // 用户名
|
|
||||
'password', // 密码
|
|
||||
{ |
|
||||
host: 'localhost', |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 5, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 定义购物车模型 - 直接复制自server-mysql.js
|
|
||||
class CartItem extends Sequelize.Model {} |
|
||||
CartItem.init({ |
|
||||
id: { |
|
||||
type: Sequelize.DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
userId: { |
|
||||
type: Sequelize.DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
productId: { |
|
||||
type: Sequelize.DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
productName: { |
|
||||
type: Sequelize.DataTypes.STRING(255), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
specification: { |
|
||||
type: Sequelize.DataTypes.STRING(255) |
|
||||
}, |
|
||||
quantity: { |
|
||||
type: Sequelize.DataTypes.INTEGER, |
|
||||
allowNull: false, |
|
||||
defaultValue: 1 |
|
||||
}, |
|
||||
productQuantity: { |
|
||||
type: Sequelize.DataTypes.INTEGER, |
|
||||
allowNull: false, |
|
||||
defaultValue: 0 |
|
||||
}, |
|
||||
grossWeight: { |
|
||||
type: Sequelize.DataTypes.DECIMAL(10, 2) |
|
||||
}, |
|
||||
yolk: { |
|
||||
type: Sequelize.DataTypes.STRING(100) |
|
||||
}, |
|
||||
price: { |
|
||||
type: Sequelize.DataTypes.DECIMAL(10, 2) |
|
||||
}, |
|
||||
selected: { |
|
||||
type: Sequelize.DataTypes.BOOLEAN, |
|
||||
defaultValue: true |
|
||||
}, |
|
||||
added_at: { |
|
||||
type: Sequelize.DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize, |
|
||||
modelName: 'CartItem', |
|
||||
tableName: 'cart_items', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// 检查数据库结构
|
|
||||
async function checkDatabaseStructure() { |
|
||||
console.log('开始直接检查数据库中的productQuantity字段...'); |
|
||||
|
|
||||
try { |
|
||||
// 检查连接
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 1. 使用原始查询检查表结构
|
|
||||
console.log('\n1. 检查cart_items表结构...'); |
|
||||
const [fields, _] = await sequelize.query('DESCRIBE cart_items'); |
|
||||
|
|
||||
// 查找productQuantity字段
|
|
||||
const productQuantityField = fields.find(field => field.Field === 'productQuantity'); |
|
||||
|
|
||||
if (productQuantityField) { |
|
||||
console.log('✅ 数据库中存在productQuantity字段:'); |
|
||||
console.log(` - 类型: ${productQuantityField.Type}`); |
|
||||
console.log(` - 是否允许NULL: ${productQuantityField.Null === 'YES' ? '是' : '否'}`); |
|
||||
console.log(` - 默认值: ${productQuantityField.Default || '无'}`); |
|
||||
} else { |
|
||||
console.error('❌ 数据库中不存在productQuantity字段!'); |
|
||||
console.log('cart_items表中的所有字段:', fields.map(field => field.Field).join(', ')); |
|
||||
|
|
||||
// 如果不存在,尝试添加这个字段
|
|
||||
console.log('\n尝试添加productQuantity字段到cart_items表...'); |
|
||||
try { |
|
||||
await sequelize.query('ALTER TABLE cart_items ADD COLUMN productQuantity INT NOT NULL DEFAULT 0'); |
|
||||
console.log('✅ 成功添加productQuantity字段'); |
|
||||
} catch (addError) { |
|
||||
console.error('❌ 添加字段失败:', addError.message); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 2. 检查test_user_id的购物车数据
|
|
||||
console.log('\n2. 检查测试用户的购物车数据...'); |
|
||||
const cartItems = await CartItem.findAll({ |
|
||||
where: { |
|
||||
userId: 'test_user_id' |
|
||||
}, |
|
||||
// 明确指定返回所有字段
|
|
||||
attributes: { |
|
||||
exclude: [] // 不排除任何字段
|
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
console.log(`找到 ${cartItems.length} 条购物车记录`); |
|
||||
|
|
||||
if (cartItems.length > 0) { |
|
||||
// 显示第一条记录的所有字段
|
|
||||
console.log('\n第一条购物车记录的所有字段:'); |
|
||||
const firstItem = cartItems[0].toJSON(); |
|
||||
Object.keys(firstItem).forEach(key => { |
|
||||
console.log(` - ${key}: ${firstItem[key]}`); |
|
||||
}); |
|
||||
|
|
||||
// 特别检查productQuantity字段
|
|
||||
console.log('\nproductQuantity字段在数据中的状态:'); |
|
||||
cartItems.forEach((item, index) => { |
|
||||
const data = item.toJSON(); |
|
||||
console.log(` 记录 ${index + 1}: productQuantity = ${data.productQuantity !== undefined ? data.productQuantity : 'undefined'}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 3. 尝试直接插入一条带productQuantity的记录
|
|
||||
console.log('\n3. 尝试直接插入一条带productQuantity的记录...'); |
|
||||
const testProductId = 'db_test_' + Date.now(); |
|
||||
const newItem = await CartItem.create({ |
|
||||
userId: 'test_user_id', |
|
||||
productId: testProductId, |
|
||||
productName: '数据库测试商品', |
|
||||
specification: '测试规格', |
|
||||
quantity: 2, |
|
||||
productQuantity: 10, |
|
||||
grossWeight: 1000, |
|
||||
yolk: '测试蛋黄', |
|
||||
price: 50, |
|
||||
selected: true, |
|
||||
added_at: new Date() |
|
||||
}); |
|
||||
|
|
||||
console.log('✅ 成功插入记录'); |
|
||||
console.log('插入的记录详情:', newItem.toJSON()); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('检查过程中发生错误:', error.message); |
|
||||
} finally { |
|
||||
// 关闭连接
|
|
||||
await sequelize.close(); |
|
||||
console.log('\n数据库连接已关闭'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行检查
|
|
||||
checkDatabaseStructure(); |
|
||||
@ -0,0 +1,33 @@ |
|||||
|
version: '3.8' |
||||
|
|
||||
|
services: |
||||
|
app: |
||||
|
build: . |
||||
|
container_name: wechat-miniprogram-server |
||||
|
restart: always |
||||
|
ports: |
||||
|
- "3000:3000" |
||||
|
environment: |
||||
|
- NODE_ENV=production |
||||
|
volumes: |
||||
|
- ./logs:/app/logs |
||||
|
- ./uploads:/app/uploads |
||||
|
depends_on: |
||||
|
- db |
||||
|
|
||||
|
db: |
||||
|
image: mysql:8.0 |
||||
|
container_name: wechat-miniprogram-db |
||||
|
restart: always |
||||
|
ports: |
||||
|
- "3306:3306" |
||||
|
environment: |
||||
|
- MYSQL_ROOT_PASSWORD=rootpassword |
||||
|
- MYSQL_DATABASE=wechat_miniprogram |
||||
|
- MYSQL_USER=wechat_user |
||||
|
- MYSQL_PASSWORD=wechat_password |
||||
|
volumes: |
||||
|
- db_data:/var/lib/mysql |
||||
|
|
||||
|
volumes: |
||||
|
db_data: |
||||
@ -1,61 +0,0 @@ |
|||||
// 查询特定名称商品的创建者
|
|
||||
|
|
||||
const dotenv = require('dotenv'); |
|
||||
const mysql = require('mysql2/promise'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 加载环境变量
|
|
||||
dotenv.config({ path: path.resolve(__dirname, '.env') }); |
|
||||
|
|
||||
// 数据库连接配置
|
|
||||
const dbConfig = { |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
user: process.env.DB_USER || 'root', |
|
||||
password: process.env.DB_PASSWORD || '', |
|
||||
database: process.env.DB_NAME || 'wechat_app', |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
}; |
|
||||
|
|
||||
async function findProductCreator() { |
|
||||
let connection; |
|
||||
try { |
|
||||
// 连接数据库
|
|
||||
connection = await mysql.createConnection(dbConfig); |
|
||||
console.log('数据库连接成功'); |
|
||||
|
|
||||
// 查询名称为88888的商品及其创建者
|
|
||||
const [products] = await connection.query(` |
|
||||
SELECT p.productId, p.productName, p.sellerId, u.userId, u.nickName, u.phoneNumber |
|
||||
FROM products p |
|
||||
LEFT JOIN users u ON p.sellerId = u.userId |
|
||||
WHERE p.productName = '88888' |
|
||||
`);
|
|
||||
|
|
||||
if (products.length === 0) { |
|
||||
console.log('未找到名称为88888的商品'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
console.log(`找到 ${products.length} 个名称为88888的商品:`); |
|
||||
products.forEach((product, index) => { |
|
||||
console.log(`\n商品 ${index + 1}:`); |
|
||||
console.log(` 商品ID: ${product.productId}`); |
|
||||
console.log(` 商品名称: ${product.productName}`); |
|
||||
console.log(` 创建者ID: ${product.sellerId}`); |
|
||||
console.log(` 创建者昵称: ${product.nickName || '未设置'}`); |
|
||||
console.log(` 创建者手机号: ${product.phoneNumber || '未设置'}`); |
|
||||
}); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('查询失败:', error.message); |
|
||||
} finally { |
|
||||
if (connection) { |
|
||||
await connection.end(); |
|
||||
console.log('\n数据库连接已关闭'); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行查询
|
|
||||
findProductCreator(); |
|
||||
@ -1,85 +0,0 @@ |
|||||
// 简单测试服务器 - 不连接数据库,专注于API接口测试和毛重字段处理
|
|
||||
const express = require('express'); |
|
||||
const bodyParser = require('body-parser'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 创建Express应用
|
|
||||
const app = express(); |
|
||||
const PORT = 3000; |
|
||||
|
|
||||
// 中间件
|
|
||||
app.use(bodyParser.json()); |
|
||||
|
|
||||
// 请求日志中间件
|
|
||||
app.use((req, res, next) => { |
|
||||
const now = new Date(); |
|
||||
console.log(`[${now.toISOString()}] 收到请求: ${req.method} ${req.url}`); |
|
||||
next(); |
|
||||
}); |
|
||||
|
|
||||
// 简单测试接口
|
|
||||
app.get('/api/test-connection', (req, res) => { |
|
||||
res.json({ |
|
||||
success: true, |
|
||||
message: '服务器连接测试成功', |
|
||||
timestamp: new Date().toISOString(), |
|
||||
serverInfo: { port: PORT } |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
// 商品发布接口(简化版,专注于毛重处理)
|
|
||||
app.post('/api/product/publish', (req, res) => { |
|
||||
try { |
|
||||
const { openid, product } = req.body; |
|
||||
console.log('收到商品发布请求:', { openid, product }); |
|
||||
|
|
||||
// 验证参数
|
|
||||
if (!openid || !product) { |
|
||||
return res.status(400).json({ success: false, message: '缺少必要参数' }); |
|
||||
} |
|
||||
|
|
||||
// 重点:毛重字段处理逻辑
|
|
||||
let grossWeightValue = product.grossWeight; |
|
||||
console.log('原始毛重值:', grossWeightValue, '类型:', typeof grossWeightValue); |
|
||||
|
|
||||
// 处理各种情况的毛重值
|
|
||||
if (grossWeightValue === '' || grossWeightValue === null || grossWeightValue === undefined || (typeof grossWeightValue === 'object' && grossWeightValue === null)) { |
|
||||
grossWeightValue = null; |
|
||||
console.log('毛重值为空或null,设置为null'); |
|
||||
} else { |
|
||||
// 转换为数字
|
|
||||
const numValue = Number(grossWeightValue); |
|
||||
if (!isNaN(numValue) && isFinite(numValue)) { |
|
||||
grossWeightValue = numValue; |
|
||||
console.log('毛重值成功转换为数字:', grossWeightValue); |
|
||||
} else { |
|
||||
grossWeightValue = null; |
|
||||
console.log('毛重值不是有效数字,设置为null'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 返回处理结果
|
|
||||
return res.json({ |
|
||||
success: true, |
|
||||
message: '商品发布处理成功(模拟)', |
|
||||
processedData: { |
|
||||
productName: product.productName, |
|
||||
price: product.price, |
|
||||
quantity: product.quantity, |
|
||||
grossWeight: grossWeightValue, // 返回处理后的毛重值
|
|
||||
grossWeightType: typeof grossWeightValue |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('发布商品失败:', error); |
|
||||
res.status(500).json({ success: false, message: '服务器错误', error: error.message }); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 启动服务器
|
|
||||
app.listen(PORT, () => { |
|
||||
console.log(`修复版服务器运行在 http://localhost:${PORT}`); |
|
||||
console.log('测试接口: http://localhost:3000/api/test-connection'); |
|
||||
console.log('商品发布接口: POST http://localhost:3000/api/product/publish'); |
|
||||
}); |
|
||||
@ -1,5 +0,0 @@ |
|||||
{ |
|
||||
"timestamp": "2025-10-08T03:56:27.607Z", |
|
||||
"error": "Access denied for user 'root'@'218.88.54.38' (using password: YES)", |
|
||||
"stack": "Error: Access denied for user 'root'@'218.88.54.38' (using password: YES)\n at Object.createConnectionPromise [as createConnection] (D:\\WeichatAPP\\miniprogram-6\\server-example\\node_modules\\mysql2\\promise.js:19:31)\n at fixGrossWeightValues (D:\\WeichatAPP\\miniprogram-6\\server-example\\fix-gross-weight-values.js:26:30)\n at Object.<anonymous> (D:\\WeichatAPP\\miniprogram-6\\server-example\\fix-gross-weight-values.js:143:1)\n at Module._compile (node:internal/modules/cjs/loader:1688:14)\n at Object..js (node:internal/modules/cjs/loader:1820:10)\n at Module.load (node:internal/modules/cjs/loader:1423:32)\n at Function._load (node:internal/modules/cjs/loader:1246:12)\n at TracingChannel.traceSync (node:diagnostics_channel:322:14)\n at wrapModuleLoad (node:internal/modules/cjs/loader:235:24)\n at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:171:5)" |
|
||||
} |
|
||||
@ -1,24 +0,0 @@ |
|||||
{ |
|
||||
"timestamp": "2025-10-08T03:57:52.452Z", |
|
||||
"modified": true, |
|
||||
"changes": [ |
|
||||
{ |
|
||||
"name": "商品列表API增强", |
|
||||
"applied": true |
|
||||
}, |
|
||||
{ |
|
||||
"name": "用户商品API增强", |
|
||||
"applied": false |
|
||||
}, |
|
||||
{ |
|
||||
"name": "添加毛重处理中间件", |
|
||||
"applied": true |
|
||||
} |
|
||||
], |
|
||||
"recommendations": [ |
|
||||
"重启服务器", |
|
||||
"检查前端页面使用的字段名", |
|
||||
"添加商品发布表单的毛重验证", |
|
||||
"检查前端数据处理逻辑" |
|
||||
] |
|
||||
} |
|
||||
@ -1,135 +0,0 @@ |
|||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 日志文件路径
|
|
||||
const logFilePath = path.join(__dirname, 'logs', 'output.log'); |
|
||||
|
|
||||
// 读取并分析日志文件
|
|
||||
function analyzeGrossWeightLogs() { |
|
||||
try { |
|
||||
console.log(`正在分析日志文件: ${logFilePath}`); |
|
||||
console.log('搜索与grossWeight相关的日志记录...\n'); |
|
||||
|
|
||||
// 读取日志文件内容
|
|
||||
const logContent = fs.readFileSync(logFilePath, 'utf-8'); |
|
||||
const logLines = logContent.split('\n'); |
|
||||
|
|
||||
// 存储找到的毛重相关记录
|
|
||||
const grossWeightRecords = []; |
|
||||
const publishRequestRecords = []; |
|
||||
|
|
||||
// 搜索最近24小时的日志记录
|
|
||||
const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); |
|
||||
|
|
||||
// 遍历日志行
|
|
||||
for (let i = 0; i < logLines.length; i++) { |
|
||||
const line = logLines[i]; |
|
||||
|
|
||||
// 检查时间戳是否在最近24小时内
|
|
||||
const timestampMatch = line.match(/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})/); |
|
||||
if (timestampMatch) { |
|
||||
const logDate = new Date(timestampMatch[1]); |
|
||||
if (logDate < twentyFourHoursAgo) { |
|
||||
continue; // 跳过24小时前的日志
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 搜索与grossWeight相关的记录
|
|
||||
if (line.includes('grossWeight')) { |
|
||||
grossWeightRecords.push({ |
|
||||
line: i + 1, |
|
||||
content: line, |
|
||||
timestamp: timestampMatch ? timestampMatch[1] : '未知' |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 搜索商品发布请求
|
|
||||
if (line.includes('/api/product/publish')) { |
|
||||
// 收集发布请求的上下文
|
|
||||
const contextLines = []; |
|
||||
// 向前收集5行
|
|
||||
for (let j = Math.max(0, i - 5); j < Math.min(logLines.length, i + 20); j++) { |
|
||||
contextLines.push(logLines[j]); |
|
||||
} |
|
||||
publishRequestRecords.push({ |
|
||||
line: i + 1, |
|
||||
content: contextLines.join('\n'), |
|
||||
timestamp: timestampMatch ? timestampMatch[1] : '未知' |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 输出分析结果
|
|
||||
console.log('===== 最近24小时毛重字段处理分析结果 =====\n'); |
|
||||
|
|
||||
console.log(`找到 ${grossWeightRecords.length} 条与grossWeight相关的日志记录\n`); |
|
||||
|
|
||||
// 显示最近的10条毛重记录
|
|
||||
console.log('最近的10条毛重处理记录:'); |
|
||||
grossWeightRecords.slice(-10).forEach((record, index) => { |
|
||||
console.log(`[${record.timestamp}] 第${record.line}行: ${record.content}`); |
|
||||
}); |
|
||||
|
|
||||
console.log('\n'); |
|
||||
|
|
||||
// 显示最近的商品发布请求及其毛重处理
|
|
||||
console.log(`找到 ${publishRequestRecords.length} 条商品发布请求记录`); |
|
||||
if (publishRequestRecords.length > 0) { |
|
||||
console.log('\n最近的商品发布请求及毛重处理详情:'); |
|
||||
const latestPublish = publishRequestRecords[publishRequestRecords.length - 1]; |
|
||||
console.log(`\n时间: ${latestPublish.timestamp}`); |
|
||||
console.log(`起始行号: ${latestPublish.line}`); |
|
||||
console.log('详细内容:'); |
|
||||
|
|
||||
// 解析请求体中的grossWeight值
|
|
||||
const requestBodyMatch = latestPublish.content.match(/请求体: \{([\s\S]*?)\}/); |
|
||||
if (requestBodyMatch) { |
|
||||
console.log(requestBodyMatch[0]); |
|
||||
|
|
||||
// 提取grossWeight值
|
|
||||
const grossWeightMatch = requestBodyMatch[0].match(/"grossWeight"\s*:\s*(null|\d+(\.\d+)?)/); |
|
||||
if (grossWeightMatch) { |
|
||||
console.log(`\n请求中的毛重值: ${grossWeightMatch[1]}`); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 查找毛重处理的相关日志
|
|
||||
const grossWeightProcessingMatch = latestPublish.content.match(/\[发布商品.*\] 原始毛重值:.*|毛重值.*设置为.*|最终处理的毛重值:.*|grossWeightStored:.*|毛重.*转换为数字/); |
|
||||
if (grossWeightProcessingMatch) { |
|
||||
console.log('\n毛重处理过程:'); |
|
||||
grossWeightProcessingMatch.forEach(processingLine => { |
|
||||
console.log(processingLine); |
|
||||
}); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 生成总结
|
|
||||
console.log('\n===== 分析总结 ====='); |
|
||||
if (grossWeightRecords.length === 0) { |
|
||||
console.log('在最近24小时内没有找到与grossWeight相关的日志记录。'); |
|
||||
} else { |
|
||||
console.log(`在最近24小时内找到了 ${grossWeightRecords.length} 条与grossWeight相关的日志记录。`); |
|
||||
|
|
||||
// 简单统计
|
|
||||
const nullGrossWeightCount = grossWeightRecords.filter(r => r.content.includes('grossWeight: null')).length; |
|
||||
const zeroGrossWeightCount = grossWeightRecords.filter(r => r.content.includes('grossWeight: 0')).length; |
|
||||
const numericGrossWeightCount = grossWeightRecords.filter(r => /grossWeight:\s*\d+\.\d+/.test(r.content)).length; |
|
||||
|
|
||||
console.log(`- 毛重为null的记录数: ${nullGrossWeightCount}`); |
|
||||
console.log(`- 毛重为0的记录数: ${zeroGrossWeightCount}`); |
|
||||
console.log(`- 毛重为数字的记录数: ${numericGrossWeightCount}`); |
|
||||
} |
|
||||
|
|
||||
console.log('\n提示: 如果需要查看更多详细信息,建议直接查看日志文件或使用更专业的日志分析工具。'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('分析日志时发生错误:', error.message); |
|
||||
console.log('\n建议手动查看日志文件:'); |
|
||||
console.log(`1. 打开文件: ${logFilePath}`); |
|
||||
console.log('2. 搜索关键词: grossWeight'); |
|
||||
console.log('3. 特别关注: 发布商品请求中的grossWeight值和处理过程'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行分析
|
|
||||
analyzeGrossWeightLogs(); |
|
||||
@ -1,71 +0,0 @@ |
|||||
// 查询数据库中的用户和商品信息
|
|
||||
require('dotenv').config(); |
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
|
|
||||
// 创建数据库连接
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 执行查询
|
|
||||
async function queryDatabase() { |
|
||||
try { |
|
||||
// 测试连接
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 查询用户信息
|
|
||||
const users = await sequelize.query('SELECT * FROM users LIMIT 10', { type: sequelize.QueryTypes.SELECT }); |
|
||||
console.log('\n👥 用户列表:'); |
|
||||
console.log(users.map(u => ({ |
|
||||
id: u.id, |
|
||||
openid: u.openid, |
|
||||
userId: u.userId, |
|
||||
type: u.type |
|
||||
}))); |
|
||||
|
|
||||
// 查询商品信息,特别是拒绝状态的商品
|
|
||||
const products = await sequelize.query( |
|
||||
'SELECT productId, sellerId, productName, status, rejectReason, created_at FROM products LIMIT 20', |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
console.log('\n🛒 商品列表:'); |
|
||||
console.log(products.map(p => ({ |
|
||||
productId: p.productId, |
|
||||
sellerId: p.sellerId, |
|
||||
productName: p.productName, |
|
||||
status: p.status, |
|
||||
rejectReason: p.rejectReason, |
|
||||
created_at: p.created_at |
|
||||
}))); |
|
||||
|
|
||||
// 特别列出拒绝状态的商品
|
|
||||
const rejectedProducts = products.filter(p => p.status === 'rejected'); |
|
||||
console.log('\n❌ 审核拒绝的商品:'); |
|
||||
console.log(rejectedProducts.map(p => ({ |
|
||||
productId: p.productId, |
|
||||
sellerId: p.sellerId, |
|
||||
productName: p.productName, |
|
||||
status: p.status, |
|
||||
rejectReason: p.rejectReason |
|
||||
}))); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 查询失败:', error.message); |
|
||||
} finally { |
|
||||
// 关闭连接
|
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 运行查询
|
|
||||
queryDatabase(); |
|
||||
@ -1,125 +0,0 @@ |
|||||
// 查询userlogin数据库中的personnel表,获取所有采购员数据
|
|
||||
require('dotenv').config({ path: 'd:\\xt\\mian_ly\\server-example\\.env' }); |
|
||||
const mysql = require('mysql2/promise'); |
|
||||
|
|
||||
// 查询personnel表中的采购员数据
|
|
||||
async function queryPersonnelData() { |
|
||||
let connection = null; |
|
||||
try { |
|
||||
console.log('连接到userlogin数据库...'); |
|
||||
connection = await mysql.createConnection({ |
|
||||
host: '1.95.162.61', // 直接使用正确的主机地址
|
|
||||
user: process.env.DB_USER || 'root', |
|
||||
password: process.env.DB_PASSWORD || 'schl@2025', // 直接使用默认密码
|
|
||||
database: 'userlogin', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
}); |
|
||||
console.log('数据库连接成功\n'); |
|
||||
|
|
||||
// 首先查询personnel表结构
|
|
||||
console.log('=== personnel表结构 ==='); |
|
||||
const [columns] = await connection.execute( |
|
||||
'SHOW COLUMNS FROM personnel' |
|
||||
); |
|
||||
console.log('字段列表:', columns.map(col => col.Field).join(', ')); |
|
||||
console.log(); |
|
||||
|
|
||||
// 查询projectName为采购员的数据
|
|
||||
console.log('=== 所有采购员数据 ==='); |
|
||||
const [personnelData] = await connection.execute( |
|
||||
'SELECT * FROM personnel WHERE projectName = ?', |
|
||||
['采购员'] |
|
||||
); |
|
||||
|
|
||||
console.log(`找到 ${personnelData.length} 名采购员记录`); |
|
||||
console.log('\n采购员数据详情:'); |
|
||||
|
|
||||
// 格式化输出数据
|
|
||||
personnelData.forEach((person, index) => { |
|
||||
console.log(`\n${index + 1}. 采购员信息:`); |
|
||||
console.log(` - ID: ${person.id || 'N/A'}`); |
|
||||
console.log(` - 用户ID: ${person.userId || 'N/A'}`); |
|
||||
console.log(` - 姓名: ${person.name || 'N/A'}`); |
|
||||
console.log(` - 别名: ${person.alias || 'N/A'}`); |
|
||||
console.log(` - 电话号码: ${person.phoneNumber || 'N/A'}`); |
|
||||
console.log(` - 公司: ${person.managercompany || 'N/A'}`); |
|
||||
console.log(` - 部门: ${person.managerdepartment || 'N/A'}`); |
|
||||
console.log(` - 角色: ${person.role || 'N/A'}`); |
|
||||
}); |
|
||||
|
|
||||
// 输出JSON格式,便于复制使用
|
|
||||
console.log('\n=== JSON格式数据 (用于客服页面) ==='); |
|
||||
const customerServiceData = personnelData.map((person, index) => ({ |
|
||||
id: index + 1, |
|
||||
managerId: person.userId || `PM${String(index + 1).padStart(3, '0')}`, |
|
||||
managercompany: person.managercompany || '未知公司', |
|
||||
managerdepartment: person.managerdepartment || '采购部', |
|
||||
organization: person.organization || '采购组', |
|
||||
projectName: person.role || '采购员', |
|
||||
name: person.name || '未知', |
|
||||
alias: person.alias || person.name || '未知', |
|
||||
phoneNumber: person.phoneNumber || '', |
|
||||
avatarUrl: '', |
|
||||
score: 990 + (index % 10), |
|
||||
isOnline: index % 4 !== 0, // 75%的在线率
|
|
||||
responsibleArea: `${getRandomArea()}鸡蛋采购`, |
|
||||
experience: getRandomExperience(), |
|
||||
serviceCount: getRandomNumber(100, 300), |
|
||||
purchaseCount: getRandomNumber(10000, 30000), |
|
||||
profitIncreaseRate: getRandomNumber(10, 25), |
|
||||
profitFarmCount: getRandomNumber(50, 200), |
|
||||
skills: getRandomSkills() |
|
||||
})); |
|
||||
|
|
||||
console.log(JSON.stringify(customerServiceData, null, 2)); |
|
||||
|
|
||||
return customerServiceData; |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('查询过程中发生错误:', error); |
|
||||
console.error('错误详情:', error.stack); |
|
||||
return null; |
|
||||
} finally { |
|
||||
if (connection) { |
|
||||
await connection.end(); |
|
||||
console.log('\n数据库连接已关闭'); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 辅助函数:生成随机区域
|
|
||||
function getRandomArea() { |
|
||||
const areas = ['华北区', '华东区', '华南区', '全国', '西南区', '西北区', '东北区']; |
|
||||
return areas[Math.floor(Math.random() * areas.length)]; |
|
||||
} |
|
||||
|
|
||||
// 辅助函数:生成随机工作经验
|
|
||||
function getRandomExperience() { |
|
||||
const experiences = ['1-2年', '1-3年', '2-3年', '3-5年', '5年以上']; |
|
||||
return experiences[Math.floor(Math.random() * experiences.length)]; |
|
||||
} |
|
||||
|
|
||||
// 辅助函数:生成随机数字
|
|
||||
function getRandomNumber(min, max) { |
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min; |
|
||||
} |
|
||||
|
|
||||
// 辅助函数:生成随机技能
|
|
||||
function getRandomSkills() { |
|
||||
const allSkills = ['渠道拓展', '供应商维护', '质量把控', '精准把控市场价格', '谈判技巧', '库存管理']; |
|
||||
const skillCount = Math.floor(Math.random() * 3) + 2; // 2-4个技能
|
|
||||
const selectedSkills = []; |
|
||||
|
|
||||
while (selectedSkills.length < skillCount) { |
|
||||
const skill = allSkills[Math.floor(Math.random() * allSkills.length)]; |
|
||||
if (!selectedSkills.includes(skill)) { |
|
||||
selectedSkills.push(skill); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return selectedSkills; |
|
||||
} |
|
||||
|
|
||||
// 运行查询
|
|
||||
queryPersonnelData(); |
|
||||
@ -1,50 +0,0 @@ |
|||||
// 简化版修复脚本:将reviewed状态商品更新为published
|
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
require('dotenv').config(); |
|
||||
|
|
||||
// 使用与update-product-review-status.js相同的数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
async function fix() { |
|
||||
console.log('===== 开始修复重复货源问题 ====='); |
|
||||
try { |
|
||||
// 连接数据库
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('数据库连接成功'); |
|
||||
|
|
||||
// 直接执行SQL更新reviewed状态为published
|
|
||||
const [result] = await sequelize.query( |
|
||||
'UPDATE products SET status = "published", updated_at = NOW() WHERE status = "reviewed"' |
|
||||
); |
|
||||
|
|
||||
console.log(`成功更新了 ${result.affectedRows} 个商品`); |
|
||||
console.log('===== 修复完成 ====='); |
|
||||
console.log('1. 数据库中的商品状态已更新'); |
|
||||
console.log('2. 请在小程序中下拉刷新页面查看效果'); |
|
||||
console.log('3. 已解决手动更新数据库状态后重复货源的问题'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('修复失败:', error.message); |
|
||||
console.error('请检查数据库连接和权限后重试'); |
|
||||
} finally { |
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
fix(); |
|
||||
@ -1,96 +0,0 @@ |
|||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 日志文件路径
|
|
||||
const logFilePath = path.join(__dirname, 'logs', 'output.log'); |
|
||||
|
|
||||
// 读取日志文件并搜索关键字
|
|
||||
function searchGrossWeightLogs() { |
|
||||
try { |
|
||||
console.log(`正在搜索日志文件: ${logFilePath}`); |
|
||||
console.log('寻找商品发布请求和毛重处理相关记录...\n'); |
|
||||
|
|
||||
// 读取日志文件内容
|
|
||||
const logContent = fs.readFileSync(logFilePath, 'utf-8'); |
|
||||
|
|
||||
// 分割日志文件为块,每块1000行
|
|
||||
const lines = logContent.split('\n'); |
|
||||
const chunkSize = 1000; |
|
||||
const chunks = []; |
|
||||
|
|
||||
for (let i = 0; i < lines.length; i += chunkSize) { |
|
||||
chunks.push(lines.slice(i, i + chunkSize).join('\n')); |
|
||||
} |
|
||||
|
|
||||
// 存储找到的相关记录
|
|
||||
const productPublishRecords = []; |
|
||||
const grossWeightProcessRecords = []; |
|
||||
|
|
||||
// 搜索最近的商品发布请求和毛重处理记录
|
|
||||
chunks.forEach((chunk, chunkIndex) => { |
|
||||
const startLine = chunkIndex * chunkSize + 1; |
|
||||
|
|
||||
// 搜索商品发布请求标志
|
|
||||
const publishMatches = chunk.matchAll(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*处理请求: POST \/api\/product\/publish/g); |
|
||||
for (const match of publishMatches) { |
|
||||
const lineNumber = startLine + chunk.substring(0, match.index).split('\n').length; |
|
||||
productPublishRecords.push({ |
|
||||
timestamp: match[1], |
|
||||
lineNumber: lineNumber, |
|
||||
content: match[0] |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 搜索毛重处理相关记录
|
|
||||
const weightMatches = chunk.matchAll(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}).*grossWeight|原始毛重值|毛重处理|grossWeightStored/g); |
|
||||
for (const match of weightMatches) { |
|
||||
const lineNumber = startLine + chunk.substring(0, match.index).split('\n').length; |
|
||||
grossWeightProcessRecords.push({ |
|
||||
timestamp: match[1] || '未知', |
|
||||
lineNumber: lineNumber, |
|
||||
content: match[0] |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 输出结果
|
|
||||
console.log('===== 毛重相关日志搜索结果 =====\n'); |
|
||||
|
|
||||
// 显示最近的商品发布请求
|
|
||||
console.log(`找到 ${productPublishRecords.length} 条商品发布请求记录`); |
|
||||
if (productPublishRecords.length > 0) { |
|
||||
console.log('\n最近的5条商品发布请求:'); |
|
||||
productPublishRecords.slice(-5).forEach((record, index) => { |
|
||||
console.log(`[${record.timestamp}] 第${record.lineNumber}行: ${record.content}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 显示最近的毛重处理记录
|
|
||||
console.log('\n\n最近的10条毛重处理记录:'); |
|
||||
grossWeightProcessRecords.slice(-10).forEach((record, index) => { |
|
||||
console.log(`[${record.timestamp}] 第${record.lineNumber}行: ${record.content}`); |
|
||||
}); |
|
||||
|
|
||||
// 给出建议
|
|
||||
console.log('\n\n===== 排查建议 ====='); |
|
||||
if (productPublishRecords.length > 0) { |
|
||||
const latestPublish = productPublishRecords[productPublishRecords.length - 1]; |
|
||||
console.log(`1. 最近的商品发布请求在第${latestPublish.lineNumber}行,请查看该请求附近的详细日志`); |
|
||||
} |
|
||||
console.log('2. 手动搜索日志中的关键字:'); |
|
||||
console.log(' - "grossWeight" - 查看所有毛重字段相关记录'); |
|
||||
console.log(' - "原始毛重值" - 查看后端处理的原始值'); |
|
||||
console.log(' - "最终处理的毛重值" - 查看处理后的存储值'); |
|
||||
console.log('3. 检查前端传递的grossWeight格式是否正确'); |
|
||||
console.log('4. 确认数据库中商品记录的grossWeight字段实际值'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('搜索日志时发生错误:', error.message); |
|
||||
console.log('\n请尝试使用以下命令手动查看日志:'); |
|
||||
console.log('在PowerShell中运行:'); |
|
||||
console.log(`Get-Content -Path "${logFilePath}" | Select-String -Pattern "grossWeight|publish" -Context 2,5 | Select-Object -Last 20`); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行搜索
|
|
||||
searchGrossWeightLogs(); |
|
||||
@ -1,145 +0,0 @@ |
|||||
// 简化版毛重字段修复验证脚本
|
|
||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 定义配置
|
|
||||
const config = { |
|
||||
serverFilePath: path.join(__dirname, 'server-mysql.js'), |
|
||||
reportPath: path.join(__dirname, 'gross-weight-fix-report.txt') |
|
||||
}; |
|
||||
|
|
||||
// 主函数
|
|
||||
function main() { |
|
||||
console.log('===== 开始验证毛重字段修复效果 =====\n'); |
|
||||
|
|
||||
// 清空报告文件
|
|
||||
try { |
|
||||
fs.writeFileSync(config.reportPath, '毛重字段修复验证报告 - ' + new Date().toISOString() + '\n\n'); |
|
||||
} catch (error) { |
|
||||
console.error('无法创建报告文件:', error.message); |
|
||||
} |
|
||||
|
|
||||
// 验证中间件修复
|
|
||||
verifyMiddlewareFix(); |
|
||||
|
|
||||
// 检查商品上传接口
|
|
||||
checkUploadApi(); |
|
||||
|
|
||||
// 提供总结
|
|
||||
provideSummary(); |
|
||||
} |
|
||||
|
|
||||
// 日志函数
|
|
||||
function log(message) { |
|
||||
console.log(message); |
|
||||
try { |
|
||||
fs.appendFileSync(config.reportPath, message + '\n'); |
|
||||
} catch (error) { |
|
||||
// 忽略日志写入错误
|
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 读取文件内容
|
|
||||
function readFile(filePath) { |
|
||||
try { |
|
||||
return fs.readFileSync(filePath, 'utf8'); |
|
||||
} catch (error) { |
|
||||
log('读取文件失败: ' + error.message); |
|
||||
return null; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 验证中间件修复
|
|
||||
function verifyMiddlewareFix() { |
|
||||
log('===== 验证中间件毛重处理逻辑 ====='); |
|
||||
|
|
||||
const content = readFile(config.serverFilePath); |
|
||||
if (!content) { |
|
||||
log('无法读取服务器文件'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 检查中间件中的毛重默认值
|
|
||||
const zeroCount = (content.match(/product\.grossWeight\s*=\s*0;/g) || []).length; |
|
||||
const fiveCount = (content.match(/product\.grossWeight\s*=\s*5;/g) || []).length; |
|
||||
|
|
||||
log('中间件中毛重默认值为0的数量: ' + zeroCount); |
|
||||
log('中间件中毛重默认值为5的数量: ' + fiveCount); |
|
||||
|
|
||||
if (zeroCount === 0 && fiveCount > 0) { |
|
||||
log('✓ 中间件修复成功:所有空值毛重都将被设置为5'); |
|
||||
} else if (zeroCount > 0 && fiveCount === 0) { |
|
||||
log('✗ 中间件修复失败:所有空值毛重仍然被设置为0'); |
|
||||
} else if (zeroCount > 0 && fiveCount > 0) { |
|
||||
log('⚠ 中间件部分修复:存在混合的默认值设置,需要进一步检查'); |
|
||||
} else { |
|
||||
log('ℹ 未找到中间件中的毛重默认值设置'); |
|
||||
} |
|
||||
|
|
||||
log(''); |
|
||||
} |
|
||||
|
|
||||
// 检查商品上传接口
|
|
||||
function checkUploadApi() { |
|
||||
log('===== 检查商品上传接口 ====='); |
|
||||
|
|
||||
const content = readFile(config.serverFilePath); |
|
||||
if (!content) { |
|
||||
log('无法读取服务器文件'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 查找商品上传接口
|
|
||||
const uploadApiPattern = /app\.post\('\/api\/products\/upload',/; |
|
||||
const match = content.match(uploadApiPattern); |
|
||||
|
|
||||
if (match) { |
|
||||
log('找到商品上传接口'); |
|
||||
|
|
||||
// 查找接口的大致位置(行号)
|
|
||||
const lines = content.substring(0, match.index).split('\n'); |
|
||||
const lineNumber = lines.length + 1; |
|
||||
log('商品上传接口位于第 ' + lineNumber + ' 行附近'); |
|
||||
|
|
||||
// 检查是否包含毛重处理逻辑
|
|
||||
const uploadApiContent = content.substring(match.index, Math.min(match.index + 500, content.length)); |
|
||||
const hasGrossWeightHandling = uploadApiContent.includes('grossWeight') && |
|
||||
(uploadApiContent.includes('parseFloat') || |
|
||||
uploadApiContent.includes('5') || |
|
||||
uploadApiContent.includes('默认值')); |
|
||||
|
|
||||
if (hasGrossWeightHandling) { |
|
||||
log('✓ 商品上传接口已包含毛重处理逻辑'); |
|
||||
} else { |
|
||||
log('✗ 商品上传接口缺少毛重处理逻辑'); |
|
||||
log('建议手动添加毛重处理逻辑'); |
|
||||
} |
|
||||
} else { |
|
||||
log('未找到商品上传接口'); |
|
||||
} |
|
||||
|
|
||||
log(''); |
|
||||
} |
|
||||
|
|
||||
// 提供总结
|
|
||||
function provideSummary() { |
|
||||
log('===== 毛重字段修复总结 ====='); |
|
||||
log('1. 中间件修复状态: ✓ 已统一所有中间件中的毛重默认值为5'); |
|
||||
log('2. 商品上传接口修复状态: ✗ 未成功添加毛重处理逻辑'); |
|
||||
log(''); |
|
||||
log('建议操作:'); |
|
||||
log('1. 重启服务器以应用中间件的修复'); |
|
||||
log('2. 手动在商品上传接口中添加毛重处理逻辑'); |
|
||||
log('3. 使用现有的test-gross-weight-fix.js测试脚本验证修复效果'); |
|
||||
log(''); |
|
||||
log('修复说明:'); |
|
||||
log('- 中间件修复确保了返回给前端的商品列表中,空值毛重将显示为5'); |
|
||||
log('- 商品上传接口需要手动修改,确保正确处理用户输入的毛重值'); |
|
||||
log('- 已创建备份文件,如有需要可恢复'); |
|
||||
log(''); |
|
||||
log('===== 验证完成 ====='); |
|
||||
log('报告已保存至: ' + config.reportPath); |
|
||||
} |
|
||||
|
|
||||
// 执行主函数
|
|
||||
main(); |
|
||||
@ -1,36 +0,0 @@ |
|||||
// 最简单的TCP端口检查脚本
|
|
||||
const net = require('net'); |
|
||||
|
|
||||
const PORT = 3001; |
|
||||
const HOST = 'localhost'; |
|
||||
|
|
||||
console.log(`正在检查 ${HOST}:${PORT} 端口...`); |
|
||||
|
|
||||
const client = new net.Socket(); |
|
||||
let isOpen = false; |
|
||||
|
|
||||
client.setTimeout(2000); |
|
||||
|
|
||||
client.connect(PORT, HOST, () => { |
|
||||
isOpen = true; |
|
||||
console.log(`✅ 端口 ${PORT} 已开放!服务器正在运行。`); |
|
||||
client.destroy(); |
|
||||
}); |
|
||||
|
|
||||
client.on('error', (e) => { |
|
||||
if (e.code === 'ECONNREFUSED') { |
|
||||
console.log(`❌ 端口 ${PORT} 未开放或被拒绝连接。`); |
|
||||
} else { |
|
||||
console.error(`❌ 连接错误: ${e.message}`); |
|
||||
} |
|
||||
client.destroy(); |
|
||||
}); |
|
||||
|
|
||||
client.on('timeout', () => { |
|
||||
console.log(`❌ 端口 ${PORT} 连接超时。`); |
|
||||
client.destroy(); |
|
||||
}); |
|
||||
|
|
||||
client.on('close', () => { |
|
||||
console.log('\n检查完成。'); |
|
||||
}); |
|
||||
@ -1,110 +0,0 @@ |
|||||
// 简单的聊天功能验证脚本
|
|
||||
const mysql = require('mysql2/promise'); |
|
||||
|
|
||||
// 数据库连接配置
|
|
||||
const config = { |
|
||||
host: '1.95.162.61', |
|
||||
port: 3306, |
|
||||
user: 'root', |
|
||||
password: 'schl@2025', |
|
||||
database: 'wechat_app' |
|
||||
}; |
|
||||
|
|
||||
async function verifyChatFix() { |
|
||||
let connection; |
|
||||
try { |
|
||||
// 连接数据库
|
|
||||
connection = await mysql.createConnection(config); |
|
||||
console.log('Database connection successful'); |
|
||||
|
|
||||
// 测试用户ID(模拟实际的字符串ID)
|
|
||||
const testUserId = 'test_user_' + Date.now(); |
|
||||
const testManagerId = '22'; |
|
||||
const testConversationId = 'conv_' + Date.now(); |
|
||||
|
|
||||
console.log('\nTest user ID:', testUserId); |
|
||||
console.log('Test manager ID:', testManagerId); |
|
||||
|
|
||||
// 1. 创建测试会话
|
|
||||
console.log('\nCreating test conversation...'); |
|
||||
await connection.execute( |
|
||||
'INSERT INTO chat_conversations (conversation_id, userId, managerId, status) VALUES (?, ?, ?, 1)', |
|
||||
[testConversationId, testUserId, testManagerId] |
|
||||
); |
|
||||
console.log('✓ Conversation created successfully'); |
|
||||
|
|
||||
// 2. 验证会话数据
|
|
||||
const [conversations] = await connection.execute( |
|
||||
'SELECT * FROM chat_conversations WHERE conversation_id = ?', |
|
||||
[testConversationId] |
|
||||
); |
|
||||
|
|
||||
if (conversations.length > 0) { |
|
||||
const conv = conversations[0]; |
|
||||
console.log('\nVerifying conversation data:'); |
|
||||
console.log(' userId stored as:', conv.userId); |
|
||||
console.log(' userId type:', typeof conv.userId); |
|
||||
|
|
||||
if (conv.userId === testUserId) { |
|
||||
console.log('✓ String userId stored correctly'); |
|
||||
} else { |
|
||||
console.log('❌ userId mismatch!'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 3. 测试查询功能
|
|
||||
const [queryResult] = await connection.execute( |
|
||||
'SELECT * FROM chat_conversations WHERE userId = ? AND managerId = ?', |
|
||||
[testUserId, testManagerId] |
|
||||
); |
|
||||
|
|
||||
console.log('\nQuery test result:', queryResult.length, 'records found'); |
|
||||
|
|
||||
// 4. 测试发送一条消息
|
|
||||
const testMessage = 'Test message at ' + new Date().toISOString(); |
|
||||
|
|
||||
// 检查chat_messages表是否存在
|
|
||||
const [tableCheck] = await connection.execute( |
|
||||
"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = 'wechat_app' AND TABLE_NAME = 'chat_messages'" |
|
||||
); |
|
||||
|
|
||||
if (tableCheck.length > 0) { |
|
||||
console.log('\nTesting message storage...'); |
|
||||
|
|
||||
await connection.execute( |
|
||||
'INSERT INTO chat_messages (conversation_id, sender_id, receiver_id, content, content_type) VALUES (?, ?, ?, ?, ?)', |
|
||||
[testConversationId, testUserId, testManagerId, testMessage, 1] |
|
||||
); |
|
||||
console.log('✓ Message stored successfully'); |
|
||||
|
|
||||
// 验证消息存储
|
|
||||
const [messages] = await connection.execute( |
|
||||
'SELECT * FROM chat_messages WHERE conversation_id = ?', |
|
||||
[testConversationId] |
|
||||
); |
|
||||
console.log('Messages found:', messages.length); |
|
||||
if (messages.length > 0) { |
|
||||
console.log('Message sender_id:', messages[0].sender_id); |
|
||||
console.log('Message content:', messages[0].content); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 5. 清理测试数据
|
|
||||
console.log('\nCleaning up test data...'); |
|
||||
if (tableCheck.length > 0) { |
|
||||
await connection.execute('DELETE FROM chat_messages WHERE conversation_id = ?', [testConversationId]); |
|
||||
} |
|
||||
await connection.execute('DELETE FROM chat_conversations WHERE conversation_id = ?', [testConversationId]); |
|
||||
console.log('✓ Test data cleaned up'); |
|
||||
|
|
||||
console.log('\n🎉 Chat functionality fix verified successfully!'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('Verification error:', error.message); |
|
||||
} finally { |
|
||||
if (connection) await connection.end(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// Run verification
|
|
||||
verifyChatFix(); |
|
||||
@ -1,108 +0,0 @@ |
|||||
// sync-review-status.js
|
|
||||
// 这个脚本用于修复手动在数据库中将pending_review改为reviewed后小程序不显示正确状态的问题
|
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
require('dotenv').config(); |
|
||||
|
|
||||
// 获取命令行参数
|
|
||||
const args = process.argv.slice(2); |
|
||||
const autoPublish = args.includes('--publish'); |
|
||||
const forceUpdate = args.includes('--force'); |
|
||||
|
|
||||
// 使用与simple-fix.js相同的数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
define: { |
|
||||
timestamps: false, |
|
||||
freezeTableName: true |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 主要同步函数 - 直接使用SQL查询以避免模型定义问题
|
|
||||
async function syncReviewStatus() { |
|
||||
try { |
|
||||
console.log('===== 开始同步已审核状态... ====='); |
|
||||
|
|
||||
// 验证数据库连接
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('数据库连接成功'); |
|
||||
|
|
||||
// 1. 查找所有状态为reviewed的商品
|
|
||||
const [reviewedProducts, _] = await sequelize.query( |
|
||||
'SELECT id, productId, productName, status FROM products WHERE status = "reviewed"' |
|
||||
); |
|
||||
|
|
||||
console.log(`找到 ${reviewedProducts.length} 个状态为reviewed的商品`); |
|
||||
|
|
||||
// 显示找到的商品信息
|
|
||||
if (reviewedProducts.length > 0) { |
|
||||
console.log('\n已审核商品列表:'); |
|
||||
reviewedProducts.forEach(product => { |
|
||||
console.log(`- ID: ${product.id}, 商品ID: ${product.productId}, 名称: ${product.productName}, 状态: ${product.status}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 2. 如果启用了自动发布功能,将reviewed状态商品更新为published
|
|
||||
if (autoPublish && reviewedProducts.length > 0) { |
|
||||
console.log('\n===== 开始自动发布已审核商品 ====='); |
|
||||
|
|
||||
// 执行批量更新
|
|
||||
const [result] = await sequelize.query( |
|
||||
'UPDATE products SET status = "published", updated_at = NOW() WHERE status = "reviewed"' |
|
||||
); |
|
||||
|
|
||||
console.log(`成功将 ${result.affectedRows} 个商品从reviewed状态更新为published状态`); |
|
||||
console.log('商品状态更新完成!现在这些商品在小程序中应该显示为已上架状态。'); |
|
||||
} |
|
||||
|
|
||||
// 3. 提供状态转换建议
|
|
||||
if (reviewedProducts.length > 0) { |
|
||||
console.log('\n操作建议:'); |
|
||||
if (autoPublish) { |
|
||||
console.log('✅ 已自动将所有reviewed状态商品更新为published状态'); |
|
||||
} else { |
|
||||
console.log('1. 在小程序中下拉刷新卖家页面,查看更新后的状态'); |
|
||||
console.log('2. 可以在小程序中直接将这些商品上架(会自动变为published状态)'); |
|
||||
console.log('3. 如需批量将reviewed状态转为published,请运行: node sync-review-status.js --publish'); |
|
||||
} |
|
||||
console.log('4. 如果仍然存在问题,请运行: node sync-review-status.js --force --publish 强制执行更新'); |
|
||||
} |
|
||||
|
|
||||
console.log('\n===== 同步完成! ====='); |
|
||||
console.log('注意:如果您在数据库中手动修改了商品状态,小程序需要重新从服务器同步数据才能显示最新状态。'); |
|
||||
console.log('请在小程序中下拉刷新页面或重新进入卖家页面。'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('同步过程中发生错误:', error.message); |
|
||||
console.log('请检查数据库连接和权限后重试。'); |
|
||||
console.log('尝试使用 --force 参数强制执行: node sync-review-status.js --force --publish'); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await sequelize.close(); |
|
||||
console.log('数据库连接已关闭'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 执行同步
|
|
||||
console.log('=== 商品审核状态同步工具 ==='); |
|
||||
console.log('此工具用于检查并同步已审核(reviewed)状态的商品'); |
|
||||
console.log('解决手动在数据库修改状态后小程序显示不正确的问题\n'); |
|
||||
console.log('使用方法:'); |
|
||||
console.log(' - 查看状态: node sync-review-status.js'); |
|
||||
console.log(' - 自动发布: node sync-review-status.js --publish'); |
|
||||
console.log(' - 强制更新: node sync-review-status.js --force --publish\n'); |
|
||||
|
|
||||
syncReviewStatus(); |
|
||||
@ -1,360 +0,0 @@ |
|||||
// 测试新的身份验证机制
|
|
||||
const WebSocket = require('ws'); |
|
||||
const readline = require('readline'); |
|
||||
const http = require('http'); |
|
||||
|
|
||||
// 创建readline接口用于用户输入
|
|
||||
const rl = readline.createInterface({ |
|
||||
input: process.stdin, |
|
||||
output: process.stdout |
|
||||
}); |
|
||||
|
|
||||
// 服务器地址
|
|
||||
const SERVER_URL = 'ws://localhost:3003'; |
|
||||
const API_URL = 'http://localhost:3003/api/managers'; |
|
||||
|
|
||||
// 测试用例数据
|
|
||||
const TEST_DATA = { |
|
||||
// 有效的客服ID
|
|
||||
validManagerId: '22', |
|
||||
// 无效的客服ID
|
|
||||
invalidManagerId: '9999', |
|
||||
// 有效的普通用户ID
|
|
||||
validUserId: 'user_123456', |
|
||||
// 无效的普通用户ID
|
|
||||
invalidUserId: 'user_999999' |
|
||||
}; |
|
||||
|
|
||||
console.log('===== 开始测试新的身份验证机制 ====='); |
|
||||
console.log('测试目标:验证新实现的用户和客服身份验证逻辑'); |
|
||||
|
|
||||
/** |
|
||||
* 测试场景1:有效的普通用户认证 |
|
||||
*/ |
|
||||
async function testValidUserAuthentication() { |
|
||||
console.log('\n=== 测试场景1: 有效的普通用户认证 ==='); |
|
||||
|
|
||||
return new Promise((resolve) => { |
|
||||
const ws = new WebSocket(SERVER_URL); |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
console.log('✅ WebSocket连接已建立'); |
|
||||
|
|
||||
// 准备认证消息
|
|
||||
const authMessage = { |
|
||||
type: 'auth', |
|
||||
userId: TEST_DATA.validUserId, |
|
||||
userType: 'customer', |
|
||||
timestamp: Date.now() |
|
||||
}; |
|
||||
|
|
||||
console.log(`发送用户认证请求: ${JSON.stringify(authMessage)}`); |
|
||||
ws.send(JSON.stringify(authMessage)); |
|
||||
}); |
|
||||
|
|
||||
ws.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('收到服务器响应:', JSON.stringify(message)); |
|
||||
|
|
||||
// 检查认证结果
|
|
||||
if (message.type === 'auth_success') { |
|
||||
console.log('✅ 测试成功: 有效的普通用户ID正确通过认证'); |
|
||||
resolve({ success: true, scenario: 'valid_user' }); |
|
||||
} else if (message.type === 'auth_error') { |
|
||||
console.error('❌ 测试失败: 有效的普通用户ID被错误拒绝'); |
|
||||
resolve({ success: false, scenario: 'valid_user', error: message.message }); |
|
||||
} |
|
||||
|
|
||||
// 关闭连接
|
|
||||
ws.close(); |
|
||||
}); |
|
||||
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('WebSocket错误:', error); |
|
||||
resolve({ success: false, scenario: 'valid_user', error: error.message }); |
|
||||
}); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
console.error('❌ 测试超时: 未收到服务器响应'); |
|
||||
ws.close(); |
|
||||
resolve({ success: false, scenario: 'valid_user', error: 'timeout' }); |
|
||||
}, 10000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 测试场景2:无效的普通用户认证 |
|
||||
*/ |
|
||||
async function testInvalidUserAuthentication() { |
|
||||
console.log('\n=== 测试场景2: 无效的普通用户认证 ==='); |
|
||||
|
|
||||
return new Promise((resolve) => { |
|
||||
const ws = new WebSocket(SERVER_URL); |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
console.log('✅ WebSocket连接已建立'); |
|
||||
|
|
||||
// 准备认证消息
|
|
||||
const authMessage = { |
|
||||
type: 'auth', |
|
||||
userId: TEST_DATA.invalidUserId, |
|
||||
userType: 'customer', |
|
||||
timestamp: Date.now() |
|
||||
}; |
|
||||
|
|
||||
console.log(`发送无效用户认证请求: ${JSON.stringify(authMessage)}`); |
|
||||
ws.send(JSON.stringify(authMessage)); |
|
||||
}); |
|
||||
|
|
||||
ws.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('收到服务器响应:', JSON.stringify(message)); |
|
||||
|
|
||||
// 检查是否收到认证错误消息
|
|
||||
if (message.type === 'auth_error') { |
|
||||
console.log('✅ 测试成功: 无效的用户ID正确被拒绝认证'); |
|
||||
resolve({ success: true, scenario: 'invalid_user' }); |
|
||||
} else if (message.type === 'auth_success') { |
|
||||
console.error('❌ 测试失败: 无效的用户ID错误地通过了认证'); |
|
||||
resolve({ success: false, scenario: 'invalid_user', error: 'invalid_user_accepted' }); |
|
||||
} |
|
||||
|
|
||||
// 关闭连接
|
|
||||
ws.close(); |
|
||||
}); |
|
||||
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('WebSocket错误:', error); |
|
||||
resolve({ success: false, scenario: 'invalid_user', error: error.message }); |
|
||||
}); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
console.error('❌ 测试超时: 未收到服务器响应'); |
|
||||
ws.close(); |
|
||||
resolve({ success: false, scenario: 'invalid_user', error: 'timeout' }); |
|
||||
}, 10000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 测试场景3:有效的客服认证 |
|
||||
*/ |
|
||||
async function testValidManagerAuthentication() { |
|
||||
console.log('\n=== 测试场景3: 有效的客服认证 ==='); |
|
||||
|
|
||||
return new Promise((resolve) => { |
|
||||
const ws = new WebSocket(SERVER_URL); |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
console.log('✅ WebSocket连接已建立'); |
|
||||
|
|
||||
// 准备客服认证消息
|
|
||||
const authMessage = { |
|
||||
type: 'auth', |
|
||||
managerId: TEST_DATA.validManagerId, |
|
||||
userType: 'manager', |
|
||||
timestamp: Date.now() |
|
||||
}; |
|
||||
|
|
||||
console.log(`发送客服认证请求: ${JSON.stringify(authMessage)}`); |
|
||||
ws.send(JSON.stringify(authMessage)); |
|
||||
}); |
|
||||
|
|
||||
ws.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('收到服务器响应:', JSON.stringify(message)); |
|
||||
|
|
||||
// 检查认证结果
|
|
||||
if (message.type === 'auth_success' && |
|
||||
(message.payload && message.payload.type === 'manager') || |
|
||||
(message.userType === 'manager')) { |
|
||||
console.log('✅ 测试成功: 有效的客服ID正确通过认证'); |
|
||||
resolve({ success: true, scenario: 'valid_manager' }); |
|
||||
} else if (message.type === 'auth_error') { |
|
||||
console.error('❌ 测试失败: 有效的客服ID被错误拒绝'); |
|
||||
resolve({ success: false, scenario: 'valid_manager', error: message.message }); |
|
||||
} |
|
||||
|
|
||||
// 关闭连接
|
|
||||
ws.close(); |
|
||||
}); |
|
||||
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('WebSocket错误:', error); |
|
||||
resolve({ success: false, scenario: 'valid_manager', error: error.message }); |
|
||||
}); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
console.error('❌ 测试超时: 未收到服务器响应'); |
|
||||
ws.close(); |
|
||||
resolve({ success: false, scenario: 'valid_manager', error: 'timeout' }); |
|
||||
}, 10000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 测试场景4:无效的客服认证 |
|
||||
*/ |
|
||||
async function testInvalidManagerAuthentication() { |
|
||||
console.log('\n=== 测试场景4: 无效的客服认证 ==='); |
|
||||
|
|
||||
return new Promise((resolve) => { |
|
||||
const ws = new WebSocket(SERVER_URL); |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
console.log('✅ WebSocket连接已建立'); |
|
||||
|
|
||||
// 准备无效客服认证消息
|
|
||||
const authMessage = { |
|
||||
type: 'auth', |
|
||||
managerId: TEST_DATA.invalidManagerId, |
|
||||
userType: 'manager', |
|
||||
timestamp: Date.now() |
|
||||
}; |
|
||||
|
|
||||
console.log(`发送无效客服认证请求: ${JSON.stringify(authMessage)}`); |
|
||||
ws.send(JSON.stringify(authMessage)); |
|
||||
}); |
|
||||
|
|
||||
ws.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('收到服务器响应:', JSON.stringify(message)); |
|
||||
|
|
||||
// 检查是否收到认证错误消息
|
|
||||
if (message.type === 'auth_error') { |
|
||||
console.log('✅ 测试成功: 无效的客服ID正确被拒绝认证'); |
|
||||
resolve({ success: true, scenario: 'invalid_manager' }); |
|
||||
} else if (message.type === 'auth_success') { |
|
||||
console.error('❌ 测试失败: 无效的客服ID错误地通过了认证'); |
|
||||
resolve({ success: false, scenario: 'invalid_manager', error: 'invalid_manager_accepted' }); |
|
||||
} |
|
||||
|
|
||||
// 关闭连接
|
|
||||
ws.close(); |
|
||||
}); |
|
||||
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('WebSocket错误:', error); |
|
||||
resolve({ success: false, scenario: 'invalid_manager', error: error.message }); |
|
||||
}); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
console.error('❌ 测试超时: 未收到服务器响应'); |
|
||||
ws.close(); |
|
||||
resolve({ success: false, scenario: 'invalid_manager', error: 'timeout' }); |
|
||||
}, 10000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 测试场景5:使用普通userId作为managerId的认证(应该失败) |
|
||||
*/ |
|
||||
async function testUserIdAsManagerId() { |
|
||||
console.log('\n=== 测试场景5: 使用普通userId作为managerId的认证 ==='); |
|
||||
console.log('验证:取消了userId作为managerId的容错处理'); |
|
||||
|
|
||||
return new Promise((resolve) => { |
|
||||
const ws = new WebSocket(SERVER_URL); |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
console.log('✅ WebSocket连接已建立'); |
|
||||
|
|
||||
// 准备使用普通userId作为managerId的认证消息
|
|
||||
const authMessage = { |
|
||||
type: 'auth', |
|
||||
managerId: TEST_DATA.validUserId, // 使用普通userId作为managerId
|
|
||||
userType: 'manager', |
|
||||
timestamp: Date.now() |
|
||||
}; |
|
||||
|
|
||||
console.log(`发送错误格式认证请求: ${JSON.stringify(authMessage)}`); |
|
||||
ws.send(JSON.stringify(authMessage)); |
|
||||
}); |
|
||||
|
|
||||
ws.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('收到服务器响应:', JSON.stringify(message)); |
|
||||
|
|
||||
// 检查是否收到认证错误消息
|
|
||||
if (message.type === 'auth_error') { |
|
||||
console.log('✅ 测试成功: 普通userId作为managerId被正确拒绝'); |
|
||||
resolve({ success: true, scenario: 'userId_as_managerId' }); |
|
||||
} else if (message.type === 'auth_success') { |
|
||||
console.error('❌ 测试失败: 普通userId作为managerId错误地通过了认证'); |
|
||||
resolve({ success: false, scenario: 'userId_as_managerId', error: 'userId_accepted_as_managerId' }); |
|
||||
} |
|
||||
|
|
||||
// 关闭连接
|
|
||||
ws.close(); |
|
||||
}); |
|
||||
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('WebSocket错误:', error); |
|
||||
resolve({ success: false, scenario: 'userId_as_managerId', error: error.message }); |
|
||||
}); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
console.error('❌ 测试超时: 未收到服务器响应'); |
|
||||
ws.close(); |
|
||||
resolve({ success: false, scenario: 'userId_as_managerId', error: 'timeout' }); |
|
||||
}, 10000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 运行所有测试 |
|
||||
*/ |
|
||||
async function runAllTests() { |
|
||||
const results = []; |
|
||||
|
|
||||
// 按顺序运行测试
|
|
||||
try { |
|
||||
// 先测试用户认证
|
|
||||
results.push(await testValidUserAuthentication()); |
|
||||
results.push(await testInvalidUserAuthentication()); |
|
||||
|
|
||||
// 再测试客服认证
|
|
||||
results.push(await testValidManagerAuthentication()); |
|
||||
results.push(await testInvalidManagerAuthentication()); |
|
||||
|
|
||||
// 测试特殊场景
|
|
||||
results.push(await testUserIdAsManagerId()); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('测试过程中发生错误:', error); |
|
||||
} |
|
||||
|
|
||||
// 输出测试总结
|
|
||||
console.log('\n===== 测试结果总结 ====='); |
|
||||
|
|
||||
const passedTests = results.filter(r => r.success).length; |
|
||||
const totalTests = results.length; |
|
||||
|
|
||||
console.log(`总测试数: ${totalTests}`); |
|
||||
console.log(`通过测试: ${passedTests}`); |
|
||||
console.log(`失败测试: ${totalTests - passedTests}`); |
|
||||
|
|
||||
// 输出失败的测试详情
|
|
||||
const failedTests = results.filter(r => !r.success); |
|
||||
if (failedTests.length > 0) { |
|
||||
console.log('\n失败测试详情:'); |
|
||||
failedTests.forEach(test => { |
|
||||
console.log(`- ${test.scenario}: ${test.error || '未知错误'}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 输出最终结果
|
|
||||
const overallResult = passedTests === totalTests; |
|
||||
console.log('\n===== 最终结果 ====='); |
|
||||
console.log(`整体测试结果: ${overallResult ? '✅ 通过' : '❌ 失败'}`); |
|
||||
|
|
||||
rl.close(); |
|
||||
} |
|
||||
|
|
||||
// 启动测试
|
|
||||
runAllTests(); |
|
||||
@ -1,37 +0,0 @@ |
|||||
const http = require('http'); |
|
||||
|
|
||||
const postData = JSON.stringify({ test: 'data' }); |
|
||||
|
|
||||
const options = { |
|
||||
hostname: 'localhost', |
|
||||
port: 3003, |
|
||||
path: '/api/test/post', |
|
||||
method: 'POST', |
|
||||
headers: { |
|
||||
'Content-Type': 'application/json', |
|
||||
'Content-Length': Buffer.byteLength(postData) |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('===== 测试 /api/test/post 接口 =====\n'); |
|
||||
|
|
||||
const req = http.request(options, (res) => { |
|
||||
let data = ''; |
|
||||
|
|
||||
res.on('data', (chunk) => { |
|
||||
data += chunk; |
|
||||
}); |
|
||||
|
|
||||
res.on('end', () => { |
|
||||
console.log('响应状态码:', res.statusCode); |
|
||||
console.log('响应结果:'); |
|
||||
console.log(data); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
req.on('error', (e) => { |
|
||||
console.error('请求失败:', e.message); |
|
||||
}); |
|
||||
|
|
||||
req.write(postData); |
|
||||
req.end(); |
|
||||
@ -1,67 +0,0 @@ |
|||||
const http = require('http'); |
|
||||
|
|
||||
const testData = { |
|
||||
openid: "oAWdd15xKm5H66TNlmjYtky_Iug8", |
|
||||
collaborationid: "chicken", |
|
||||
company: "123", |
|
||||
province: "北京市", |
|
||||
city: "北京市", |
|
||||
district: "东城区", |
|
||||
detailedaddress: "123", |
|
||||
cooperation: "采销联盟合作", |
|
||||
phoneNumber: "18482694520", |
|
||||
businesslicenseurl: "", |
|
||||
proofurl: "", |
|
||||
brandurl: "https://my-supplier-photos.oss-cn-chengdu.aliyuncs.com/settlement/brandAuth/1766729271862_BJrCwyOx1Bugaf5545a3f0d4e140bf21f92323555688.png/image/af5545a3f0d4e140bf21f92323555688.png" |
|
||||
}; |
|
||||
|
|
||||
const postData = JSON.stringify(testData); |
|
||||
|
|
||||
const options = { |
|
||||
hostname: 'localhost', |
|
||||
port: 3003, |
|
||||
path: '/api/settlement/submit', |
|
||||
method: 'POST', |
|
||||
headers: { |
|
||||
'Content-Type': 'application/json', |
|
||||
'Content-Length': Buffer.byteLength(postData) |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
console.log('===== 测试立即入驻API ====='); |
|
||||
console.log('1. 测试数据:', JSON.stringify(testData, null, 2)); |
|
||||
console.log('2. 发送请求到: http://localhost:3003/api/settlement/submit\n'); |
|
||||
|
|
||||
const req = http.request(options, (res) => { |
|
||||
let data = ''; |
|
||||
|
|
||||
res.on('data', (chunk) => { |
|
||||
data += chunk; |
|
||||
}); |
|
||||
|
|
||||
res.on('end', () => { |
|
||||
console.log('3. 响应状态码:', res.statusCode); |
|
||||
console.log('4. 响应结果:'); |
|
||||
try { |
|
||||
const result = JSON.parse(data); |
|
||||
console.log(JSON.stringify(result, null, 2)); |
|
||||
|
|
||||
if (result.success && result.code === 200) { |
|
||||
console.log('\n✅ 入驻申请提交成功!'); |
|
||||
} else { |
|
||||
console.log('\n❌ 入驻申请提交失败:', result.message); |
|
||||
} |
|
||||
} catch (e) { |
|
||||
console.log('响应数据:', data); |
|
||||
console.log('\n❌ JSON解析失败'); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
req.on('error', (e) => { |
|
||||
console.error('❌ 请求失败:', e.message); |
|
||||
console.error('\n请确保后端服务器已启动: node server-mysql.js'); |
|
||||
}); |
|
||||
|
|
||||
req.write(postData); |
|
||||
req.end(); |
|
||||
@ -1,166 +0,0 @@ |
|||||
// 测试用户类型同步修复
|
|
||||
// 此脚本用于验证updateManagerOnlineStatus函数中的用户类型更新逻辑
|
|
||||
|
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 读取环境变量
|
|
||||
const envFile = path.join(__dirname, '.env'); |
|
||||
if (fs.existsSync(envFile)) { |
|
||||
const envContent = fs.readFileSync(envFile, 'utf8'); |
|
||||
envContent.split('\n').forEach(line => { |
|
||||
const match = line.match(/^(\w+)=(.*)$/); |
|
||||
if (match) { |
|
||||
process.env[match[1]] = match[2]; |
|
||||
} |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 数据库配置 - 使用独立的数据源连接,与server-mysql.js保持一致
|
|
||||
const dbConfig = { |
|
||||
host: process.env.DB_HOST || '1.95.162.61', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
user: process.env.DB_USER || 'root', |
|
||||
password: process.env.DB_PASSWORD || '' |
|
||||
}; |
|
||||
|
|
||||
console.log('数据库连接配置:'); |
|
||||
console.log(JSON.stringify(dbConfig, null, 2)); |
|
||||
|
|
||||
// 创建独立的数据源连接
|
|
||||
const wechatAppSequelize = new Sequelize( |
|
||||
'wechat_app', |
|
||||
dbConfig.user, |
|
||||
dbConfig.password, |
|
||||
{ |
|
||||
host: dbConfig.host, |
|
||||
port: dbConfig.port, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
logging: true, |
|
||||
define: { |
|
||||
timestamps: false |
|
||||
}, |
|
||||
timezone: '+00:00' |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
const userLoginSequelize = new Sequelize( |
|
||||
'userlogin', |
|
||||
dbConfig.user, |
|
||||
dbConfig.password, |
|
||||
{ |
|
||||
host: dbConfig.host, |
|
||||
port: dbConfig.port, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
logging: true, |
|
||||
define: { |
|
||||
timestamps: false |
|
||||
}, |
|
||||
timezone: '+00:00' |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 测试电话号码
|
|
||||
const testPhoneNumber = '17780155537'; |
|
||||
|
|
||||
async function testTypeSyncFix() { |
|
||||
console.log('\n=== 开始用户类型同步修复测试 ===\n'); |
|
||||
|
|
||||
try { |
|
||||
// 1. 首先检查users表中当前用户类型 (使用wechatAppSequelize)
|
|
||||
console.log('1. 检查users表中的当前用户类型...'); |
|
||||
const userResult = await wechatAppSequelize.query( |
|
||||
'SELECT * FROM users WHERE phoneNumber = ?', |
|
||||
{ replacements: [testPhoneNumber], type: wechatAppSequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (userResult && userResult.length > 0) { |
|
||||
console.log('用户信息:', userResult[0]); |
|
||||
console.log(`当前用户类型: ${userResult[0].type}`); |
|
||||
} else { |
|
||||
console.log('用户不存在:', testPhoneNumber); |
|
||||
} |
|
||||
|
|
||||
// 2. 检查personnel表中的客服信息 (使用userLoginSequelize)
|
|
||||
console.log('\n2. 检查personnel表中的客服信息...'); |
|
||||
const personnelResult = await userLoginSequelize.query( |
|
||||
'SELECT * FROM personnel WHERE phoneNumber = ?', |
|
||||
{ replacements: [testPhoneNumber], type: userLoginSequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (personnelResult && personnelResult.length > 0) { |
|
||||
console.log('客服信息:', personnelResult[0]); |
|
||||
const managerId = personnelResult[0].id || personnelResult[0].userId; |
|
||||
console.log(`客服managerId: ${managerId}`); |
|
||||
|
|
||||
// 3. 测试用户类型更新逻辑 (使用wechatAppSequelize)
|
|
||||
console.log('\n3. 测试用户类型更新逻辑...'); |
|
||||
const updateResult = await wechatAppSequelize.query( |
|
||||
'UPDATE users SET type = ? WHERE phoneNumber = ? AND type = ?', |
|
||||
{ replacements: ['manager', testPhoneNumber, 'customer'] } |
|
||||
); |
|
||||
|
|
||||
const affectedRows = updateResult[1].affectedRows; |
|
||||
if (affectedRows > 0) { |
|
||||
console.log(`✓ 成功更新用户类型: 手机号=${testPhoneNumber}, 用户类型从customer更新为manager`); |
|
||||
} else { |
|
||||
console.log(`✓ 用户类型无需更新: 手机号=${testPhoneNumber}, 可能已经是manager类型`); |
|
||||
} |
|
||||
|
|
||||
// 4. 验证更新结果 (使用wechatAppSequelize)
|
|
||||
console.log('\n4. 验证更新结果...'); |
|
||||
const updatedUserResult = await wechatAppSequelize.query( |
|
||||
'SELECT * FROM users WHERE phoneNumber = ?', |
|
||||
{ replacements: [testPhoneNumber], type: wechatAppSequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (updatedUserResult && updatedUserResult.length > 0) { |
|
||||
console.log('更新后的用户信息:', updatedUserResult[0]); |
|
||||
console.log(`更新后的用户类型: ${updatedUserResult[0].type}`); |
|
||||
|
|
||||
if (updatedUserResult[0].type === 'manager') { |
|
||||
console.log('✓ 验证成功: 用户类型已更新为manager'); |
|
||||
} else { |
|
||||
console.log('⚠ 验证警告: 用户类型仍为:', updatedUserResult[0].type); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
} else { |
|
||||
console.log('该手机号不是客服'); |
|
||||
} |
|
||||
|
|
||||
// 总结
|
|
||||
console.log('\n=== 测试总结 ==='); |
|
||||
console.log('1. 数据库连接: 成功'); |
|
||||
console.log('2. users表查询: 成功'); |
|
||||
console.log('3. personnel表查询: 成功'); |
|
||||
console.log('4. 用户类型更新逻辑: 已测试'); |
|
||||
console.log('5. 跨数据源操作: 已验证'); |
|
||||
console.log('\n提示: 服务器已重启,客服登录后将会自动更新用户类型'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('测试过程中出现错误:', error); |
|
||||
console.error('错误详情:', error.stack); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await wechatAppSequelize.close(); |
|
||||
await userLoginSequelize.close(); |
|
||||
console.log('\n数据库连接已关闭'); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 运行测试
|
|
||||
testTypeSyncFix(); |
|
||||
@ -1,85 +0,0 @@ |
|||||
const WebSocket = require('ws'); |
|
||||
const readline = require('readline'); |
|
||||
|
|
||||
// 创建readline接口用于用户输入
|
|
||||
const rl = readline.createInterface({ |
|
||||
input: process.stdin, |
|
||||
output: process.stdout |
|
||||
}); |
|
||||
|
|
||||
// 测试WebSocket认证验证
|
|
||||
async function testUserAuthValidation() { |
|
||||
console.log('===== 开始测试用户认证验证 ====='); |
|
||||
console.log('此测试将验证不存在的用户ID是否无法通过认证'); |
|
||||
|
|
||||
// 不存在的用户ID(与日志中的相同)
|
|
||||
const nonExistentUserId = 'user_1765760444819'; |
|
||||
|
|
||||
// 服务器WebSocket地址
|
|
||||
const wsUrl = 'ws://localhost:3003'; |
|
||||
|
|
||||
return new Promise((resolve) => { |
|
||||
// 创建WebSocket连接
|
|
||||
const ws = new WebSocket(wsUrl); |
|
||||
|
|
||||
ws.on('open', () => { |
|
||||
console.log('WebSocket连接已建立'); |
|
||||
|
|
||||
// 准备认证消息
|
|
||||
const authMessage = { |
|
||||
type: 'auth', |
|
||||
userId: nonExistentUserId, |
|
||||
userType: 'customer', |
|
||||
timestamp: Date.now() |
|
||||
}; |
|
||||
|
|
||||
console.log(`发送认证请求: ${JSON.stringify(authMessage)}`); |
|
||||
ws.send(JSON.stringify(authMessage)); |
|
||||
}); |
|
||||
|
|
||||
ws.on('message', (data) => { |
|
||||
const message = JSON.parse(data.toString()); |
|
||||
console.log('收到服务器响应:', JSON.stringify(message)); |
|
||||
|
|
||||
// 检查是否收到认证错误消息
|
|
||||
if (message.type === 'auth_error' && message.message === '用户不存在') { |
|
||||
console.log('✅ 测试成功: 不存在的用户ID正确被拒绝认证'); |
|
||||
resolve(true); |
|
||||
} else if (message.type === 'auth_success') { |
|
||||
console.log('❌ 测试失败: 不存在的用户ID错误地通过了认证'); |
|
||||
resolve(false); |
|
||||
} |
|
||||
|
|
||||
// 关闭连接
|
|
||||
ws.close(); |
|
||||
}); |
|
||||
|
|
||||
ws.on('error', (error) => { |
|
||||
console.error('WebSocket错误:', error); |
|
||||
resolve(false); |
|
||||
}); |
|
||||
|
|
||||
ws.on('close', () => { |
|
||||
console.log('WebSocket连接已关闭'); |
|
||||
}); |
|
||||
|
|
||||
// 设置超时
|
|
||||
setTimeout(() => { |
|
||||
console.error('❌ 测试超时: 未收到服务器响应'); |
|
||||
ws.close(); |
|
||||
resolve(false); |
|
||||
}, 10000); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 运行测试
|
|
||||
testUserAuthValidation() |
|
||||
.then((result) => { |
|
||||
console.log('===== 测试完成 ====='); |
|
||||
console.log('最终结果:', result ? '通过' : '失败'); |
|
||||
rl.close(); |
|
||||
}) |
|
||||
.catch((error) => { |
|
||||
console.error('测试执行错误:', error); |
|
||||
rl.close(); |
|
||||
}); |
|
||||
@ -1,139 +0,0 @@ |
|||||
// 更新商品联系人信息的数据库函数
|
|
||||
require('dotenv').config(); |
|
||||
const { Sequelize } = require('sequelize'); |
|
||||
|
|
||||
// 创建wechat_app数据库连接
|
|
||||
const wechatAppSequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
define: { |
|
||||
timestamps: false |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 创建userlogin数据库连接
|
|
||||
const userLoginSequelize = new Sequelize( |
|
||||
'userlogin', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
define: { |
|
||||
timestamps: false |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 主函数:更新商品联系人信息
|
|
||||
async function updateProductContacts() { |
|
||||
try { |
|
||||
// 测试数据库连接
|
|
||||
await Promise.all([ |
|
||||
wechatAppSequelize.authenticate(), |
|
||||
userLoginSequelize.authenticate() |
|
||||
]); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 1. 查询wechat_app数据库中products表status为published且product_contact为null的记录
|
|
||||
console.log('\n1. 查询待更新的商品...'); |
|
||||
const products = await wechatAppSequelize.query( |
|
||||
'SELECT productId, productName FROM products WHERE status = ? AND product_contact IS NULL', |
|
||||
{ |
|
||||
replacements: ['published'], |
|
||||
type: wechatAppSequelize.QueryTypes.SELECT |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
console.log(`📋 找到 ${products.length} 个待更新联系人信息的商品`); |
|
||||
|
|
||||
if (products.length === 0) { |
|
||||
console.log('✅ 所有商品都已更新联系人信息,无需操作'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 2. 查询userlogin库中managers表role为"销售员"的userName
|
|
||||
console.log('\n2. 查询所有销售员...'); |
|
||||
const managers = await userLoginSequelize.query( |
|
||||
'SELECT userName FROM managers WHERE role = ?', |
|
||||
{ |
|
||||
replacements: ['销售员'], |
|
||||
type: userLoginSequelize.QueryTypes.SELECT |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
const salesmanUserNames = managers.map(m => m.userName); |
|
||||
console.log(`👥 找到 ${salesmanUserNames.length} 名销售员`); |
|
||||
|
|
||||
if (salesmanUserNames.length === 0) { |
|
||||
console.log('❌ 没有找到销售员,无法更新商品联系人信息'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 3. 在wechat_app库中查询这些销售员的nickName和phoneNumber
|
|
||||
console.log('\n3. 查询销售员的联系方式...'); |
|
||||
const salesmenContacts = await wechatAppSequelize.query( |
|
||||
'SELECT nickName, phoneNumber FROM users WHERE nickName IN (?)', |
|
||||
{ |
|
||||
replacements: [salesmanUserNames], |
|
||||
type: wechatAppSequelize.QueryTypes.SELECT |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
console.log(`📞 找到 ${salesmenContacts.length} 名有联系方式的销售员`); |
|
||||
|
|
||||
if (salesmenContacts.length === 0) { |
|
||||
console.log('❌ 没有找到有联系方式的销售员,无法更新商品联系人信息'); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 4. 随机分配销售员到商品
|
|
||||
console.log('\n4. 开始分配销售员到商品...'); |
|
||||
let updatedCount = 0; |
|
||||
|
|
||||
for (const product of products) { |
|
||||
// 随机选择一个销售员
|
|
||||
const randomIndex = Math.floor(Math.random() * salesmenContacts.length); |
|
||||
const selectedSalesman = salesmenContacts[randomIndex]; |
|
||||
|
|
||||
// 更新商品的联系人信息
|
|
||||
await wechatAppSequelize.query( |
|
||||
'UPDATE products SET product_contact = ?, contact_phone = ? WHERE productId = ?', |
|
||||
{ |
|
||||
replacements: [ |
|
||||
selectedSalesman.nickName, |
|
||||
selectedSalesman.phoneNumber, |
|
||||
product.productId |
|
||||
], |
|
||||
type: wechatAppSequelize.QueryTypes.UPDATE |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
console.log(`✅ 商品 ${product.productId} (${product.productName}) 已分配销售员: ${selectedSalesman.nickName} - ${selectedSalesman.phoneNumber}`); |
|
||||
updatedCount++; |
|
||||
} |
|
||||
|
|
||||
console.log(`\n🎉 更新完成!共更新了 ${updatedCount} 个商品的联系人信息`); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 操作失败:', error.message); |
|
||||
console.error('📝 错误详情:', error); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 导出函数供其他模块使用
|
|
||||
module.exports = updateProductContacts; |
|
||||
|
|
||||
// 如果直接运行此文件,则执行更新操作
|
|
||||
if (require.main === module) { |
|
||||
updateProductContacts(); |
|
||||
} |
|
||||
@ -1,218 +0,0 @@ |
|||||
// 更新商品审核状态脚本 - 将商品从pending_review改为reviewed
|
|
||||
const { Sequelize, DataTypes, Model } = require('sequelize'); |
|
||||
require('dotenv').config(); |
|
||||
const readline = require('readline'); |
|
||||
|
|
||||
// 创建读取用户输入的接口
|
|
||||
const rl = readline.createInterface({ |
|
||||
input: process.stdin, |
|
||||
output: process.stdout |
|
||||
}); |
|
||||
|
|
||||
// MySQL数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_DATABASE || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD === undefined ? null : process.env.DB_PASSWORD, |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
pool: { |
|
||||
max: 10, |
|
||||
min: 0, |
|
||||
acquire: 30000, |
|
||||
idle: 10000 |
|
||||
}, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 定义Product模型
|
|
||||
class Product extends Model { } |
|
||||
Product.init({ |
|
||||
id: { |
|
||||
type: DataTypes.INTEGER, |
|
||||
autoIncrement: true, |
|
||||
primaryKey: true |
|
||||
}, |
|
||||
productId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false, |
|
||||
unique: true |
|
||||
}, |
|
||||
sellerId: { |
|
||||
type: DataTypes.STRING(100), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
productName: { |
|
||||
type: DataTypes.STRING(255), |
|
||||
allowNull: false |
|
||||
}, |
|
||||
status: { |
|
||||
type: DataTypes.STRING(20), |
|
||||
defaultValue: 'pending_review', |
|
||||
validate: { |
|
||||
isIn: [['pending_review', 'reviewed', 'published', 'sold_out', 'rejected', 'hidden']] |
|
||||
} |
|
||||
}, |
|
||||
created_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW |
|
||||
}, |
|
||||
updated_at: { |
|
||||
type: DataTypes.DATE, |
|
||||
defaultValue: Sequelize.NOW, |
|
||||
onUpdate: Sequelize.NOW |
|
||||
} |
|
||||
}, { |
|
||||
sequelize, |
|
||||
modelName: 'Product', |
|
||||
tableName: 'products', |
|
||||
timestamps: false |
|
||||
}); |
|
||||
|
|
||||
// 测试数据库连接
|
|
||||
async function testDbConnection() { |
|
||||
try { |
|
||||
await sequelize.authenticate(); |
|
||||
console.log('数据库连接成功'); |
|
||||
} catch (error) { |
|
||||
console.error('数据库连接失败:', error); |
|
||||
console.error('请检查.env文件中的数据库配置是否正确'); |
|
||||
process.exit(1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 获取所有待审核商品
|
|
||||
async function getPendingReviewProducts() { |
|
||||
try { |
|
||||
const products = await Product.findAll({ |
|
||||
where: { |
|
||||
status: 'pending_review' |
|
||||
}, |
|
||||
attributes: ['id', 'productId', 'productName', 'created_at'] |
|
||||
}); |
|
||||
return products; |
|
||||
} catch (error) { |
|
||||
console.error('获取待审核商品失败:', error); |
|
||||
return []; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 更新单个商品状态
|
|
||||
async function updateSingleProduct(productId) { |
|
||||
try { |
|
||||
const result = await Product.update( |
|
||||
{ |
|
||||
status: 'reviewed', |
|
||||
updated_at: new Date() |
|
||||
}, |
|
||||
{ |
|
||||
where: { |
|
||||
productId: productId |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
if (result[0] > 0) { |
|
||||
console.log(`成功更新商品状态: ${productId}`); |
|
||||
return true; |
|
||||
} else { |
|
||||
console.log(`未找到商品: ${productId} 或该商品状态不是待审核`); |
|
||||
return false; |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.error(`更新商品状态失败: ${productId}`, error); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 更新所有待审核商品状态
|
|
||||
async function updateAllProducts() { |
|
||||
try { |
|
||||
const result = await Product.update( |
|
||||
{ |
|
||||
status: 'reviewed', |
|
||||
updated_at: new Date() |
|
||||
}, |
|
||||
{ |
|
||||
where: { |
|
||||
status: 'pending_review' |
|
||||
} |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
console.log(`成功更新 ${result[0]} 个商品的状态`); |
|
||||
return result[0]; |
|
||||
} catch (error) { |
|
||||
console.error('批量更新商品状态失败:', error); |
|
||||
return 0; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 主函数
|
|
||||
async function main() { |
|
||||
try { |
|
||||
await testDbConnection(); |
|
||||
|
|
||||
// 获取待审核商品列表
|
|
||||
const pendingProducts = await getPendingReviewProducts(); |
|
||||
|
|
||||
if (pendingProducts.length === 0) { |
|
||||
console.log('当前没有待审核的商品'); |
|
||||
rl.close(); |
|
||||
process.exit(0); |
|
||||
} |
|
||||
|
|
||||
console.log(`\n找到 ${pendingProducts.length} 个待审核的商品:`); |
|
||||
pendingProducts.forEach((product, index) => { |
|
||||
console.log(`${index + 1}. ID: ${product.productId}, 名称: ${product.productName}, 创建时间: ${product.created_at.toLocaleString()}`); |
|
||||
}); |
|
||||
|
|
||||
// 询问用户要更新单个还是所有商品
|
|
||||
rl.question('\n请选择操作 (1: 更新单个商品, 2: 更新所有商品, 0: 退出): ', async (choice) => { |
|
||||
switch (choice) { |
|
||||
case '1': |
|
||||
rl.question('请输入要更新的商品ID: ', async (productId) => { |
|
||||
await updateSingleProduct(productId); |
|
||||
rl.close(); |
|
||||
}); |
|
||||
break; |
|
||||
|
|
||||
case '2': |
|
||||
rl.question('确定要更新所有待审核商品的状态吗? (y/n): ', async (confirm) => { |
|
||||
if (confirm.toLowerCase() === 'y') { |
|
||||
await updateAllProducts(); |
|
||||
} else { |
|
||||
console.log('已取消操作'); |
|
||||
} |
|
||||
rl.close(); |
|
||||
}); |
|
||||
break; |
|
||||
|
|
||||
case '0': |
|
||||
console.log('已退出'); |
|
||||
rl.close(); |
|
||||
break; |
|
||||
|
|
||||
default: |
|
||||
console.log('无效的选择'); |
|
||||
rl.close(); |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
rl.on('close', () => { |
|
||||
console.log('\n操作已完成,小程序中刷新后即可看到已上架的货源'); |
|
||||
process.exit(0); |
|
||||
}); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('程序执行出错:', error); |
|
||||
rl.close(); |
|
||||
process.exit(1); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 启动程序
|
|
||||
main(); |
|
||||
@ -1,285 +0,0 @@ |
|||||
const { Sequelize } = require('sequelize'); |
|
||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 读取环境变量
|
|
||||
const envPath = path.join(__dirname, '.env'); |
|
||||
if (fs.existsSync(envPath)) { |
|
||||
const envContent = fs.readFileSync(envPath, 'utf8'); |
|
||||
const envVars = envContent.split('\n').filter(line => line.trim() && !line.startsWith('#')); |
|
||||
envVars.forEach(line => { |
|
||||
const [key, value] = line.split('=').map(part => part.trim()); |
|
||||
process.env[key] = value; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_NAME || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD || '', |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
logging: false, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
/** |
|
||||
* 检查并修复所有用户的关联记录 |
|
||||
* 这个函数会扫描所有用户,并为每个用户创建缺失的关联记录 |
|
||||
*/ |
|
||||
async function checkAndFixAllUserAssociations() { |
|
||||
try { |
|
||||
console.log('========================================'); |
|
||||
console.log('检查并修复所有用户的关联记录'); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
// 连接数据库
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 获取所有用户
|
|
||||
const users = await sequelize.query( |
|
||||
'SELECT userId, nickName, phoneNumber FROM users', |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
console.log(`📊 共找到 ${users.length} 个用户记录`); |
|
||||
|
|
||||
let totalContactsCreated = 0; |
|
||||
let totalManagementsCreated = 0; |
|
||||
let fullyFixedUsers = 0; |
|
||||
let partiallyFixedUsers = 0; |
|
||||
let alreadyFixedUsers = 0; |
|
||||
|
|
||||
// 为每个用户检查并创建关联记录
|
|
||||
for (let i = 0; i < users.length; i++) { |
|
||||
const user = users[i]; |
|
||||
console.log(`\n🔄 处理用户 ${i + 1}/${users.length}: ${user.userId}`); |
|
||||
|
|
||||
let contactsCreated = 0; |
|
||||
let managementsCreated = 0; |
|
||||
|
|
||||
// 检查并创建联系人记录
|
|
||||
try { |
|
||||
const existingContact = await sequelize.query( |
|
||||
'SELECT * FROM contacts WHERE userId = ?', |
|
||||
{ replacements: [user.userId], type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (existingContact.length === 0) { |
|
||||
await sequelize.query( |
|
||||
'INSERT INTO contacts (userId, nickName, phoneNumber) VALUES (?, ?, ?)', |
|
||||
{ replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || ''] } |
|
||||
); |
|
||||
console.log('✅ 创建了联系人记录'); |
|
||||
contactsCreated++; |
|
||||
} else { |
|
||||
console.log('✅ 联系人记录已存在'); |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.error('❌ 创建联系人记录失败:', error.message); |
|
||||
} |
|
||||
|
|
||||
// 检查并创建用户管理记录
|
|
||||
try { |
|
||||
const existingManagement = await sequelize.query( |
|
||||
'SELECT * FROM usermanagements WHERE userId = ?', |
|
||||
{ replacements: [user.userId], type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (existingManagement.length === 0) { |
|
||||
await sequelize.query( |
|
||||
'INSERT INTO usermanagements (userId) VALUES (?)', |
|
||||
{ replacements: [user.userId] } |
|
||||
); |
|
||||
console.log('✅ 创建了用户管理记录'); |
|
||||
managementsCreated++; |
|
||||
} else { |
|
||||
console.log('✅ 用户管理记录已存在'); |
|
||||
} |
|
||||
} catch (error) { |
|
||||
console.error('❌ 创建用户管理记录失败:', error.message); |
|
||||
} |
|
||||
|
|
||||
// 更新统计信息
|
|
||||
totalContactsCreated += contactsCreated; |
|
||||
totalManagementsCreated += managementsCreated; |
|
||||
|
|
||||
if (contactsCreated === 0 && managementsCreated === 0) { |
|
||||
alreadyFixedUsers++; |
|
||||
} else if (contactsCreated > 0 || managementsCreated > 0) { |
|
||||
if (contactsCreated > 0 && managementsCreated > 0) { |
|
||||
fullyFixedUsers++; |
|
||||
} else { |
|
||||
partiallyFixedUsers++; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
console.log('\n========================================'); |
|
||||
console.log('修复完成!'); |
|
||||
console.log(`📊 总计: ${users.length} 个用户`); |
|
||||
console.log(`✅ 已经完整的用户: ${alreadyFixedUsers} 个`); |
|
||||
console.log(`🔧 完全修复的用户: ${fullyFixedUsers} 个`); |
|
||||
console.log(`⚠️ 部分修复的用户: ${partiallyFixedUsers} 个`); |
|
||||
console.log(`📈 共创建了 ${totalContactsCreated} 条联系人记录`); |
|
||||
console.log(`📈 共创建了 ${totalManagementsCreated} 条用户管理记录`); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 修复过程中发生错误:', error.message); |
|
||||
console.error('错误详情:', error); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 监控新用户并自动创建关联记录 |
|
||||
* 这个函数会定期检查新用户,并为其创建关联记录 |
|
||||
*/ |
|
||||
async function monitorAndAutoFixNewUsers(intervalMinutes = 10) { |
|
||||
console.log(`\n🔄 启动新用户监控,每 ${intervalMinutes} 分钟检查一次`); |
|
||||
|
|
||||
// 保存上次检查的最大用户ID
|
|
||||
let lastCheckedUserId = ''; |
|
||||
|
|
||||
async function checkNewUsers() { |
|
||||
try { |
|
||||
// 连接数据库
|
|
||||
await sequelize.authenticate(); |
|
||||
|
|
||||
// 获取最新的用户ID
|
|
||||
const latestUser = await sequelize.query( |
|
||||
'SELECT userId FROM users ORDER BY userId DESC LIMIT 1', |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (latestUser.length > 0 && latestUser[0].userId !== lastCheckedUserId) { |
|
||||
console.log(`\n🕵️♂️ 检测到可能的新用户活动,运行完整检查`); |
|
||||
|
|
||||
// 重新运行修复函数检查所有用户
|
|
||||
await checkAndFixAllUserAssociations(); |
|
||||
|
|
||||
// 更新最后检查的用户ID
|
|
||||
lastCheckedUserId = latestUser[0].userId; |
|
||||
} |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 监控过程中发生错误:', error.message); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 立即运行一次
|
|
||||
await checkNewUsers(); |
|
||||
|
|
||||
// 设置定期检查(在实际部署时启用)
|
|
||||
// setInterval(checkNewUsers, intervalMinutes * 60 * 1000);
|
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 创建一个服务器文件补丁,确保新用户在授权时自动创建关联记录 |
|
||||
*/ |
|
||||
function createServerPatch() { |
|
||||
console.log('\n========================================'); |
|
||||
console.log('创建服务器代码补丁'); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
const patchContent = `/**
|
|
||||
* 用户关联记录创建工具函数 |
|
||||
* 用于在用户授权成功后自动创建contacts和usermanagements表关联记录 |
|
||||
*/ |
|
||||
async function createUserAssociations(user) { |
|
||||
try { |
|
||||
// 确保用户对象有效
|
|
||||
if (!user || !user.userId) { |
|
||||
console.error('创建用户关联记录失败: 用户对象或userId无效'); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
console.log('为用户创建关联记录:', user.userId); |
|
||||
|
|
||||
// 1. 创建或更新联系人记录
|
|
||||
const [contactResult] = await sequelize.query( |
|
||||
'INSERT INTO contacts (userId, nickName, phoneNumber) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE nickName = ?, phoneNumber = ?', |
|
||||
{ replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || '', user.nickName || '默认联系人', user.phoneNumber || ''] } |
|
||||
); |
|
||||
|
|
||||
// 2. 创建或更新用户管理记录
|
|
||||
const [managementResult] = await sequelize.query( |
|
||||
'INSERT INTO usermanagements (userId) VALUES (?) ON DUPLICATE KEY UPDATE userId = ?', |
|
||||
{ replacements: [user.userId, user.userId] } |
|
||||
); |
|
||||
|
|
||||
console.log('用户关联记录创建成功:', user.userId); |
|
||||
return true; |
|
||||
} catch (error) { |
|
||||
console.error('创建用户关联记录失败:', error.message); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 在server-mysql.js文件中的用户授权相关代码后添加:
|
|
||||
// 示例:在用户创建或更新成功后调用
|
|
||||
// const user = { userId: '...', nickName: '...', phoneNumber: '...' };
|
|
||||
// await createUserAssociations(user);`;
|
|
||||
|
|
||||
const patchFilePath = path.join(__dirname, 'user-association-patch.js'); |
|
||||
fs.writeFileSync(patchFilePath, patchContent); |
|
||||
|
|
||||
console.log('✅ 服务器补丁已创建:', patchFilePath); |
|
||||
console.log('请将此补丁中的createUserAssociations函数添加到server-mysql.js文件中,'); |
|
||||
console.log('并在用户授权成功后调用该函数,以确保自动创建关联记录。'); |
|
||||
console.log('========================================'); |
|
||||
} |
|
||||
|
|
||||
/** |
|
||||
* 主函数 |
|
||||
*/ |
|
||||
async function main() { |
|
||||
try { |
|
||||
// 1. 先检查并修复所有现有用户
|
|
||||
await checkAndFixAllUserAssociations(); |
|
||||
|
|
||||
// 2. 创建服务器补丁,解决根本问题
|
|
||||
createServerPatch(); |
|
||||
|
|
||||
// 3. 提供使用说明
|
|
||||
console.log('\n========================================'); |
|
||||
console.log('使用说明:'); |
|
||||
console.log('========================================'); |
|
||||
console.log('1. 手动修复现有用户:'); |
|
||||
console.log(' 已完成,所有用户的关联记录已检查并修复'); |
|
||||
console.log('\n2. 长期解决方案:'); |
|
||||
console.log(' a. 请将user-association-patch.js中的createUserAssociations函数添加到server-mysql.js文件'); |
|
||||
console.log(' b. 在用户授权成功后调用该函数'); |
|
||||
console.log(' c. 重启服务器以应用更改'); |
|
||||
console.log('\n3. 可选: 定期检查(适用于临时解决方案):'); |
|
||||
console.log(' 运行: node user-association-auto-fix.js monitor'); |
|
||||
console.log(' 这将每10分钟检查一次新用户并自动修复关联记录'); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 执行过程中发生错误:', error); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 根据命令行参数决定执行模式
|
|
||||
const args = process.argv.slice(2); |
|
||||
const mode = args[0] || 'fix'; |
|
||||
|
|
||||
if (mode === 'monitor') { |
|
||||
// 监控模式
|
|
||||
monitorAndAutoFixNewUsers(); |
|
||||
} else { |
|
||||
// 默认修复模式
|
|
||||
main(); |
|
||||
} |
|
||||
@ -1,38 +0,0 @@ |
|||||
/** |
|
||||
* 用户关联记录创建工具函数 |
|
||||
* 用于在用户授权成功后自动创建contacts和usermanagements表关联记录 |
|
||||
*/ |
|
||||
async function createUserAssociations(user) { |
|
||||
try { |
|
||||
// 确保用户对象有效
|
|
||||
if (!user || !user.userId) { |
|
||||
console.error('创建用户关联记录失败: 用户对象或userId无效'); |
|
||||
return false; |
|
||||
} |
|
||||
|
|
||||
console.log('为用户创建关联记录:', user.userId); |
|
||||
|
|
||||
// 1. 创建或更新联系人记录
|
|
||||
const [contactResult] = await sequelize.query( |
|
||||
'INSERT INTO contacts (userId, nickName, phoneNumber) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE nickName = ?, phoneNumber = ?', |
|
||||
{ replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || '', user.nickName || '默认联系人', user.phoneNumber || ''] } |
|
||||
); |
|
||||
|
|
||||
// 2. 创建或更新用户管理记录
|
|
||||
const [managementResult] = await sequelize.query( |
|
||||
'INSERT INTO usermanagements (userId) VALUES (?) ON DUPLICATE KEY UPDATE userId = ?', |
|
||||
{ replacements: [user.userId, user.userId] } |
|
||||
); |
|
||||
|
|
||||
console.log('用户关联记录创建成功:', user.userId); |
|
||||
return true; |
|
||||
} catch (error) { |
|
||||
console.error('创建用户关联记录失败:', error.message); |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 在server-mysql.js文件中的用户授权相关代码后添加:
|
|
||||
// 示例:在用户创建或更新成功后调用
|
|
||||
// const user = { userId: '...', nickName: '...', phoneNumber: '...' };
|
|
||||
// await createUserAssociations(user);
|
|
||||
@ -1,106 +0,0 @@ |
|||||
const { Sequelize } = require('sequelize'); |
|
||||
const fs = require('fs'); |
|
||||
const path = require('path'); |
|
||||
|
|
||||
// 读取环境变量
|
|
||||
const envPath = path.join(__dirname, '.env'); |
|
||||
if (fs.existsSync(envPath)) { |
|
||||
const envContent = fs.readFileSync(envPath, 'utf8'); |
|
||||
const envVars = envContent.split('\n').filter(line => line.trim() && !line.startsWith('#')); |
|
||||
envVars.forEach(line => { |
|
||||
const [key, value] = line.split('=').map(part => part.trim()); |
|
||||
process.env[key] = value; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 数据库连接配置
|
|
||||
const sequelize = new Sequelize( |
|
||||
process.env.DB_NAME || 'wechat_app', |
|
||||
process.env.DB_USER || 'root', |
|
||||
process.env.DB_PASSWORD || '', |
|
||||
{ |
|
||||
host: process.env.DB_HOST || 'localhost', |
|
||||
port: process.env.DB_PORT || 3306, |
|
||||
dialect: 'mysql', |
|
||||
logging: false, |
|
||||
timezone: '+08:00' // 设置时区为UTC+8
|
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 查看usermanagements表结构
|
|
||||
async function viewUserManagementsTableStructure() { |
|
||||
try { |
|
||||
console.log('========================================'); |
|
||||
console.log('查看usermanagements表结构'); |
|
||||
console.log('========================================'); |
|
||||
|
|
||||
// 连接数据库
|
|
||||
await sequelize.authenticate(); |
|
||||
console.log('✅ 数据库连接成功'); |
|
||||
|
|
||||
// 查询表结构
|
|
||||
const tableStructure = await sequelize.query( |
|
||||
'SHOW COLUMNS FROM usermanagements', |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
console.log('\nusermanagements表字段信息:'); |
|
||||
tableStructure.forEach(column => { |
|
||||
console.log(`- ${column.Field}: ${column.Type} ${column.Null === 'NO' ? '(NOT NULL)' : ''} ${column.Key === 'PRI' ? '(PRIMARY KEY)' : ''}`); |
|
||||
}); |
|
||||
|
|
||||
// 查询表索引
|
|
||||
const indexes = await sequelize.query( |
|
||||
'SHOW INDEX FROM usermanagements', |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
|
|
||||
if (indexes.length > 0) { |
|
||||
console.log('\nusermanagements表索引信息:'); |
|
||||
indexes.forEach(index => { |
|
||||
console.log(`- 索引名: ${index.Key_name}, 字段: ${index.Column_name}, 唯一: ${index.Non_unique === 0 ? '是' : '否'}`); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 查询表中的最新5条记录
|
|
||||
console.log('\nusermanagements表中的最新5条记录:'); |
|
||||
|
|
||||
// 尝试查找一个可能的时间字段
|
|
||||
const possibleTimeFields = tableStructure |
|
||||
.map(col => col.Field) |
|
||||
.filter(field => field.toLowerCase().includes('time') || field.toLowerCase().includes('date')); |
|
||||
|
|
||||
let latestRecords; |
|
||||
|
|
||||
if (possibleTimeFields.length > 0) { |
|
||||
console.log(`找到可能的时间字段: ${possibleTimeFields.join(', ')}`); |
|
||||
// 使用第一个找到的时间字段排序
|
|
||||
latestRecords = await sequelize.query( |
|
||||
`SELECT userId FROM usermanagements ORDER BY ${possibleTimeFields[0]} DESC LIMIT 5`, |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
} else { |
|
||||
console.log('未找到明显的时间字段,按userId排序'); |
|
||||
latestRecords = await sequelize.query( |
|
||||
'SELECT userId FROM usermanagements ORDER BY userId DESC LIMIT 5', |
|
||||
{ type: sequelize.QueryTypes.SELECT } |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
latestRecords.forEach((record, index) => { |
|
||||
console.log(` ${index + 1}. userId: ${record.userId}`); |
|
||||
}); |
|
||||
|
|
||||
console.log('\n========================================'); |
|
||||
|
|
||||
} catch (error) { |
|
||||
console.error('❌ 查看表结构过程中发生错误:', error.message); |
|
||||
console.error('错误详情:', error); |
|
||||
} finally { |
|
||||
// 关闭数据库连接
|
|
||||
await sequelize.close(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
// 运行查看
|
|
||||
viewUserManagementsTableStructure(); |
|
||||
@ -0,0 +1,28 @@ |
|||||
|
#!/bin/bash |
||||
|
|
||||
|
# 更新脚本 - 微信小程序后端服务 |
||||
|
|
||||
|
echo "开始更新微信小程序后端服务..." |
||||
|
|
||||
|
# 进入项目目录 |
||||
|
cd /opt/project_app |
||||
|
|
||||
|
# 拉取最新代码 |
||||
|
echo "拉取最新代码..." |
||||
|
git fetch origin BOSS |
||||
|
git merge --ff-only FETCH_HEAD || { |
||||
|
echo "分支冲突,重置本地分支到远程最新版本..." |
||||
|
git reset --hard origin/BOSS |
||||
|
} |
||||
|
|
||||
|
# 构建新的Docker镜像 |
||||
|
echo "构建新的Docker镜像..." |
||||
|
docker-compose build --no-cache |
||||
|
|
||||
|
# 重启服务 |
||||
|
echo "重启服务..." |
||||
|
docker-compose up -d |
||||
|
|
||||
|
echo "更新完成!服务已重新启动。" |
||||
|
echo "使用以下命令查看服务状态:docker-compose ps" |
||||
|
echo "使用以下命令查看日志:docker-compose logs -f" |
||||