xxfy #2

Merged
ly merged 25 commits from xxfy into Xfy 2 months ago
  1. 24
      .env.example
  2. 23
      Dockerfile
  3. 254
      MULTI_PROJECT_DEPLOYMENT.md
  4. 34
      cleanup.sh
  5. 4
      custom-tab-bar/index.wxml
  6. 186
      deploy.sh
  7. 28
      docker-compose.yml
  8. BIN
      images/1.jpg
  9. BIN
      images/2.jpg
  10. BIN
      images/3.jpg
  11. BIN
      images/4.jpg
  12. 0
      images/招商图片.jpg
  13. 2
      pages/cooperation/index.wxml
  14. 14
      pages/favorites/index.js
  15. 2
      pages/goods-detail/goods-detail.wxml
  16. 40
      pages/index/index.js
  17. 29
      pages/index/index.wxml
  18. 6
      pages/index/index.wxss
  19. 5
      pages/profile/index.wxml
  20. 104
      server-example/.env配置说明.md
  21. 94
      server-example/DOCKER_DEPLOYMENT.md
  22. 23
      server-example/Dockerfile
  23. 1
      server-example/PATH.txt
  24. 41
      server-example/add-department-column.js
  25. 70
      server-example/check-online-status.js
  26. 133
      server-example/check-settlement-data.js
  27. 107
      server-example/cleanup_temp_user_ids.js
  28. 29
      server-example/cleanup_test_data.js
  29. 143
      server-example/complete-gross-weight-fix.js
  30. 123
      server-example/complete-gross-weight-verification.js
  31. 155
      server-example/create-missing-associations.js
  32. 356
      server-example/database-extension.js
  33. 116
      server-example/debug-websocket.js
  34. 193
      server-example/debug_complete_flow.js
  35. 112
      server-example/debug_final.js
  36. 215
      server-example/debug_full_flow.js
  37. 86
      server-example/debug_log_server.js
  38. 111
      server-example/debug_simple.js
  39. 191
      server-example/debug_verbose.js
  40. 175
      server-example/direct-db-check.js
  41. 33
      server-example/docker-compose.yml
  42. 61
      server-example/find-product-creator.js
  43. 85
      server-example/fixed-server.js
  44. 5
      server-example/gross-weight-fix-error.json
  45. 24
      server-example/gross-weight-frontend-fix-report.json
  46. 135
      server-example/gross-weight-log-analyzer.js
  47. 71
      server-example/query-database.js
  48. 125
      server-example/query-personnel.js
  49. 3258
      server-example/server-mysql-backup-alias.js
  50. 2970
      server-example/server-mysql-backup-count.js
  51. 3001
      server-example/server-mysql-backup-final.js
  52. 2960
      server-example/server-mysql.backup.js
  53. 50
      server-example/simple-fix.js
  54. 96
      server-example/simple-gross-weight-check.js
  55. 145
      server-example/simple-gross-weight-verification.js
  56. 36
      server-example/simple-port-check.js
  57. 110
      server-example/simple_verify.js
  58. 108
      server-example/sync-review-status.js
  59. 360
      server-example/test-new-auth-mechanism.js
  60. 37
      server-example/test-post.js
  61. 67
      server-example/test-settlement.js
  62. 166
      server-example/test-type-sync-fix.js
  63. 85
      server-example/test-user-auth-validation.js
  64. 139
      server-example/update-product-contacts.js
  65. BIN
      server-example/update-product-contacts.zip
  66. 218
      server-example/update-product-review-status.js
  67. 285
      server-example/user-association-auto-fix.js
  68. 38
      server-example/user-association-patch.js
  69. 106
      server-example/view-users-table-structure.js
  70. 28
      update.sh

24
.env.example

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

23
Dockerfile

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

254
MULTI_PROJECT_DEPLOYMENT.md

@ -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)实现自动化部署

34
cleanup.sh

@ -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 "清理完成!所有部署的资源已清除。"

4
custom-tab-bar/index.wxml

