@ -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" |
|||