@ -25,8 +25,8 @@
<!-- 鸡蛋估价按钮 -->
<view class="tab-bar-center" bindtap="goToEvaluatePage">
<view class="egg-button">
<view class="egg-icon">🥚</view>
<view class="egg-text">估</view>
<view class="egg-icon" style="position: absolute; left: 1rpx; top: 30rpx">🥚</view>
<view class="egg-text" style="position: absolute; left: 60rpx; top: 83rpx">估</view>
</view>
</view>

186
deploy.sh

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

28
docker-compose.yml

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

BIN
images/1.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 118 KiB

BIN
images/2.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
images/3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

BIN
images/4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

0
images/9e590639954f2598a54eddf72b7d1fa.jpg → images/招商图片.jpg

Before

Width:  |  Height:  |  Size: 251 KiB

After

Width:  |  Height:  |  Size: 251 KiB

2
pages/cooperation/index.wxml

@ -6,7 +6,7 @@
<!-- 设计展示区域 -->
<view class="design-section">
<image class="design-image" src="/images/9e590639954f2598a54eddf72b7d1fa.jpg" mode="aspectFit"></image>
<image class="design-image" src="/images/招商图片.jpg" mode="aspectFit"></image>
</view>
<!-- 招商广告区域 -->

14
pages/favorites/index.js

@ -227,6 +227,7 @@ Page({
let contact_phone = '';
let fullRegion = '';
let province = '';
let status = 'pending_review'; // 默认状态
// 优先从商品列表中获取联系人信息和地区信息
if (matchingProduct) {
@ -236,6 +237,8 @@ Page({
fullRegion = matchingProduct.fullRegion || matchingProduct.region || '';
// 提取省份
province = extractProvince(fullRegion);
// 获取商品状态
status = matchingProduct.status || 'pending_review';
}
// 然后尝试从收藏数据中获取
else if (item.Product) {
@ -245,6 +248,8 @@ Page({
fullRegion = item.Product.region || '';
// 提取省份
province = extractProvince(fullRegion);
// 获取商品状态
status = item.Product.status || 'pending_review';
}
// 更新item对象,确保联系人信息和地区信息同时存在于顶层和Product对象中
@ -252,16 +257,25 @@ Page({
item.contact_phone = contact_phone;
item.fullRegion = fullRegion;
item.region = province;
item.status = status;
if (item.Product) {
item.Product.product_contact = product_contact;
item.Product.contact_phone = contact_phone;
item.Product.fullRegion = fullRegion;
item.Product.region = province;
item.Product.status = status;
}
return item;
});
// 过滤掉已下架或软删除的商品,只保留已发布状态的货源
favorites = favorites.filter(item => {
const status = item.status || item.Product?.status || 'pending_review';
// 只保留已发布状态的商品
return status === 'published';
});
console.log('更新后的收藏列表:', favorites);
this.setData({
favoritesList: favorites,

2
pages/goods-detail/goods-detail.wxml

@ -54,7 +54,7 @@
bindtap="onFavoriteClick"
style="display: flex; align-items: center;"
>
<text style="font-size: 44rpx; {{isFavorite ? 'color: #ff4d4f;' : 'color: #999;'}} margin-top: -20rpx;">{{isFavorite ? '❤️' : '🤍'}}</text>
<text style="font-size: 44rpx; {{isFavorite ? 'color: #ff4d4f;' : 'color: #999;'}} ">{{isFavorite ? '❤️' : '🤍'}}</text>
</view>
</view>
</view>

40
pages/index/index.js

@ -632,22 +632,34 @@ Page({
// 广告点击事件处理
onAdClick: function(e) {
const adData = e.currentTarget.dataset.ad
console.log('广告被点击, 广告ID:', adData ? adData.id : 'unknown')
const adSlot = e.currentTarget.dataset.ad;
let imageSrc = e.currentTarget.dataset.src;
if (adData && adData.adType) {
// 如果没有从data-src获取到,尝试从图片元素直接获取src
if (!imageSrc) {
imageSrc = e.currentTarget.src;
}
console.log('广告被点击, 广告位:', adSlot);
console.log('广告图片路径:', imageSrc);
// 直接预览广告图片(单击触发)
const validImageUrls = [imageSrc];
if (validImageUrls.length > 0 && validImageUrls[0]) {
this.setData({
previewImageUrls: validImageUrls,
previewImageIndex: 0,
showImagePreview: true
});
console.log('广告图片预览已打开,图片URL:', validImageUrls[0]);
} else {
console.error('无法获取广告图片路径');
wx.showToast({
title: '广告位: ' + adData.adType,
icon: 'none',
duration: 2000
})
if (adData.adType === 'full_card') {
console.log('完整卡片广告被点击')
} else if (adData.adType === 'half_image') {
console.log('半高图片广告被点击')
}
}
title: '图片加载失败',
icon: 'none'
});
}
},
// 加载商品分类列表

29
pages/index/index.wxml

@ -157,16 +157,20 @@
<!-- 顶部空白间距 -->
<view style="height: 20rpx;"></view>
<!-- 广告位1:完整卡片广告 (始终显示) -->
<!-- 广告位1:轮播图1 -->
<view class="waterfall-item">
<view class="ad-card ad-full-card">
<image
class="ad-image"
src="/images/1.jpg"
mode="aspectFill"
bindtap="onAdClick"
data-ad="ad_slot_1"
></image>
<swiper class="ad-swiper" autoplay="true" interval="3000" duration="500" circular="true">
<swiper-item>
<image class="ad-image" src="/images/1.jpg" mode="aspectFill" bindtap="onAdClick" data-ad="ad_slot_1_1" data-src="/images/1.jpg"></image>
</swiper-item>
<swiper-item>
<image class="ad-image" src="/images/2.jpg" mode="aspectFill" bindtap="onAdClick" data-ad="ad_slot_1_2" data-src="/images/2.jpg"></image>
</swiper-item>
<swiper-item>
<image class="ad-image" src="/images/3.jpg" mode="aspectFill" bindtap="onAdClick" data-ad="ad_slot_1_3" data-src="/images/3.jpg"></image>
</swiper-item>
</swiper>
</view>
</view>
@ -190,9 +194,6 @@
bindload="onImageLoad"
data-index="{{index}}"
data-column="left"
bindtap="previewImage"
data-item="{{item}}"
data-index="0"
></image>
<view wx:if="{{item.supplyStatus === '预售'}}" class="promo-tag presale">预售</view>
<view wx:if="{{item.supplyStatus === '现货'}}" class="promo-tag in-stock">现货</view>
@ -224,10 +225,11 @@
<view class="ad-card ad-half-image">
<image
class="ad-image-half"
src="/images/2.jpg"
src="/images/4.jpg"
mode="aspectFill"
bindtap="onAdClick"
data-ad="ad_slot_2"
data-src="/images/4.jpg"
></image>
</view>
</view>
@ -251,9 +253,6 @@
bindload="onImageLoad"
data-index="{{index}}"
data-column="right"
bindtap="previewImage"
data-item="{{item}}"
data-index="0"
></image>
<view wx:if="{{item.supplyStatus === '预售'}}" class="promo-tag presale">预售</view>
<view wx:if="{{item.supplyStatus === '现货'}}" class="promo-tag in-stock">现货</view>

6
pages/index/index.wxss

@ -1492,6 +1492,12 @@ wx-button:not([size=mini]) {
z-index: 1001;
}
/* 轮播广告样式 */
.ad-swiper {
width: 100%;
height: 100%;
}
.ad-image {
width: 100%;
height: 350rpx;

5
pages/profile/index.wxml

@ -7,7 +7,6 @@
></image>
<view>
<view style="font-size: 32rpx; font-weight: bold;">{{userInfo.phoneNumber|| '未登录'}}</view>
<view style="font-size: 26rpx; color: #666;">当前身份: {{userType || '未设置'}}</view>
<view style="font-size: 26rpx; color: #666;">
</view>
@ -24,7 +23,7 @@
type="primary"
style="margin: 20rpx 0;"
>
授权手机号
授权登录
</button>
</view>
@ -37,7 +36,7 @@
<!-- 淘宝样式的收藏按钮 -->
<view style="background: linear-gradient(135deg, #ff6b6b 0%, #ee5a52 100%); color: white; padding: 12rpx 24rpx; border-radius: 24rpx; margin: 10rpx; font-size: 26rpx; font-weight: bold; box-shadow: 0 4rpx 12rpx rgba(255, 107, 107, 0.3); display: flex; align-items: center; cursor: pointer;" bindtap="goToFavorites">
<text style="margin-right: 8rpx;">⭐</text>
<text>收藏</text>
<text>收藏</text>
</view>
</view>
</view>

104
server-example/.env配置说明.md

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

94
server-example/DOCKER_DEPLOYMENT.md

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

23
server-example/Dockerfile

@ -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
server-example/PATH.txt

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

41
server-example/add-department-column.js

@ -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();

70
server-example/check-online-status.js

@ -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();

133
server-example/check-settlement-data.js

@ -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();

107
server-example/cleanup_temp_user_ids.js

@ -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);

29
server-example/cleanup_test_data.js

@ -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();

143
server-example/complete-gross-weight-fix.js

@ -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();

123
server-example/complete-gross-weight-verification.js

@ -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();

155
server-example/create-missing-associations.js

@ -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();

356
server-example/database-extension.js

@ -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模型(可能是实际的模型或临时模型)
};

116
server-example/debug-websocket.js

@ -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停止调试');

193
server-example/debug_complete_flow.js

@ -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();

112
server-example/debug_final.js

@ -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);
}

215
server-example/debug_full_flow.js

@ -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();

86
server-example/debug_log_server.js

@ -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);
}

111
server-example/debug_simple.js

@ -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();

191
server-example/debug_verbose.js

@ -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();

175
server-example/direct-db-check.js

@ -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();

33
server-example/docker-compose.yml

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

61
server-example/find-product-creator.js

@ -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();

85
server-example/fixed-server.js

@ -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');
});

5
server-example/gross-weight-fix-error.json

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

24
server-example/gross-weight-frontend-fix-report.json

@ -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": [
"重启服务器",
"检查前端页面使用的字段名",
"添加商品发布表单的毛重验证",
"检查前端数据处理逻辑"
]
}

135
server-example/gross-weight-log-analyzer.js

@ -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();

71
server-example/query-database.js

@ -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();

125
server-example/query-personnel.js

@ -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();

3258
server-example/server-mysql-backup-alias.js

File diff suppressed because it is too large

2970
server-example/server-mysql-backup-count.js

File diff suppressed because it is too large

3001
server-example/server-mysql-backup-final.js

File diff suppressed because it is too large

2960
server-example/server-mysql.backup.js

File diff suppressed because it is too large

50
server-example/simple-fix.js

@ -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();

96
server-example/simple-gross-weight-check.js

@ -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();

145
server-example/simple-gross-weight-verification.js

@ -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();

36
server-example/simple-port-check.js

@ -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检查完成。');
});

110
server-example/simple_verify.js

@ -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();

108
server-example/sync-review-status.js

@ -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();

360
server-example/test-new-auth-mechanism.js

@ -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();

37
server-example/test-post.js

@ -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();

67
server-example/test-settlement.js

@ -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();

166
server-example/test-type-sync-fix.js

@ -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();

85
server-example/test-user-auth-validation.js

@ -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();
});

139
server-example/update-product-contacts.js

@ -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();
}

BIN
server-example/update-product-contacts.zip

Binary file not shown.

218
server-example/update-product-review-status.js

@ -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();

285
server-example/user-association-auto-fix.js

@ -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();
}

38
server-example/user-association-patch.js

@ -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);

106
server-example/view-users-table-structure.js

@ -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();

28
update.sh

@ -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"
Loading…
Cancel
Save