diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..387804d
--- /dev/null
+++ b/.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
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..93e07ec
--- /dev/null
+++ b/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"]
\ No newline at end of file
diff --git a/MULTI_PROJECT_DEPLOYMENT.md b/MULTI_PROJECT_DEPLOYMENT.md
new file mode 100644
index 0000000..c42ba2e
--- /dev/null
+++ b/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)实现自动化部署
diff --git a/cleanup.sh b/cleanup.sh
new file mode 100644
index 0000000..d60bf66
--- /dev/null
+++ b/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 "清理完成!所有部署的资源已清除。"
diff --git a/custom-tab-bar/index.wxml b/custom-tab-bar/index.wxml
index 1f1da78..4428e37 100644
--- a/custom-tab-bar/index.wxml
+++ b/custom-tab-bar/index.wxml
@@ -25,8 +25,8 @@
- 🥚
- 估
+ 🥚
+ 估
diff --git a/deploy.sh b/deploy.sh
new file mode 100644
index 0000000..083e612
--- /dev/null
+++ b/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"
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..de673e1
--- /dev/null
+++ b/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
\ No newline at end of file
diff --git a/images/1.jpg b/images/1.jpg
index 1a1a832..b356b20 100644
Binary files a/images/1.jpg and b/images/1.jpg differ
diff --git a/images/2.jpg b/images/2.jpg
index 79bfb8e..0658315 100644
Binary files a/images/2.jpg and b/images/2.jpg differ
diff --git a/images/3.jpg b/images/3.jpg
new file mode 100644
index 0000000..b390174
Binary files /dev/null and b/images/3.jpg differ
diff --git a/images/4.jpg b/images/4.jpg
new file mode 100644
index 0000000..29f81ad
Binary files /dev/null and b/images/4.jpg differ
diff --git a/images/9e590639954f2598a54eddf72b7d1fa.jpg b/images/招商图片.jpg
similarity index 100%
rename from images/9e590639954f2598a54eddf72b7d1fa.jpg
rename to images/招商图片.jpg
diff --git a/pages/cooperation/index.wxml b/pages/cooperation/index.wxml
index 82a49d3..925fc94 100644
--- a/pages/cooperation/index.wxml
+++ b/pages/cooperation/index.wxml
@@ -6,7 +6,7 @@
-
+
diff --git a/pages/favorites/index.js b/pages/favorites/index.js
index 0106417..841eb9d 100644
--- a/pages/favorites/index.js
+++ b/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,
diff --git a/pages/goods-detail/goods-detail.wxml b/pages/goods-detail/goods-detail.wxml
index 16efdbe..aab45ee 100644
--- a/pages/goods-detail/goods-detail.wxml
+++ b/pages/goods-detail/goods-detail.wxml
@@ -54,7 +54,7 @@
bindtap="onFavoriteClick"
style="display: flex; align-items: center;"
>
- {{isFavorite ? '❤️' : '🤍'}}
+ {{isFavorite ? '❤️' : '🤍'}}
diff --git a/pages/index/index.js b/pages/index/index.js
index 1869f30..2e92a52 100644
--- a/pages/index/index.js
+++ b/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'
+ });
+ }
},
// 加载商品分类列表
diff --git a/pages/index/index.wxml b/pages/index/index.wxml
index bcaae89..84e6455 100644
--- a/pages/index/index.wxml
+++ b/pages/index/index.wxml
@@ -157,16 +157,20 @@
-
+
-
+
+
+
+
+
+
+
+
+
+
+
@@ -190,9 +194,6 @@
bindload="onImageLoad"
data-index="{{index}}"
data-column="left"
- bindtap="previewImage"
- data-item="{{item}}"
- data-index="0"
>
预售
现货
@@ -224,10 +225,11 @@
@@ -251,9 +253,6 @@
bindload="onImageLoad"
data-index="{{index}}"
data-column="right"
- bindtap="previewImage"
- data-item="{{item}}"
- data-index="0"
>
预售
现货
diff --git a/pages/index/index.wxss b/pages/index/index.wxss
index abfc779..a9d0581 100644
--- a/pages/index/index.wxss
+++ b/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;
diff --git a/pages/profile/index.wxml b/pages/profile/index.wxml
index b801c76..623a318 100644
--- a/pages/profile/index.wxml
+++ b/pages/profile/index.wxml
@@ -7,7 +7,6 @@
>
{{userInfo.phoneNumber|| '未登录'}}
- 当前身份: {{userType || '未设置'}}
@@ -24,7 +23,7 @@
type="primary"
style="margin: 20rpx 0;"
>
- 授权手机号
+ 授权登录
@@ -37,7 +36,7 @@
⭐
- 收藏
+ 收藏夹
diff --git a/server-example/.env配置说明.md b/server-example/.env配置说明.md
new file mode 100644
index 0000000..bc88bee
--- /dev/null
+++ b/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
+```
\ No newline at end of file
diff --git a/server-example/DOCKER_DEPLOYMENT.md b/server-example/DOCKER_DEPLOYMENT.md
new file mode 100644
index 0000000..cc9b90b
--- /dev/null
+++ b/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
+```
diff --git a/server-example/Dockerfile b/server-example/Dockerfile
new file mode 100644
index 0000000..cd5c21f
--- /dev/null
+++ b/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"]
diff --git a/server-example/PATH.txt b/server-example/PATH.txt
deleted file mode 100644
index a5f0db3..0000000
--- a/server-example/PATH.txt
+++ /dev/null
@@ -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;
diff --git a/server-example/add-department-column.js b/server-example/add-department-column.js
deleted file mode 100644
index 518db97..0000000
--- a/server-example/add-department-column.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/check-online-status.js b/server-example/check-online-status.js
deleted file mode 100644
index 00f5538..0000000
--- a/server-example/check-online-status.js
+++ /dev/null
@@ -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();
diff --git a/server-example/check-settlement-data.js b/server-example/check-settlement-data.js
deleted file mode 100644
index b9c9be5..0000000
--- a/server-example/check-settlement-data.js
+++ /dev/null
@@ -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();
diff --git a/server-example/cleanup_temp_user_ids.js b/server-example/cleanup_temp_user_ids.js
deleted file mode 100644
index 16b77ba..0000000
--- a/server-example/cleanup_temp_user_ids.js
+++ /dev/null
@@ -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);
diff --git a/server-example/cleanup_test_data.js b/server-example/cleanup_test_data.js
deleted file mode 100644
index 3f7730d..0000000
--- a/server-example/cleanup_test_data.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/complete-gross-weight-fix.js b/server-example/complete-gross-weight-fix.js
deleted file mode 100644
index a6bc44d..0000000
--- a/server-example/complete-gross-weight-fix.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/complete-gross-weight-verification.js b/server-example/complete-gross-weight-verification.js
deleted file mode 100644
index 849c58d..0000000
--- a/server-example/complete-gross-weight-verification.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/create-missing-associations.js b/server-example/create-missing-associations.js
deleted file mode 100644
index 7389ec7..0000000
--- a/server-example/create-missing-associations.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/database-extension.js b/server-example/database-extension.js
deleted file mode 100644
index 3cd70ff..0000000
--- a/server-example/database-extension.js
+++ /dev/null
@@ -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模型(可能是实际的模型或临时模型)
-};
\ No newline at end of file
diff --git a/server-example/debug-websocket.js b/server-example/debug-websocket.js
deleted file mode 100644
index 4eab4cc..0000000
--- a/server-example/debug-websocket.js
+++ /dev/null
@@ -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停止调试');
diff --git a/server-example/debug_complete_flow.js b/server-example/debug_complete_flow.js
deleted file mode 100644
index e73779a..0000000
--- a/server-example/debug_complete_flow.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/debug_final.js b/server-example/debug_final.js
deleted file mode 100644
index 8006e5b..0000000
--- a/server-example/debug_final.js
+++ /dev/null
@@ -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);
-}
\ No newline at end of file
diff --git a/server-example/debug_full_flow.js b/server-example/debug_full_flow.js
deleted file mode 100644
index 10805ef..0000000
--- a/server-example/debug_full_flow.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/debug_log_server.js b/server-example/debug_log_server.js
deleted file mode 100644
index b4dae56..0000000
--- a/server-example/debug_log_server.js
+++ /dev/null
@@ -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);
-}
\ No newline at end of file
diff --git a/server-example/debug_simple.js b/server-example/debug_simple.js
deleted file mode 100644
index d31f233..0000000
--- a/server-example/debug_simple.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/debug_verbose.js b/server-example/debug_verbose.js
deleted file mode 100644
index 06cde18..0000000
--- a/server-example/debug_verbose.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/direct-db-check.js b/server-example/direct-db-check.js
deleted file mode 100644
index f6ba3d0..0000000
--- a/server-example/direct-db-check.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/docker-compose.yml b/server-example/docker-compose.yml
new file mode 100644
index 0000000..b9bfd9c
--- /dev/null
+++ b/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:
diff --git a/server-example/find-product-creator.js b/server-example/find-product-creator.js
deleted file mode 100644
index d53e1ea..0000000
--- a/server-example/find-product-creator.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/fixed-server.js b/server-example/fixed-server.js
deleted file mode 100644
index d32a12b..0000000
--- a/server-example/fixed-server.js
+++ /dev/null
@@ -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');
-});
\ No newline at end of file
diff --git a/server-example/gross-weight-fix-error.json b/server-example/gross-weight-fix-error.json
deleted file mode 100644
index b408862..0000000
--- a/server-example/gross-weight-fix-error.json
+++ /dev/null
@@ -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. (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)"
-}
\ No newline at end of file
diff --git a/server-example/gross-weight-frontend-fix-report.json b/server-example/gross-weight-frontend-fix-report.json
deleted file mode 100644
index b0dc4da..0000000
--- a/server-example/gross-weight-frontend-fix-report.json
+++ /dev/null
@@ -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": [
- "重启服务器",
- "检查前端页面使用的字段名",
- "添加商品发布表单的毛重验证",
- "检查前端数据处理逻辑"
- ]
-}
\ No newline at end of file
diff --git a/server-example/gross-weight-log-analyzer.js b/server-example/gross-weight-log-analyzer.js
deleted file mode 100644
index a6c41e7..0000000
--- a/server-example/gross-weight-log-analyzer.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/query-database.js b/server-example/query-database.js
deleted file mode 100644
index d19a8e6..0000000
--- a/server-example/query-database.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/query-personnel.js b/server-example/query-personnel.js
deleted file mode 100644
index 015f1ae..0000000
--- a/server-example/query-personnel.js
+++ /dev/null
@@ -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();
diff --git a/server-example/server-mysql-backup-alias.js b/server-example/server-mysql-backup-alias.js
deleted file mode 100644
index 5c5ce9b..0000000
--- a/server-example/server-mysql-backup-alias.js
+++ /dev/null
@@ -1,3258 +0,0 @@
-// ECS服务器示例代码 - Node.js版 (MySQL版本)
-const express = require('express');
-const crypto = require('crypto');
-const bodyParser = require('body-parser');
-const { Sequelize, DataTypes, Model, Op } = require('sequelize');
-require('dotenv').config();
-
-// 创建Express应用
-const app = express();
-const PORT = process.env.PORT || 3002;
-
-// 中间件
-app.use(bodyParser.json());
-
-// 添加请求日志中间件,捕获所有到达服务器的请求(必须放在bodyParser之后)
-app.use((req, res, next) => {
- // 将UTC时间转换为北京时间(UTC+8)
- const now = new Date();
- const beijingTime = new Date(now.getTime() + 8 * 60 * 60 * 1000);
- const formattedTime = beijingTime.toISOString().replace('Z', '+08:00');
-
- console.log(`[${formattedTime}] 收到请求: ${req.method} ${req.url}`);
- console.log('请求头:', req.headers);
- console.log('请求体:', req.body);
- next();
-});
-
-// 商品毛重处理中间件 - 确保所有返回的商品数据中毛重字段保持原始值
-app.use((req, res, next) => {
- // 保存原始的json方法
- const originalJson = res.json;
-
- // 重写json方法来处理响应数据
- res.json = function (data) {
- // 检查数据中是否包含商品列表
- if (data && typeof data === 'object') {
- // 处理/products/list接口的响应
- if (data.products && Array.isArray(data.products)) {
- data.products = data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
-
- // 处理/data字段中的商品列表
- if (data.data && data.data.products && Array.isArray(data.data.products)) {
- data.data.products = data.data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
- }
-
- // 调用原始的json方法
- return originalJson.call(this, data);
- };
-
- next();
-});
-
-// 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
- }
- }
-);
-
-// 微信小程序配置
-const WECHAT_CONFIG = {
- APPID: process.env.WECHAT_APPID || 'your-wechat-appid',
- APPSECRET: process.env.WECHAT_APPSECRET || 'your-wechat-appsecret',
- TOKEN: process.env.WECHAT_TOKEN || 'your-wechat-token'
-};
-
-// 显示当前使用的数据库配置(用于调试)
-console.log('当前数据库连接配置:');
-console.log(' 主机:', process.env.DB_HOST || 'localhost');
-console.log(' 端口:', process.env.DB_PORT || 3306);
-console.log(' 数据库名:', process.env.DB_DATABASE || 'wechat_app');
-console.log(' 用户名:', process.env.DB_USER || 'root');
-console.log(' 密码:', process.env.DB_PASSWORD === undefined || process.env.DB_PASSWORD === '' ? '无密码' : '******');
-
-// 测试数据库连接
-async function testDbConnection() {
- try {
- await sequelize.authenticate();
- console.log('数据库连接成功');
- } catch (error) {
- console.error('数据库连接失败:', error);
- console.error('\n请检查以下几点:');
- console.error('1. MySQL服务是否已经启动');
- console.error('2. wechat_app数据库是否已创建');
- console.error('3. .env文件中的数据库用户名和密码是否正确');
- console.error('4. 用户名是否有足够的权限访问数据库');
- console.error('\n如果是首次配置,请参考README文件中的数据库设置指南。');
- process.exit(1);
- }
-}
-
-testDbConnection();
-
-// 定义数据模型
-
-// 用户模型
-class User extends Model { }
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- openid: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 微信名,必填
- },
- avatarUrl: {
- type: DataTypes.TEXT
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 电话号码,必填
- },
- type: {
- type: DataTypes.STRING(20),
- allowNull: false // 用户身份(buyer/seller/both),必填
- },
- gender: {
- type: DataTypes.INTEGER
- },
- country: {
- type: DataTypes.STRING(50)
- },
- province: {
- type: DataTypes.STRING(50)
- },
- city: {
- type: DataTypes.STRING(50)
- },
- language: {
- type: DataTypes.STRING(20)
- },
- session_key: {
- type: DataTypes.STRING(255)
- },
- // 新增字段
- company: {
- type: DataTypes.STRING(255) // 客户公司
- },
- region: {
- type: DataTypes.STRING(255) // 客户地区
- },
- level: {
- type: DataTypes.STRING(255),
- defaultValue: 'company-sea-pools' // 客户等级,默认值为company-sea-pools
- },
- demand: {
- type: DataTypes.TEXT // 基本需求
- },
- spec: {
- type: DataTypes.TEXT // 规格
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'User',
- tableName: 'users',
- timestamps: false
-});
-
-// 商品模型
-class Product extends Model { }
-Product.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- sellerId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- price: {
- type: DataTypes.DECIMAL(10, 2),
- allowNull: false
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- imageUrls: {
- type: DataTypes.TEXT
- },
- region: {
- type: DataTypes.STRING(100),
- defaultValue: ''
- },
- status: {
- type: DataTypes.STRING(20),
- defaultValue: 'pending_review',
- validate: {
- isIn: [['pending_review', 'reviewed', 'published', 'sold_out', 'rejected', 'hidden']]
- }
- },
- rejectReason: {
- type: DataTypes.TEXT
- },
- // 新增联系人相关字段
- product_contact: {
- type: DataTypes.STRING(100),
- defaultValue: ''
- },
- contact_phone: {
- type: DataTypes.STRING(20),
- defaultValue: ''
- },
- // 新增预约相关字段
- reservedCount: {
- type: DataTypes.INTEGER,
- defaultValue: 0,
- allowNull: false,
- comment: '已有几人想要'
- },
- 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
-});
-
-// 购物车模型
-class CartItem extends Model { }
-CartItem.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 1
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- price: {
- type: DataTypes.DECIMAL(10, 2)
- },
- selected: {
- type: DataTypes.BOOLEAN,
- defaultValue: true
- },
- added_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'CartItem',
- tableName: 'cart_items',
- timestamps: false
-});
-
-// 联系人表模型
-class Contact extends Model { }
-Contact.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 联系人
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 手机号
- },
- wechat: {
- type: DataTypes.STRING(100) // 微信号
- },
- account: {
- type: DataTypes.STRING(100) // 账户
- },
- accountNumber: {
- type: DataTypes.STRING(100) // 账号
- },
- bank: {
- type: DataTypes.STRING(100) // 开户行
- },
- address: {
- type: DataTypes.TEXT // 地址
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'Contact',
- tableName: 'contacts',
- timestamps: false
-});
-
-// 用户管理表模型
-class UserManagement extends Model { }
-UserManagement.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- managerId: {
- type: DataTypes.STRING(100),
- defaultValue: null // 经理ID,默认值为null
- },
- company: {
- type: DataTypes.STRING(255),
- defaultValue: null // 公司,默认值为null
- },
- department: {
- type: DataTypes.STRING(255),
- defaultValue: null // 部门,默认值为null
- },
- organization: {
- type: DataTypes.STRING(255),
- defaultValue: null // 组织,默认值为null
- },
- role: {
- type: DataTypes.STRING(100),
- defaultValue: null // 角色,默认值为null
- },
- root: {
- type: DataTypes.STRING(100),
- defaultValue: null // 根节点,默认值为null
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'UserManagement',
- tableName: 'usermanagements',
- timestamps: false
-});
-
-// 定义模型之间的关联关系
-
-// 用户和商品的一对多关系 (卖家发布商品)
-User.hasMany(Product, {
- foreignKey: 'sellerId', // 外键字段名
- sourceKey: 'userId', // 源键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'products', // 别名,用于关联查询
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Product.belongsTo(User, {
- foreignKey: 'sellerId',
- targetKey: 'userId', // 目标键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'seller' // 别名,用于关联查询
-});
-
-// 用户和购物车项的一对多关系 (买家的购物需求/购物车)
-User.hasMany(CartItem, {
- foreignKey: 'userId',
- as: 'cartItems', // 用户的购物车(购物需求)列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(User, {
- foreignKey: 'userId',
- as: 'buyer' // 别名,明确表示这是购物需求的买家
-});
-
-// 商品和购物车项的一对多关系 (商品被添加到购物车)
-Product.hasMany(CartItem, {
- foreignKey: 'productId',
- as: 'cartItems', // 商品出现在哪些购物车中
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(Product, {
- foreignKey: 'productId',
- as: 'product' // 购物车项中的商品
-});
-
-// 用户和联系人的一对多关系
-User.hasMany(Contact, {
- foreignKey: 'userId',
- as: 'contacts', // 用户的联系人列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Contact.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 联系人所属用户
-});
-
-// 用户和用户管理的一对一关系
-User.hasOne(UserManagement, {
- foreignKey: 'userId',
- as: 'management', // 用户的管理信息
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-UserManagement.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 管理信息所属用户
-});
-
-// 同步数据库模型到MySQL
-async function syncDatabase() {
- try {
- // 不使用alter: true,避免尝试修改已有表结构导致的外键约束问题
- await sequelize.sync({
- force: false // 不强制重新创建表
- });
- console.log('数据库模型同步成功');
- } catch (error) {
- console.error('数据库模型同步失败:', error);
- // 即使同步失败也继续运行,因为我们只需要API功能
- console.log('数据库模型同步失败,但服务器继续运行,使用现有表结构');
- }
-}
-
-syncDatabase();
-
-// 解密微信加密数据
-function decryptData(encryptedData, sessionKey, iv) {
- try {
- // Base64解码
- const sessionKeyBuf = Buffer.from(sessionKey, 'base64');
- const encryptedDataBuf = Buffer.from(encryptedData, 'base64');
- const ivBuf = Buffer.from(iv, 'base64');
-
- // AES解密
- const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKeyBuf, ivBuf);
- decipher.setAutoPadding(true);
- let decoded = decipher.update(encryptedDataBuf, 'binary', 'utf8');
- decoded += decipher.final('utf8');
-
- // 解析JSON
- return JSON.parse(decoded);
- } catch (error) {
- console.error('解密失败:', error);
- // 提供更具体的错误信息
- if (error.code === 'ERR_OSSL_BAD_DECRYPT') {
- throw new Error('登录信息已过期,请重新登录');
- } else if (error.name === 'SyntaxError') {
- throw new Error('数据格式错误,解密结果无效');
- } else {
- throw new Error('解密失败,请重试');
- }
- }
-}
-
-// 获取微信session_key
-async function getSessionKey(code) {
- const axios = require('axios');
- const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${WECHAT_CONFIG.APPID}&secret=${WECHAT_CONFIG.APPSECRET}&js_code=${code}&grant_type=authorization_code`;
-
- try {
- const response = await axios.get(url);
- return response.data;
- } catch (error) {
- console.error('获取session_key失败:', error);
- throw new Error('获取session_key失败');
- }
-}
-
-// 创建用户关联记录函数 - 自动为用户创建contacts和usermanagements表的关联记录
-async function createUserAssociations(user) {
- try {
- if (!user || !user.userId) {
- console.error('无效的用户数据,无法创建关联记录');
- return false;
- }
-
- console.log('为用户创建关联记录:', user.userId);
-
- // 使用事务确保操作原子性
- await sequelize.transaction(async (transaction) => {
- // 1. 处理联系人记录 - 使用INSERT ... ON DUPLICATE KEY UPDATE确保无论如何都只保留一条记录
- await sequelize.query(
- `INSERT INTO contacts (userId, nickName, phoneNumber, created_at, updated_at)
- VALUES (?, ?, ?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- nickName = VALUES(nickName),
- phoneNumber = VALUES(phoneNumber),
- updated_at = NOW()`,
- {
- replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || ''],
- transaction: transaction
- }
- );
- console.log('联系人记录已处理(创建或更新):', user.userId);
-
- // 2. 处理用户管理记录 - 使用相同策略
- await sequelize.query(
- `INSERT INTO usermanagements (userId, created_at, updated_at)
- VALUES (?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- updated_at = NOW()`,
- {
- replacements: [user.userId],
- transaction: transaction
- }
- );
- console.log('用户管理记录已处理(创建或更新):', user.userId);
- });
-
- console.log('用户关联记录处理成功:', user.userId);
- return true;
- } catch (error) {
- console.error('创建用户关联记录失败:', error.message);
- return false;
- }
-}
-
-// API路由
-
-// 上传用户信息
-app.post('/api/user/upload', async (req, res) => {
- try {
- const userData = req.body;
- console.log('收到用户信息上传请求:', userData);
-
- // 如果用户信息中包含手机号,检查手机号是否已被其他用户使用
- if (userData.phoneNumber) {
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: userData.phoneNumber,
- openid: { [Sequelize.Op.ne]: userData.openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${userData.phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 创建新对象,移除手机号字段
- const userDataWithoutPhone = { ...userData };
- delete userDataWithoutPhone.phoneNumber;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息(不包含手机号)
- await User.update(
- {
- ...userDataWithoutPhone,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
- } else {
- // 创建新用户
- user = await User.create({
- ...userDataWithoutPhone,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- // 返回成功,但提示手机号已被使用
- return res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功,但手机号已被其他账号绑定',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: true
- });
- }
- }
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息
- await User.update(
- {
- ...userData,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- } else {
- // 创建新用户
- user = await User.create({
- ...userData,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: false
- });
- } catch (error) {
- console.error('保存用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '保存用户信息失败',
- error: error.message
- });
- }
-});
-
-// 解密手机号
-app.post('/api/user/decodePhone', async (req, res) => {
- try {
- const { encryptedData, iv, openid } = req.body;
-
- // 参数校验
- if (!encryptedData || !iv || !openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数'
- });
- }
-
- // 查找用户的session_key
- const user = await User.findOne({ where: { openid } });
-
- if (!user || !user.session_key) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录,请先登录',
- needRelogin: true
- });
- }
-
- // 解密手机号
- let decryptedData, phoneNumber;
- try {
- decryptedData = decryptData(encryptedData, user.session_key, iv);
- phoneNumber = decryptedData.phoneNumber;
- } catch (decryptError) {
- // 解密失败,可能是session_key过期,建议重新登录
- return res.status(401).json({
- success: false,
- code: 401,
- message: decryptError.message || '手机号解密失败',
- needRelogin: true
- });
- }
-
- // 检查手机号是否已被其他用户使用
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: phoneNumber,
- openid: { [Sequelize.Op.ne]: openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 返回成功,但不更新手机号,提示用户
- return res.json({
- success: true,
- code: 200,
- message: '手机号已被其他账号绑定',
- phoneNumber: user.phoneNumber, // 返回原手机号
- isNewPhone: false
- });
- }
-
- // 更新用户手机号
- await User.update(
- {
- phoneNumber: phoneNumber,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 更新用户手机号后,更新关联记录
- const updatedUser = await User.findOne({ where: { openid } });
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '手机号解密成功',
- phoneNumber: phoneNumber,
- isNewPhone: true
- });
- } catch (error) {
- console.error('手机号解密失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '手机号解密失败',
- error: error.message
- });
- }
-});
-
-// 处理微信登录,获取openid和session_key
-app.post('/api/wechat/getOpenid', async (req, res) => {
- try {
- const { code } = req.body;
-
- if (!code) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少code参数'
- });
- }
-
- // 获取openid和session_key
- const wxData = await getSessionKey(code);
-
- if (wxData.errcode) {
- throw new Error(`微信接口错误: ${wxData.errmsg}`);
- }
-
- const { openid, session_key, unionid } = wxData;
-
- // 生成userId
- const userId = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid }
- });
-
- if (user) {
- // 更新用户session_key
- await User.update(
- {
- session_key: session_key,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
- } else {
- // 创建新用户
- // 支持从客户端传入type参数,如果没有则默认为buyer
- const userType = req.body.type || 'buyer';
- await User.create({
- openid,
- userId,
- session_key,
- nickName: '微信用户', // 临时占位,等待用户授权
- type: userType, // 使用客户端传入的类型或默认买家身份
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 为新创建的用户创建关联记录
- const newUser = { userId, openid, nickName: '微信用户' };
- await createUserAssociations(newUser);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取openid成功',
- data: {
- openid,
- userId: user ? user.userId : userId
- }
- });
- } catch (error) {
- console.error('获取openid失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取openid失败',
- error: error.message
- });
- }
-});
-
-// 验证用户登录状态
-app.post('/api/user/validate', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'avatarUrl', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '验证成功',
- data: user
- });
- } catch (error) {
- console.error('验证用户登录状态失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '验证失败',
- error: error.message
- });
- }
-});
-
-// 获取用户信息
-app.post('/api/user/get', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- include: [
- {
- model: Contact,
- as: 'contacts',
- attributes: ['id', 'nickName', 'phoneNumber', 'wechat', 'account', 'accountNumber', 'bank', 'address']
- },
- { model: UserManagement,
- as: 'management',
- attributes: ['id', 'managerId', 'managercompany', 'department', 'organization', 'role', 'root']
- }
- ]
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取用户信息成功',
- data: user
- });
- } catch (error) {
- console.error('获取用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户信息失败',
- error: error.message
- });
- }
-});
-
-// 更新用户信息
-app.post('/api/user/update', async (req, res) => {
- try {
- const { openid, ...updateData } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid }
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 更新用户信息
- await User.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 获取更新后的用户信息
- const updatedUser = await User.findOne({
- where: { openid }
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '更新用户信息成功',
- data: updatedUser
- });
- } catch (error) {
- console.error('更新用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新用户信息失败',
- error: error.message
- });
- }
-});
-
-// 获取商品列表 - 优化版本确保状态筛选正确应用
-app.post('/api/product/list', async (req, res) => {
- try {
- const { openid, status, keyword, page = 1, pageSize = 20, testMode = false, viewMode = '' } = req.body;
-
- // 构建查询条件
- const where = {};
-
- // 查找用户
- let user = null;
- if (openid && !testMode) {
- user = await User.findOne({ where: { openid } });
- // 不再因为用户不存在而返回错误,允许未登录用户访问
- if (!user) {
- console.log('用户不存在,但仍允许访问公开商品');
- }
-
- // 如果是卖家查看自己的商品或管理员,应用相应的权限控制
- // 但对于购物模式,即使有登录信息也返回所有公开商品
- if (user && user.type !== 'admin' && viewMode !== 'shopping') {
- where.sellerId = user.userId;
- }
- } else if (testMode) {
- // 测试模式:不验证用户,查询所有已发布的商品
- console.log('测试模式:查询所有已发布的商品');
- }
-
- // 购物模式:无论用户是否登录,都显示公开商品
- if (viewMode === 'shopping' || !openid) {
- console.log('购物模式或未登录用户:显示所有公开商品');
- }
-
- // 状态筛选 - 直接构建到where对象中,确保不会丢失
- console.log(`当前用户类型: ${user ? user.type : '未知'},请求状态: ${status || '未指定'},测试模式: ${testMode}`);
-
- // 如果有指定status参数,按参数筛选但同时排除hidden
- if (status) {
- console.log(`按状态筛选商品: status=${status},并排除hidden状态`);
- if (status === 'all') {
- // 特殊情况:请求所有商品但仍然排除hidden
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else if (Array.isArray(status)) {
- // 如果status是数组,确保不包含hidden
- where.status = { [Sequelize.Op.in]: status.filter(s => s !== 'hidden') };
- } else {
- // 单个状态值,确保不是hidden
- if (status !== 'hidden') {
- where.status = { [Sequelize.Op.eq]: status };
- } else {
- // 如果明确请求hidden状态,也返回空结果
- where.status = { [Sequelize.Op.not]: 'hidden' };
- }
- }
- } else {
- // 没有指定status参数时 - 直接在where对象中设置状态筛选
- if (user && (user.type === 'seller' || user.type === 'both') && !testMode) {
- // 卖家用户且非测试模式
- console.log(`卖家用户 ${user.userId} (类型:${user.type}) 查看自己的所有商品,但排除hidden状态`);
- // 卖家可以查看自己的所有商品,但仍然排除hidden状态
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else {
- // 测试模式或非卖家用户
- console.log(`测试模式或非卖家用户,使用默认状态筛选: reviewed/published`);
- // 默认只显示已审核和已发布的商品,排除hidden和sold_out状态
- where.status = { [Sequelize.Op.in]: ['reviewed', 'published'] };
- }
- }
-
- console.log(`构建的完整查询条件:`, JSON.stringify(where, null, 2));
-
- // 关键词搜索
- if (keyword) {
- where.productName = { [Sequelize.Op.like]: `%${keyword}%` };
- }
-
- // 计算偏移量
- const offset = (page - 1) * pageSize;
-
- // 查询商品列表
- const { count, rows: products } = await Product.findAndCountAll({
- where,
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ],
- order: [['created_at', 'DESC']],
- limit: pageSize,
- offset,
- // 移除GROUP BY以避免嵌套查询问题
- });
-
- // 对于每个商品,单独查询预约人数(cartItems数量)
- // 这是为了避免GROUP BY和嵌套查询的复杂问题
- const productsWithSelected = await Promise.all(
- products.map(async (product) => {
- const productJSON = product.toJSON();
- const cartItemCount = await CartItem.count({
- where: { productId: productJSON.id }
- });
- productJSON.selected = cartItemCount;
- return productJSON;
- })
- );
-
- // 添加详细日志,记录查询结果
- console.log(`商品列表查询结果 - 商品数量: ${count}, 商品列表长度: ${productsWithSelected.length}`);
- if (productsWithSelected.length > 0) {
- console.log(`第一个商品数据:`, JSON.stringify(productsWithSelected[0], null, 2));
-
- // 添加selected字段的专门日志
- console.log('商品预约人数(selected字段)统计:');
- productsWithSelected.slice(0, 5).forEach(product => {
- console.log(`- ${product.productName}: 预约人数=${product.selected || 0}, 商品ID=${product.productId}`);
- });
- }
-
- // 处理商品列表中的grossWeight字段,确保是数字类型,同时处理imageUrls的JSON解析
- const processedProducts = productsWithSelected.map(product => {
- // 创建副本以避免直接修改原始对象
- const productJSON = {...product};
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productJSON.grossWeight,
- type: typeof productJSON.grossWeight,
- isEmpty: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined,
- isNumeric: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined || !isNaN(parseFloat(productJSON.grossWeight)) && isFinite(productJSON.grossWeight),
- parsedValue: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined ? 0 : parseFloat(productJSON.grossWeight)
- };
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- productJSON.grossWeight = finalGrossWeight;
-
- // 确保selected字段存在并设置为数字类型
- productJSON.selected = productJSON.selected || 0;
- productJSON.selected = parseInt(productJSON.selected, 10);
-
- // 修复imageUrls字段:使用简化但可靠的方法确保返回正确格式的URL数组
- try {
- if (productJSON.imageUrls) {
- // 强制转换为字符串
- let imageUrlsStr = String(productJSON.imageUrls);
-
- // 方法:直接查找并提取所有有效的URL
- // 匹配所有可能的URL模式,避免包含特殊字符
- const allUrls = [];
-
- // 查找所有阿里云OSS链接
- const ossUrlPattern = /https?:\/\/my-supplier-photos\.oss-cn-chengdu\.aliyuncs\.com[^"\'\[\]\\,]+/g;
- const ossUrls = imageUrlsStr.match(ossUrlPattern) || [];
- ossUrls.forEach(url => allUrls.push(url));
-
- // 如果没有找到OSS链接,尝试通用URL模式
- if (allUrls.length === 0) {
- const genericUrlPattern = /https?:\/\/[^"\'\[\]\\,]+/g;
- const genericUrls = imageUrlsStr.match(genericUrlPattern) || [];
- genericUrls.forEach(url => allUrls.push(url));
- }
-
- // 最后的处理:确保返回的是干净的URL数组
- productJSON.imageUrls = allUrls.map(url => {
- // 彻底清理每个URL
- let cleanUrl = url;
-
- // 移除所有可能的末尾特殊字符
- while (cleanUrl && (cleanUrl.endsWith('\\') || cleanUrl.endsWith('"') || cleanUrl.endsWith("'"))) {
- cleanUrl = cleanUrl.slice(0, -1);
- }
-
- return cleanUrl;
- }).filter(url => url && url.startsWith('http')); // 过滤掉空值和无效URL
-
- // 如果经过所有处理后仍然没有URL,设置一个默认图片或空数组
- if (productJSON.imageUrls.length === 0) {
- productJSON.imageUrls = [];
- }
-
- // 记录第一个商品的处理信息
- if (productsWithSelected.indexOf(product) === 0) {
- console.log('商品列表 - imageUrls处理结果:');
- console.log('- 原始字符串长度:', imageUrlsStr.length);
- console.log('- 提取到的URL数量:', productJSON.imageUrls.length);
- console.log('- 最终类型:', Array.isArray(productJSON.imageUrls) ? '数组' : typeof productJSON.imageUrls);
- if (productJSON.imageUrls.length > 0) {
- console.log('- 第一个URL示例:', productJSON.imageUrls[0]);
- console.log('- URL格式验证:', /^https?:\/\/.+$/.test(productJSON.imageUrls[0]) ? '有效' : '无效');
- }
- }
- } else {
- // 如果imageUrls为空,设置为空数组
- productJSON.imageUrls = [];
- }
- } catch (error) {
- console.error(`处理商品${productJSON.productId}的imageUrls时出错:`, error);
- // 兜底:确保始终返回数组
- productJSON.imageUrls = [];
- }
-
- // 记录第一个商品的转换信息用于调试
- if (productsWithSelected.indexOf(product) === 0) {
- console.log('商品列表 - 第一个商品毛重字段处理:');
- console.log('- 原始值:', grossWeightDetails.value, '类型:', grossWeightDetails.type);
- console.log('- 转换后的值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('- selected字段: 值=', productJSON.selected, '类型:', typeof productJSON.selected);
- }
-
- return productJSON;
- });;
-
- // 准备响应数据 - 修改格式以匹配前端期望
- const responseData = {
- success: true,
- code: 200,
- message: '获取商品列表成功',
- products: processedProducts,
- total: count,
- page: page,
- pageSize: pageSize,
- totalPages: Math.ceil(count / pageSize)
- };
-
- console.log(`准备返回的响应数据格式:`, JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
-
- // 添加详细的查询条件日志
- console.log(`最终查询条件:`, JSON.stringify(where, null, 2));
-
- res.json(responseData);
- } catch (error) {
- console.error('获取商品列表失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品列表失败',
- error: error.message
- });
- }
-});
-
-// 上传商品
-app.post('/api/products/upload', async (req, res) => {
- try {
- // 修复毛重字段处理逻辑
- let productData = req.body;
- if (productData && productData.productData) {
- productData = productData.productData; // 使用正确的productData对象
- }
-
- // 改进的毛重字段处理逻辑,与编辑API保持一致
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productData.grossWeight,
- type: typeof productData.grossWeight,
- isEmpty: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined,
- isNumeric: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined || !isNaN(parseFloat(productData.grossWeight)) && isFinite(productData.grossWeight),
- parsedValue: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined ? 0 : parseFloat(productData.grossWeight)
- };
-
- // 详细的日志记录
- console.log('上传商品 - 毛重字段详细分析:');
- console.log('- 原始值:', productData.grossWeight, '类型:', typeof productData.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行精确四舍五入,确保3位小数以上的值正确转换
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- productData.grossWeight = finalGrossWeight;
- console.log('上传商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('收到商品上传请求:', productData);
-
- // 验证必要字段
- if (!productData.sellerId || !productData.productName || !productData.price || !productData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的商品信息'
- });
- }
-
- // 检查sellerId是否为openid,如果是则查找对应的userId
- let actualSellerId = productData.sellerId;
-
- // 如果sellerId看起来像一个openid(包含特殊字符如'-'),则尝试查找对应的userId
- if (productData.sellerId.includes('-')) {
- console.log('sellerId看起来像openid,尝试查找对应的userId');
- const user = await User.findOne({
- where: {
- openid: productData.sellerId
- }
- });
-
- if (user && user.userId) {
- console.log(`找到了对应的userId: ${user.userId}`);
- actualSellerId = user.userId;
- } else {
- console.error(`未找到对应的用户记录,openid: ${productData.sellerId}`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '找不到对应的用户记录'
- });
- }
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 创建商品,使用实际的sellerId
- let product = await Product.create({
- ...productData,
- sellerId: actualSellerId, // 使用查找到的userId
- productId,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- product = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 与编辑API保持一致的处理逻辑
- if (product) {
- console.log('上传商品 - 处理前grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (product.grossWeight === undefined || product.grossWeight === null || product.grossWeight === '') {
- product.grossWeight = 0;
- console.log('上传商品 - 检测到空值,已设置为0');
- } else {
- // 否则转换为浮点数
- product.grossWeight = parseFloat(product.grossWeight);
- }
-
- console.log('上传商品 - 处理后grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品上传成功',
- data: {
- productId: product.productId,
- product: product
- }
- });
- } catch (error) {
- console.error('商品上传失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '商品上传失败',
- error: error.message
- });
- }
-});
-
-// 获取商品详情
-app.post('/api/products/detail', async (req, res) => {
- try {
- const { productId } = req.body;
-
- if (!productId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId参数'
- });
- }
-
- // 查询商品详情 - 排除hidden状态商品
- const product = await Product.findOne({
- where: {
- productId,
- status: { [Sequelize.Op.not]: 'hidden' }
- },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 对返回的商品数据中的grossWeight字段进行处理,确保是数字类型
- let updatedProduct = { ...product.toJSON() };
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: updatedProduct.grossWeight,
- type: typeof updatedProduct.grossWeight,
- isEmpty: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined,
- isNumeric: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined || !isNaN(parseFloat(updatedProduct.grossWeight)) && isFinite(updatedProduct.grossWeight),
- parsedValue: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined ? 0 : parseFloat(updatedProduct.grossWeight)
- };
-
- // 详细的日志记录
- console.log('商品详情 - 毛重字段详细分析:');
- console.log('- 原始值:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- updatedProduct.grossWeight = finalGrossWeight;
- console.log('商品详情 - 最终返回的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- res.json({
- success: true,
- code: 200,
- message: '获取商品详情成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('获取商品详情失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品详情失败',
- error: error.message
- });
- }
-});
-
-// 修改商品
-app.post('/api/products/edit', async (req, res) => {
- try {
- const { productId, ...updateData } = req.body;
- const { sellerId } = req.body;
-
- if (!productId || !sellerId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权修改此商品'
- });
- }
-
- // 更新商品信息
- await Product.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { productId }
- }
- );
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '修改商品成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('修改商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '修改商品失败',
- error: error.message
- });
- }
-});
-
-// 删除商品 - 将商品状态设置为hidden表示已删除
-app.post('/api/products/delete', async (req, res) => {
- console.log('收到删除商品请求:', req.body);
- try {
- const { productId, sellerId } = req.body;
-
- if (!productId || !sellerId) {
- console.error('删除商品失败: 缺少productId或sellerId参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- console.error('删除商品失败: 商品不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- console.error('删除商品失败: 权限不足 - 卖家ID不匹配', { expected: product.sellerId, actual: sellerId });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权删除此商品'
- });
- }
-
- console.log('准备更新商品状态为hidden,当前状态:', product.status);
-
- // 直接使用商品实例更新状态
- product.status = 'hidden';
- product.updated_at = new Date();
-
- try {
- // 先尝试保存商品实例
- await product.save();
- console.log('删除商品成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- // 如果保存失败,尝试使用update方法
- try {
- const updateResult = await Product.update(
- { status: 'hidden', updated_at: new Date() },
- { where: { productId } }
- );
- console.log('删除商品成功(使用update方法):', { productId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- // 如果update方法也失败,尝试直接执行SQL语句绕过ORM验证
- try {
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId',
- {
- replacements: {
- status: 'hidden',
- updatedAt: new Date(),
- productId: productId
- }
- }
- );
- console.log('删除商品成功(使用原始SQL):', { productId });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 从购物车中移除该商品
- const destroyResult = await CartItem.destroy({
- where: { productId }
- });
- console.log('从购物车移除商品结果:', destroyResult);
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除商品成功',
- product: {
- productId: updatedProduct.productId,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('删除商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除商品失败',
- error: error.message
- });
- }
-});
-
-// 更新商品联系人信息API
-app.post('/api/products/update-contacts', async (req, res) => {
- try {
- // 查找需要更新联系人信息的商品(已发布但联系人信息为空)
- const products = await Product.findAll({
- where: {
- status: 'published',
- [Sequelize.Op.or]: [
- { product_contact: null },
- { product_contact: '' },
- { contact_phone: null },
- { contact_phone: '' }
- ]
- },
- attributes: ['productId']
- });
-
- if (products.length === 0) {
- return res.json({
- success: true,
- code: 200,
- message: '没有需要更新联系人信息的商品'
- });
- }
-
- // 查找所有用户,用于随机分配联系人
- const users = await User.findAll({
- where: {
- [Sequelize.Op.or]: [
- { role: 'seller' },
- { role: '管理员' }
- ],
- [Sequelize.Op.and]: [
- { phoneNumber: { [Sequelize.Op.not]: null } },
- { phoneNumber: { [Sequelize.Op.not]: '' } },
- { nickName: { [Sequelize.Op.not]: null } },
- { nickName: { [Sequelize.Op.not]: '' } }
- ]
- },
- attributes: ['nickName', 'phoneNumber']
- });
-
- if (users.length === 0) {
- return res.json({
- success: false,
- code: 500,
- message: '没有可用的卖家用户来分配联系人信息'
- });
- }
-
- // 随机为每个商品分配联系人信息
- let updatedCount = 0;
- for (const product of products) {
- // 随机选择一个用户
- const randomUser = users[Math.floor(Math.random() * users.length)];
-
- // 更新商品联系人信息
- await Product.update(
- {
- product_contact: randomUser.nickName,
- contact_phone: randomUser.phoneNumber,
- updated_at: new Date()
- },
- {
- where: { productId: product.productId }
- }
- );
-
- updatedCount++;
- }
-
- res.json({
- success: true,
- code: 200,
- message: `商品联系人信息更新成功,共更新了${updatedCount}个商品`
- });
- } catch (error) {
- console.error('更新商品联系人信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新商品联系人信息失败',
- error: error.message
- });
- }
-});
-
-// 添加商品到购物车
-app.post('/api/cart/add', async (req, res) => {
- // 增加全局错误捕获,确保即使在try-catch外部的错误也能被处理
- try {
- console.log('收到添加到购物车请求 - 开始处理', req.url);
- let cartData = req.body;
- console.log('收到添加到购物车请求数据:', cartData);
- console.log('请求头:', req.headers);
- console.log('请求IP:', req.ip);
-
- // 兼容客户端请求格式:客户端可能将数据封装在product对象中,并且使用openid而不是userId
- if (cartData.product && !cartData.productId) {
- // 从product对象中提取数据
- const productData = cartData.product;
- console.log('从product对象提取数据:', productData);
- console.log('客户端提供的openid:', cartData.openid);
-
- // 使用openid作为userId
- cartData = {
- userId: cartData.openid || productData.userId,
- productId: productData.productId || productData.id,
- productName: productData.productName || productData.name,
- quantity: productData.quantity || 1,
- price: productData.price,
- specification: productData.specification || productData.spec || '',
- grossWeight: productData.grossWeight || productData.weight,
- yolk: productData.yolk || productData.variety || '',
- testMode: productData.testMode || cartData.testMode
- };
- console.log('转换后的购物车数据:', cartData);
-
- // 检查转换后的userId是否存在于users表中
- try {
- console.log('开始查询用户信息,openid:', cartData.userId);
- const user = await User.findOne({
- where: { openid: cartData.userId }
- });
- if (user) {
- console.log(`找到对应的用户记录: openid=${cartData.userId}, userId=${user.userId}`);
- // 修正:使用数据库中真实的userId而不是openid
- cartData.userId = user.userId;
- console.log('修正后的userId:', cartData.userId);
- } else {
- console.error(`未找到openid为 ${cartData.userId} 的用户记录,无法添加到购物车`);
- // 重要:找不到用户时返回错误,避免使用无效的userId导致外键约束失败
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `未找到用户记录: ${cartData.userId}`
- });
- }
- } catch (error) {
- console.error('查询用户信息失败:', error);
- // 查询失败时也返回错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '查询用户信息失败',
- error: error.message
- });
- }
- }
-
- // 验证必要字段
- if (!cartData.userId || !cartData.productId || !cartData.productName || !cartData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的购物车信息',
- missingFields: [
- !cartData.userId ? 'userId' : '',
- !cartData.productId ? 'productId' : '',
- !cartData.productName ? 'productName' : '',
- !cartData.quantity ? 'quantity' : ''
- ].filter(Boolean)
- });
- }
-
- // 先验证用户ID是否存在于users表中
- try {
- const userExists = await User.findOne({
- where: { userId: cartData.userId }
- });
- if (!userExists) {
- console.error(`用户ID ${cartData.userId} 不存在于users表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `用户ID ${cartData.userId} 不存在`
- });
- } else {
- console.log(`用户ID ${cartData.userId} 存在于users表中,用户验证通过`);
- }
- } catch (error) {
- console.error('验证用户ID失败:', error);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '验证用户信息失败',
- error: error.message
- });
- }
-
- // 检查商品是否存在以及是否为hidden状态
- console.log(`检查商品ID: ${cartData.productId} 是否存在于products表中`);
- const product = await Product.findOne({
- where: {
- productId: cartData.productId
- }
- });
-
- if (!product) {
- console.error(`商品ID ${cartData.productId} 不存在于products表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '商品不存在或已被移除',
- error: `未找到商品ID: ${cartData.productId}`
- });
- } else {
- console.log(`商品ID ${cartData.productId} 存在于products表中,商品名称: ${product.productName}`);
- }
-
- if (product.status === 'hidden') {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '该商品已下架,无法添加到购物车'
- });
- }
-
- // 在testMode下,不执行实际的数据库操作,直接返回成功
- if (cartData.testMode) {
- console.log('测试模式:跳过实际的数据库操作');
- res.json({
- success: true,
- code: 200,
- message: '测试模式:添加到购物车成功',
- data: {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity
- }
- });
- return;
- }
-
- // 检查是否已存在相同商品
- const existingItem = await CartItem.findOne({
- where: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
-
- // 添加try-catch捕获外键约束错误
- try {
- console.log(`准备创建/更新购物车项: userId=${cartData.userId}, productId=${cartData.productId}`);
- if (existingItem) {
- // 已存在,更新数量
- await CartItem.update(
- {
- quantity: existingItem.quantity + cartData.quantity,
- updated_at: new Date()
- },
- {
- where: {
- id: existingItem.id
- }
- }
- );
- console.log(`更新购物车项成功: id=${existingItem.id}, 新数量=${existingItem.quantity + cartData.quantity}`);
- } else {
- // 不存在,创建新购物车项
- console.log('创建新购物车项,所有字段:', {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity,
- price: cartData.price,
- specification: cartData.specification,
- grossWeight: cartData.grossWeight,
- yolk: cartData.yolk
- });
- // 重要:在创建前再次验证数据完整性
- if (!cartData.userId || !cartData.productId) {
- throw new Error(`数据不完整: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- await CartItem.create({
- ...cartData,
- added_at: new Date()
- });
- console.log(`创建购物车项成功: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- } catch (createError) {
- console.error('创建/更新购物车项失败,可能是外键约束问题:', createError);
- console.error('详细错误信息:', {
- name: createError.name,
- message: createError.message,
- stack: createError.stack,
- sql: createError.sql || '无SQL信息',
- userId: cartData.userId,
- productId: cartData.productId
- });
-
- // 检测是否是外键约束错误
- if (createError.name === 'SequelizeForeignKeyConstraintError' || createError.message.includes('foreign key')) {
- // 区分是用户ID还是商品ID问题
- let errorField = 'productId';
- let errorMessage = '商品信息已更新,请刷新页面后重试';
-
- if (createError.message.includes('userId') || createError.message.includes('user') || createError.message.toLowerCase().includes('user')) {
- errorField = 'userId';
- errorMessage = '用户信息无效,请重新登录后重试';
- }
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: errorMessage,
- error: `外键约束错误: ${errorField} 不存在或已失效`,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 其他类型的错误也返回400状态码,避免500错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '添加购物车项失败,请稍后重试',
- error: createError.message,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 更新商品的预约人数 - 更健壮的实现
- try {
- console.log(`尝试更新商品预约人数: productId=${cartData.productId}`);
-
- // 先验证商品是否存在
- const productCheck = await Product.findOne({where: {productId: cartData.productId}});
- if (productCheck) {
- // 商品存在,才进行更新
- await Product.increment('reservedCount', {by: 1, where: {productId: cartData.productId}});
- console.log(`商品预约人数更新成功: productId=${cartData.productId}, 新数量=${productCheck.reservedCount + 1}`);
- } else {
- console.error(`更新商品预约人数失败: 商品ID ${cartData.productId} 不存在`);
- }
- } catch (updateError) {
- console.error(`更新商品预约人数失败:`, updateError);
- // 继续执行,不中断主要流程
- }
-
- res.json({
- success: true,
- code: 200,
- message: '添加到购物车成功'
- });
- } catch (error) {
- console.error('添加到购物车失败:', error);
- console.error('全局错误捕获,详细信息:', {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- });
-
- // 增强的错误处理 - 强制所有错误返回400状态码
- console.error('全局错误处理 - 捕获到未处理的错误:', error);
- const statusCode = 400; // 强制所有错误返回400状态码,避免前端显示500错误
- let errorMessage = '添加到购物车失败';
-
- // 更精确地检测外键约束错误
- if (error.name === 'SequelizeForeignKeyConstraintError' ||
- error.message.toLowerCase().includes('foreign key') ||
- error.message.toLowerCase().includes('constraint fails') ||
- error.message.toLowerCase().includes('constraint')) {
- errorMessage = '添加到购物车失败:商品或用户信息已更新,请刷新页面后重试';
- console.error('检测到外键约束相关错误,返回400状态码');
- }
-
- console.log(`准备返回错误响应 - 状态码: ${statusCode}, 消息: ${errorMessage}`);
-
- // 确保响应能够正确发送
- try {
- res.status(statusCode).json({
- success: false,
- code: statusCode,
- message: errorMessage,
- error: error.message,
- errorDetails: {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- }
- });
- } catch (resError) {
- console.error('发送错误响应失败:', resError);
- // 即使发送响应失败,也尝试以文本格式发送
- try {
- res.status(400).send('添加到购物车失败,请刷新页面后重试');
- } catch (finalError) {
- console.error('无法发送任何响应:', finalError);
- }
- }
- }
-});
-
-// 获取购物车信息
-app.post('/api/cart/get', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 查询购物车信息 - 排除关联商品为hidden或sold_out状态的项
- const cartItems = await CartItem.findAll({
- where: { userId },
- include: [
- {
- model: Product,
- as: 'product',
- attributes: ['productName', 'price', 'quantity', 'status', 'specification', 'grossWeight', 'yolk'],
- where: {
- status: { [Sequelize.Op.notIn]: ['hidden', 'sold_out'] }
- }
- }
- ],
- order: [['added_at', 'DESC']]
- });
-
- res.json({
- success: true,
- code: 200,
- message: '获取购物车信息成功',
- data: {
- cartItems
- }
- });
- } catch (error) {
- console.error('获取购物车信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取购物车信息失败',
- error: error.message
- });
- }
-});
-
-// 更新购物车项
-app.post('/api/cart/update', async (req, res) => {
- try {
- const { id, quantity, selected } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 构建更新数据
- const updateData = {};
- if (quantity !== undefined) updateData.quantity = quantity;
- if (selected !== undefined) updateData.selected = selected;
- updateData.updated_at = new Date();
-
- // 更新购物车项
- await CartItem.update(updateData, {
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '更新购物车成功'
- });
- } catch (error) {
- console.error('更新购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新购物车失败',
- error: error.message
- });
- }
-});
-
-// 删除购物车项
-app.post('/api/cart/delete', async (req, res) => {
- try {
- const { id } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 删除购物车项
- await CartItem.destroy({
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除购物车项成功'
- });
- } catch (error) {
- console.error('删除购物车项失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除购物车项失败',
- error: error.message
- });
- }
-});
-
-// 清空购物车
-app.post('/api/cart/clear', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 清空购物车
- await CartItem.destroy({
- where: { userId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '清空购物车成功'
- });
- } catch (error) {
- console.error('清空购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '清空购物车失败',
- error: error.message
- });
- }
-});
-
-// 测试连接接口
-app.get('/api/test-connection', async (req, res) => {
- try {
- // 检查数据库连接
- await sequelize.authenticate();
-
- res.json({
- success: true,
- code: 200,
- message: '服务器连接成功,数据库可用',
- timestamp: new Date().toISOString(),
- serverInfo: {
- port: PORT,
- nodeVersion: process.version,
- database: 'MySQL',
- status: 'running'
- }
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- code: 500,
- message: '服务器连接失败',
- error: error.message
- });
- }
-});
-
-// 用户类型调试接口 - 增强版:用于排查用户类型和商品显示问题
-app.post('/api/user/debug', async (req, res) => {
- try {
- const { openid } = req.body;
-
- console.log('收到用户调试请求,openid:', openid);
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查询用户信息
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在',
- debugInfo: {
- searchCriteria: { openid },
- timestamp: new Date().toISOString()
- }
- });
- }
-
- // 查询该用户的商品统计信息
- const totalProducts = await Product.count({ where: { sellerId: user.userId } });
- const pendingProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'pending_review'
- }
- });
- const reviewedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'reviewed'
- }
- });
- const publishedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'published'
- }
- });
- const soldOutProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'sold_out'
- }
- });
-
- // 判断用户是否有权限查看所有商品
- const canViewAllProducts = ['seller', 'both', 'admin'].includes(user.type);
-
- // 获取该用户的最新5个商品信息(用于调试)
- const latestProducts = await Product.findAll({
- where: { sellerId: user.userId },
- limit: 5,
- order: [['created_at', 'DESC']],
- attributes: ['productId', 'productName', 'status', 'created_at']
- });
-
- const responseData = {
- success: true,
- code: 200,
- message: '获取用户调试信息成功',
- userInfo: user,
- productStats: {
- total: totalProducts,
- pendingReview: pendingProducts,
- reviewed: reviewedProducts,
- published: publishedProducts,
- soldOut: soldOutProducts
- },
- permissionInfo: {
- canViewAllProducts: canViewAllProducts,
- userType: user.type,
- allowedTypesForViewingAllProducts: ['seller', 'both', 'admin']
- },
- latestProducts: latestProducts,
- debugInfo: {
- userCount: await User.count(),
- totalProductsInSystem: await Product.count(),
- timestamp: new Date().toISOString(),
- serverTime: new Date().toLocaleString('zh-CN')
- }
- };
-
- console.log('调试信息返回数据:', JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
- res.json(responseData);
- } catch (error) {
- console.error('获取用户调试信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户调试信息失败',
- error: error.message,
- debugInfo: {
- errorStack: error.stack,
- timestamp: new Date().toISOString()
- }
- });
- }
-});
-
-// 下架商品接口 - 将商品状态设置为sold_out表示已下架
-app.post('/api/product/hide', async (req, res) => {
- console.log('收到下架商品请求:', req.body);
-
- try {
- const { openid, productId } = req.body;
-
- // 验证请求参数
- if (!openid || !productId) {
- console.error('下架商品失败: 缺少必要参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要参数: openid和productId都是必需的'
- });
- }
-
- // 查找用户
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('下架商品失败: 用户不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- console.log('找到用户信息:', { userId: user.userId, nickName: user.nickName });
-
- // 查找商品并验证所有权 - 直接使用userId,因为商品创建时使用的就是userId
- const product = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
-
- if (!product) {
- console.error('下架商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 记录当前状态,用于调试
- console.log('当前商品状态:', product.status, '允许的状态列表:', Product.rawAttributes.status.validate.isIn);
- console.log('商品所属卖家ID:', product.sellerId);
- console.log('用户ID信息对比:', { userId: user.userId, id: user.id });
-
- console.log('准备更新商品状态为sold_out,当前状态:', product.status);
-
- // 更新商品状态为已下架(sold_out) - 尝试多种更新方式确保成功
- try {
- // 方法1: 直接保存实例
- product.status = 'sold_out';
- product.updated_at = new Date();
- await product.save();
- console.log('商品下架成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- try {
- // 方法2: 使用update方法
- const updateResult = await Product.update(
- { status: 'sold_out', updated_at: new Date() },
- { where: { productId: productId, sellerId: user.userId } }
- );
- console.log('商品下架成功(使用update方法):', { productId: productId, sellerIdType: typeof user.userId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- try {
- // 方法3: 直接执行SQL语句绕过ORM验证
- const replacements = {
- status: 'sold_out',
- updatedAt: new Date(),
- productId: productId,
- sellerId: user.userId
- };
-
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId AND sellerId = :sellerId',
- {
- replacements: replacements
- }
- );
- console.log('商品下架成功(使用原始SQL):', { productId: product.productId, productName: product.productName });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: product.sellerId // 使用找到的商品的sellerId进行查询
- }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '商品下架成功',
- product: {
- productId: updatedProduct.productId,
- productName: updatedProduct.productName,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('下架商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '下架商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 发布商品API
-app.post('/api/product/publish', async (req, res) => {
- console.log('收到发布商品请求:', req.body); // 记录完整请求体
-
- try {
- const { openid, product } = req.body;
-
- // 验证必填字段
- console.log('验证请求参数: openid=', !!openid, ', product=', !!product);
- if (!openid || !product) {
- console.error('缺少必要参数: openid=', openid, 'product=', product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid或product对象)'
- });
- }
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price, '转换为数字=', parseFloat(product.price));
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity, '转换为数字=', parseInt(product.quantity));
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误和字段值详情
- const validationErrors = [];
- const fieldDetails = {};
-
- // 检查商品名称
- fieldDetails.productName = {
- value: product.productName,
- type: typeof product.productName,
- isEmpty: !product.productName || product.productName.trim() === ''
- };
- if (fieldDetails.productName.isEmpty) {
- console.error('商品名称为空');
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- }
-
- // 检查价格
- fieldDetails.price = {
- value: product.price,
- type: typeof product.price,
- isNumber: !isNaN(parseFloat(product.price)) && isFinite(product.price),
- parsedValue: parseFloat(product.price),
- isValid: !isNaN(parseFloat(product.price)) && isFinite(product.price) && parseFloat(product.price) > 0
- };
- if (!product.price) {
- console.error('价格为空');
- validationErrors.push('价格为必填项');
- } else if (!fieldDetails.price.isNumber) {
- console.error('价格不是有效数字: price=', product.price);
- validationErrors.push('价格必须是有效数字格式');
- } else if (fieldDetails.price.parsedValue <= 0) {
- console.error('价格小于等于0: price=', product.price, '转换为数字后=', fieldDetails.price.parsedValue);
- validationErrors.push('价格必须大于0');
- }
-
- // 检查数量
- fieldDetails.quantity = {
- value: product.quantity,
- type: typeof product.quantity,
- isNumeric: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity),
- parsedValue: Math.floor(parseFloat(product.quantity)),
- isValid: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity) && parseFloat(product.quantity) > 0
- };
- if (!product.quantity) {
- console.error('数量为空');
- validationErrors.push('数量为必填项');
- } else if (!fieldDetails.quantity.isNumeric) {
- console.error('数量不是有效数字: quantity=', product.quantity);
- validationErrors.push('数量必须是有效数字格式');
- } else if (fieldDetails.quantity.parsedValue <= 0) {
- console.error('数量小于等于0: quantity=', product.quantity, '转换为数字后=', fieldDetails.quantity.parsedValue);
- validationErrors.push('数量必须大于0');
- }
-
- // 改进的毛重字段处理逻辑 - 与其他API保持一致
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('发布商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保商品名称不超过数据库字段长度限制
- if (product.productName && product.productName.length > 255) {
- console.error('商品名称过长: 长度=', product.productName.length);
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 如果有验证错误,一次性返回所有错误信息和字段详情
- if (validationErrors.length > 0) {
- console.error('验证失败 - 详细信息:', JSON.stringify({
- errors: validationErrors,
- fieldDetails: fieldDetails
- }, null, 2));
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors,
- detailedMessage: validationErrors.join('; '),
- fieldDetails: fieldDetails
- });
- }
-
- // 查找用户
- console.log('开始查找用户: openid=', openid);
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- console.log('找到用户:', { userId: user.userId, nickName: user.nickName, type: user.type });
-
- // 验证用户类型
- console.log(`验证用户类型: 用户ID=${user.userId}, 类型=${user.type}`);
- if (user.type !== 'seller' && user.type !== 'both') {
- console.error(`商品发布失败: 用户${user.userId}类型为${user.type},需要seller或both类型`);
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有卖家才能发布商品,请在个人资料中修改用户类型'
- });
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
- console.log('生成商品ID:', productId);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行正确的四舍五入
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- console.log('发布商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 创建商品
- console.log('准备创建商品:', {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight,
- sellerId: user.userId
- });
-
- const newProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk || '',
- specification: product.specification || '',
- status: 'pending_review', // 默认状态为待审核
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- const createdProduct = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- if (createdProduct) {
- console.log('发布商品 - 数据库查询后grossWeight:', createdProduct.grossWeight, '类型:', typeof createdProduct.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品发布成功',
- product: createdProduct,
- productId: productId
- });
-
- } catch (error) {
- console.error('发布商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '发布商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 启动服务器
-app.listen(PORT, () => {
- console.log(`服务器运行在 http://localhost:${PORT}`);
- console.log('注意:当前服务器已添加详细日志记录,用于排查发布商品问题');
- console.log('调试API: POST /api/user/debug - 用于查看用户类型信息');
- console.log(`测试连接接口: http://localhost:${PORT}/api/test-connection`);
-});
-
-// 编辑商品API - 用于审核失败商品重新编辑
-app.post('/api/product/edit', async (req, res) => {
- console.log('收到编辑商品请求 - 详细信息:');
- console.log('- 请求路径:', req.url);
- console.log('- 请求方法:', req.method);
- console.log('- 请求完整body:', req.body);
- console.log('- 服务器端口:', PORT);
- console.log('- 环境变量:', process.env.PORT);
-
- try {
- // 正确解析请求参数,处理嵌套的productData结构
- let openid = req.body.openid;
- let productId = req.body.productId;
- let status = req.body.status;
- let testMode = req.body.testMode;
- let product = req.body.product;
-
- // 处理多层嵌套的productData结构
- if (!product && req.body.productData) {
- // 处理第一种情况: { productData: { openid, productId, product: { ... } } }
- if (req.body.productData.product) {
- product = req.body.productData.product;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- // 处理第二种情况: { productData: { openid, productId, productName, price, ... } }
- else {
- product = req.body.productData;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- }
-
- // 调试日志
- console.log('解析后参数:', { openid, productId, status, testMode, product: !!product });
-
- console.log('收到编辑商品请求,包含状态参数:', { openid, productId, status, testMode });
-
- // 验证必填字段
- if (!openid || !productId || !product) {
- console.error('缺少必要参数: openid=', !!openid, 'productId=', !!productId, 'product=', !!product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid、productId或product对象)'
- });
- }
-
- // 查找用户
- let user = null;
-
- // 测试模式下的特殊处理
- if (testMode) {
- console.log('测试模式:尝试查找或创建测试用户');
- // 首先尝试查找openid为'test_openid'的用户
- user = await User.findOne({
- where: { openid: 'test_openid' }
- });
-
- if (!user) {
- // 如果不存在,创建一个新的测试用户
- console.log('测试模式:创建测试用户');
- try {
- user = await User.create({
- openid: 'test_openid',
- userId: 'test_user_id',
- nickName: '测试用户',
- type: 'seller'
- });
- } catch (createError) {
- console.error('测试模式:创建测试用户失败', createError);
- // 如果创建失败,尝试查找数据库中的第一个用户
- user = await User.findOne({
- order: [['id', 'ASC']]
- });
- if (user) {
- console.log('测试模式:使用数据库中的现有用户', user.userId);
- }
- }
- } else {
- console.log('测试模式:使用已存在的测试用户', user.userId);
- }
- } else {
- // 非测试模式:按常规方式查找用户
- user = await User.findOne({ where: { openid } });
- }
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- // 查找商品
- let existingProduct = null;
-
- if (testMode) {
- // 测试模式:如果找不到商品,尝试使用测试商品或创建一个新的测试商品
- existingProduct = await Product.findOne({
- where: {
- productId: productId
- }
- });
-
- // 如果找不到指定的商品,创建一个新的测试商品
- if (!existingProduct) {
- console.log('测试模式:创建测试商品');
- try {
- existingProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: '测试商品',
- price: 99.99,
- quantity: 100,
- grossWeight: 0, // 默认为0而不是5,符合用户需求
- yolk: '测试描述',
- specification: '测试规格',
- status: 'rejected', // 设置为可编辑状态
- created_at: new Date(),
- updated_at: new Date()
- });
- console.log('测试模式:测试商品创建成功');
- } catch (createProductError) {
- console.error('测试模式:创建测试商品失败', createProductError);
- }
- }
- } else {
- // 非测试模式:验证商品所有权
- existingProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
- }
-
- if (!existingProduct) {
- console.error('编辑商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 验证商品状态是否允许编辑
- if (!['rejected', 'sold_out', 'pending_review', 'reviewed'].includes(existingProduct.status)) {
- console.error(`编辑商品失败: 商品状态(${existingProduct.status})不允许编辑`, {
- productId: productId,
- sellerId: user.userId,
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有审核失败、已下架、审核中或已审核的商品才能编辑',
- debugInfo: {
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- }
- });
- }
-
- // 记录商品编辑信息,用于调试
- console.log(`允许编辑商品: productId=${productId}, status=${existingProduct.status}, sellerId=${user.userId}`);
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price);
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity);
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误
- const validationErrors = [];
-
- // 检查商品名称
- if (!product.productName || product.productName.trim() === '') {
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- } else if (product.productName.length > 255) {
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 检查价格
- if (!product.price) {
- validationErrors.push('价格为必填项');
- } else if (isNaN(parseFloat(product.price)) || parseFloat(product.price) <= 0) {
- validationErrors.push('价格必须是大于0的有效数字');
- }
-
- // 检查数量
- if (!product.quantity) {
- validationErrors.push('数量为必填项');
- } else if (isNaN(parseInt(product.quantity)) || parseInt(product.quantity) <= 0) {
- validationErrors.push('数量必须是大于0的有效数字');
- }
-
- // 改进的毛重字段处理逻辑,与其他API保持一致,空值默认设为0
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('编辑商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保grossWeight值是数字类型
- const finalGrossWeight = Number(grossWeightDetails.parsedValue);
- console.log('编辑商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 如果有验证错误,返回错误信息
- if (validationErrors.length > 0) {
- console.error('验证失败 - 错误:', validationErrors.join('; '));
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors
- });
- }
-
- // 准备更新的商品数据
- const updatedProductData = {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk,
- specification: product.specification,
- // 优先使用前端传递的status参数,如果没有传递则使用原来的逻辑
- status: status && ['pending_review', 'published'].includes(status) ? status :
- (product.resubmit && ['rejected', 'sold_out'].includes(existingProduct.status)) ? 'pending_review' : existingProduct.status,
- rejectReason: (status === 'pending_review' || (product.resubmit && existingProduct.status === 'rejected')) ? null : existingProduct.rejectReason, // 提交审核时清除拒绝原因
- updated_at: new Date()
- };
-
- console.log('准备更新商品数据:', { productId, updatedStatus: updatedProductData.status, fromStatus: existingProduct.status });
-
- // 更新商品
- const [updatedCount] = await Product.update(updatedProductData, {
- where: testMode ? {
- // 测试模式:只根据productId更新
- productId: productId
- } : {
- // 非测试模式:验证商品所有权
- productId: productId,
- sellerId: user.userId
- }
- });
-
- // 检查更新是否成功
- if (updatedCount === 0) {
- console.error('商品更新失败: 没有找到匹配的商品或权限不足');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品更新失败: 没有找到匹配的商品或权限不足'
- });
- }
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({ where: { productId: productId } });
-
- console.log('查询数据库后 - 更新的商品信息:', {
- grossWeight: updatedProduct?.grossWeight,
- grossWeightType: typeof updatedProduct?.grossWeight,
- productId: updatedProduct?.productId,
- status: updatedProduct?.status
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 注意:这里检查undefined和null,并且对于空字符串或5的情况也进行处理
- if (updatedProduct) {
- console.log('处理前 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (updatedProduct.grossWeight === undefined || updatedProduct.grossWeight === null || updatedProduct.grossWeight === '') {
- updatedProduct.grossWeight = 0;
- console.log('检测到空值 - 已设置为0');
- } else {
- // 否则转换为浮点数
- updatedProduct.grossWeight = parseFloat(updatedProduct.grossWeight);
- }
-
- console.log('处理后 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- }
-
- console.log('商品编辑成功:', {
- productId: productId,
- productName: product.productName,
- oldStatus: existingProduct.status, // 记录更新前的状态
- newStatus: updatedProduct.status, // 记录更新后的状态
- grossWeight: updatedProduct.grossWeight // 记录处理后的毛重值
- });
-
- // 根据新的状态生成适当的返回消息
- let returnMessage = '';
- if (updatedProduct.status === 'pending_review') {
- returnMessage = '商品编辑成功,已重新提交审核';
- } else if (updatedProduct.status === 'published') {
- returnMessage = '商品编辑成功,已上架';
- } else if (updatedProduct.status === existingProduct.status) {
- returnMessage = '商品编辑成功,状态保持不变';
- } else {
- returnMessage = '商品编辑成功';
- }
-
- res.json({
- success: true,
- code: 200,
- message: returnMessage,
- product: updatedProduct
- });
- } catch (error) {
- console.error('编辑商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '编辑商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 提交入驻申请
-app.post('/api/settlement/submit', async (req, res) => {
- try {
- const { openid,
- collaborationid,
- cooperation,
- company,
- phoneNumber,
- province,
- city,
- district,
- businesslicenseurl,
- proofurl,
- brandurl
- } = req.body;
-
- console.log('收到入驻申请:', req.body);
-
- // 验证必填字段
- if (!openid || !collaborationid || !cooperation || !company || !phoneNumber || !province || !city || !district) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整的申请信息'
- });
- }
-
- // 查找用户信息
- const user = await User.findOne({ where: { openid } });
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 检查用户是否已有入驻信息且状态为审核中
- if (user.collaborationid && user.partnerstatus === 'underreview') {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '您已有待审核的入驻申请,请勿重复提交'
- });
- }
-
- // 更新用户表中的入驻信息
- // 转换collaborationid为中文(使用明确的英文标识以避免混淆)
- let collaborationidCN = collaborationid;
- if (collaborationid === 'chicken') {
- collaborationidCN = '鸡场';
- } else if (collaborationid === 'trader') {
- collaborationidCN = '贸易商';
- }
- // 兼容旧的wholesale标识
- else if (collaborationid === 'wholesale') {
- collaborationidCN = '贸易商';
- }
-
- // 转换cooperation为中文合作模式(使用明确的英文标识以避免混淆)
- // 直接使用传入的中文合作模式,确保支持:资源委托、自主定义销售、区域包场合作、其他
- let cooperationCN = cooperation;
-
- // 如果传入的是英文值,则进行映射
- if (cooperation === 'resource_delegation') {
- cooperationCN = '资源委托';
- } else if (cooperation === 'self_define_sales') {
- cooperationCN = '自主定义销售';
- } else if (cooperation === 'regional_exclusive') {
- cooperationCN = '区域包场合作';
- } else if (cooperation === 'other') {
- cooperationCN = '其他';
- }
- // 兼容旧的wholesale标识
- else if (cooperation === 'wholesale') {
- cooperationCN = '资源委托';
- }
- // 兼容旧的self_define标识
- else if (cooperation === 'self_define') {
- cooperationCN = '自主定义销售';
- }
- // 确保存储的是中文合作模式
-
- // 执行更新操作
- const updateResult = await User.update({
- collaborationid: collaborationidCN, // 合作商身份(中文)
- cooperation: cooperationCN, // 合作模式(中文)
- company: company, // 公司名称
- phoneNumber: phoneNumber, // 电话号码
- province: province, // 省份
- city: city, // 城市
- district: district, // 区县
- businesslicenseurl: businesslicenseurl || '', // 营业执照 - NOT NULL约束,使用空字符串
- proofurl: proofurl || '', // 证明材料 - NOT NULL约束,使用空字符串
- brandurl: brandurl || '', // 品牌授权链文件
- partnerstatus: 'underreview', // 合作商状态明确设置为审核中,覆盖数据库默认值
- updated_at: new Date()
- }, {
- where: { userId: user.userId }
- });
-
- // 验证更新是否成功
- const updatedUser = await User.findOne({ where: { userId: user.userId } });
- console.log('更新后的用户状态:', updatedUser.partnerstatus);
-
- // 双重确认:如果状态仍不是underreview,再次更新
- if (updatedUser && updatedUser.partnerstatus !== 'underreview') {
- console.warn('检测到状态未更新正确,执行二次更新:', updatedUser.partnerstatus);
- await User.update({
- partnerstatus: 'underreview'
- }, {
- where: { userId: user.userId }
- });
- }
-
- console.log('用户入驻信息更新成功,用户ID:', user.userId);
-
- res.json({
- success: true,
- code: 200,
- message: '入驻申请提交成功,请等待审核',
- data: {
- status: 'pending'
- }
- });
- } catch (error) {
- console.error('提交入驻申请失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '提交入驻申请失败: ' + error.message
- });
- }
-});
-
-// 导出模型和Express应用供其他模块使用
-module.exports = {
- User,
- Product,
- CartItem,
- sequelize,
- createUserAssociations,
- app,
- PORT
-};
\ No newline at end of file
diff --git a/server-example/server-mysql-backup-count.js b/server-example/server-mysql-backup-count.js
deleted file mode 100644
index c8dee55..0000000
--- a/server-example/server-mysql-backup-count.js
+++ /dev/null
@@ -1,2970 +0,0 @@
-// ECS服务器示例代码 - Node.js版 (MySQL版本)
-const express = require('express');
-const crypto = require('crypto');
-const bodyParser = require('body-parser');
-const { Sequelize, DataTypes, Model, Op } = require('sequelize');
-require('dotenv').config();
-
-// 创建Express应用
-const app = express();
-const PORT = process.env.PORT || 3002;
-
-// 中间件
-app.use(bodyParser.json());
-
-// 添加请求日志中间件,捕获所有到达服务器的请求(必须放在bodyParser之后)
-app.use((req, res, next) => {
- // 将UTC时间转换为北京时间(UTC+8)
- const now = new Date();
- const beijingTime = new Date(now.getTime() + 8 * 60 * 60 * 1000);
- const formattedTime = beijingTime.toISOString().replace('Z', '+08:00');
-
- console.log(`[${formattedTime}] 收到请求: ${req.method} ${req.url}`);
- console.log('请求头:', req.headers);
- console.log('请求体:', req.body);
- next();
-});
-
-// 商品毛重处理中间件 - 确保所有返回的商品数据中毛重字段保持原始值
-app.use((req, res, next) => {
- // 保存原始的json方法
- const originalJson = res.json;
-
- // 重写json方法来处理响应数据
- res.json = function (data) {
- // 检查数据中是否包含商品列表
- if (data && typeof data === 'object') {
- // 处理/products/list接口的响应
- if (data.products && Array.isArray(data.products)) {
- data.products = data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
-
- // 处理/data字段中的商品列表
- if (data.data && data.data.products && Array.isArray(data.data.products)) {
- data.data.products = data.data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
- }
-
- // 调用原始的json方法
- return originalJson.call(this, data);
- };
-
- next();
-});
-
-// 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
- }
- }
-);
-
-// 微信小程序配置
-const WECHAT_CONFIG = {
- APPID: process.env.WECHAT_APPID || 'your-wechat-appid',
- APPSECRET: process.env.WECHAT_APPSECRET || 'your-wechat-appsecret',
- TOKEN: process.env.WECHAT_TOKEN || 'your-wechat-token'
-};
-
-// 显示当前使用的数据库配置(用于调试)
-console.log('当前数据库连接配置:');
-console.log(' 主机:', process.env.DB_HOST || 'localhost');
-console.log(' 端口:', process.env.DB_PORT || 3306);
-console.log(' 数据库名:', process.env.DB_DATABASE || 'wechat_app');
-console.log(' 用户名:', process.env.DB_USER || 'root');
-console.log(' 密码:', process.env.DB_PASSWORD === undefined || process.env.DB_PASSWORD === '' ? '无密码' : '******');
-
-// 测试数据库连接
-async function testDbConnection() {
- try {
- await sequelize.authenticate();
- console.log('数据库连接成功');
- } catch (error) {
- console.error('数据库连接失败:', error);
- console.error('\n请检查以下几点:');
- console.error('1. MySQL服务是否已经启动');
- console.error('2. wechat_app数据库是否已创建');
- console.error('3. .env文件中的数据库用户名和密码是否正确');
- console.error('4. 用户名是否有足够的权限访问数据库');
- console.error('\n如果是首次配置,请参考README文件中的数据库设置指南。');
- process.exit(1);
- }
-}
-
-testDbConnection();
-
-// 定义数据模型
-
-// 用户模型
-class User extends Model { }
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- openid: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 微信名,必填
- },
- avatarUrl: {
- type: DataTypes.TEXT
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 电话号码,必填
- },
- type: {
- type: DataTypes.STRING(20),
- allowNull: false // 用户身份(buyer/seller/both),必填
- },
- gender: {
- type: DataTypes.INTEGER
- },
- country: {
- type: DataTypes.STRING(50)
- },
- province: {
- type: DataTypes.STRING(50)
- },
- city: {
- type: DataTypes.STRING(50)
- },
- language: {
- type: DataTypes.STRING(20)
- },
- session_key: {
- type: DataTypes.STRING(255)
- },
- // 新增字段
- company: {
- type: DataTypes.STRING(255) // 客户公司
- },
- region: {
- type: DataTypes.STRING(255) // 客户地区
- },
- level: {
- type: DataTypes.STRING(255),
- defaultValue: 'company-sea-pools' // 客户等级,默认值为company-sea-pools
- },
- demand: {
- type: DataTypes.TEXT // 基本需求
- },
- spec: {
- type: DataTypes.TEXT // 规格
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'User',
- tableName: 'users',
- timestamps: false
-});
-
-// 商品模型
-class Product extends Model { }
-Product.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- sellerId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- price: {
- type: DataTypes.DECIMAL(10, 2),
- allowNull: false
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- status: {
- type: DataTypes.STRING(20),
- defaultValue: 'pending_review',
- validate: {
- isIn: [['pending_review', 'reviewed', 'published', 'sold_out', 'rejected', 'hidden']]
- }
- },
- rejectReason: {
- type: DataTypes.TEXT
- },
- // 新增预约相关字段
- reservedCount: {
- type: DataTypes.INTEGER,
- defaultValue: 0,
- allowNull: false,
- comment: '已有几人想要'
- },
- 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
-});
-
-// 购物车模型
-class CartItem extends Model { }
-CartItem.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 1
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- price: {
- type: DataTypes.DECIMAL(10, 2)
- },
- selected: {
- type: DataTypes.BOOLEAN,
- defaultValue: true
- },
- added_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'CartItem',
- tableName: 'cart_items',
- timestamps: false
-});
-
-// 联系人表模型
-class Contact extends Model { }
-Contact.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 联系人
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 手机号
- },
- wechat: {
- type: DataTypes.STRING(100) // 微信号
- },
- account: {
- type: DataTypes.STRING(100) // 账户
- },
- accountNumber: {
- type: DataTypes.STRING(100) // 账号
- },
- bank: {
- type: DataTypes.STRING(100) // 开户行
- },
- address: {
- type: DataTypes.TEXT // 地址
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'Contact',
- tableName: 'contacts',
- timestamps: false
-});
-
-// 用户管理表模型
-class UserManagement extends Model { }
-UserManagement.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- managerId: {
- type: DataTypes.STRING(100),
- defaultValue: null // 经理ID,默认值为null
- },
- company: {
- type: DataTypes.STRING(255),
- defaultValue: null // 公司,默认值为null
- },
- department: {
- type: DataTypes.STRING(255),
- defaultValue: null // 部门,默认值为null
- },
- organization: {
- type: DataTypes.STRING(255),
- defaultValue: null // 组织,默认值为null
- },
- role: {
- type: DataTypes.STRING(100),
- defaultValue: null // 角色,默认值为null
- },
- root: {
- type: DataTypes.STRING(100),
- defaultValue: null // 根节点,默认值为null
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'UserManagement',
- tableName: 'usermanagements',
- timestamps: false
-});
-
-// 定义模型之间的关联关系
-
-// 用户和商品的一对多关系 (卖家发布商品)
-User.hasMany(Product, {
- foreignKey: 'sellerId', // 外键字段名
- sourceKey: 'userId', // 源键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'products', // 别名,用于关联查询
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Product.belongsTo(User, {
- foreignKey: 'sellerId',
- targetKey: 'userId', // 目标键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'seller' // 别名,用于关联查询
-});
-
-// 用户和购物车项的一对多关系 (买家的购物需求/购物车)
-User.hasMany(CartItem, {
- foreignKey: 'userId',
- as: 'cartItems', // 用户的购物车(购物需求)列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(User, {
- foreignKey: 'userId',
- as: 'buyer' // 别名,明确表示这是购物需求的买家
-});
-
-// 商品和购物车项的一对多关系 (商品被添加到购物车)
-Product.hasMany(CartItem, {
- foreignKey: 'productId',
- as: 'cartItems', // 商品出现在哪些购物车中
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(Product, {
- foreignKey: 'productId',
- as: 'product' // 购物车项中的商品
-});
-
-// 用户和联系人的一对多关系
-User.hasMany(Contact, {
- foreignKey: 'userId',
- as: 'contacts', // 用户的联系人列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Contact.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 联系人所属用户
-});
-
-// 用户和用户管理的一对一关系
-User.hasOne(UserManagement, {
- foreignKey: 'userId',
- as: 'management', // 用户的管理信息
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-UserManagement.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 管理信息所属用户
-});
-
-// 同步数据库模型到MySQL
-async function syncDatabase() {
- try {
- // 不使用alter: true,避免尝试修改已有表结构导致的外键约束问题
- await sequelize.sync({
- force: false // 不强制重新创建表
- });
- console.log('数据库模型同步成功');
- } catch (error) {
- console.error('数据库模型同步失败:', error);
- // 即使同步失败也继续运行,因为我们只需要API功能
- console.log('数据库模型同步失败,但服务器继续运行,使用现有表结构');
- }
-}
-
-syncDatabase();
-
-// 解密微信加密数据
-function decryptData(encryptedData, sessionKey, iv) {
- try {
- // Base64解码
- const sessionKeyBuf = Buffer.from(sessionKey, 'base64');
- const encryptedDataBuf = Buffer.from(encryptedData, 'base64');
- const ivBuf = Buffer.from(iv, 'base64');
-
- // AES解密
- const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKeyBuf, ivBuf);
- decipher.setAutoPadding(true);
- let decoded = decipher.update(encryptedDataBuf, 'binary', 'utf8');
- decoded += decipher.final('utf8');
-
- // 解析JSON
- return JSON.parse(decoded);
- } catch (error) {
- console.error('解密失败:', error);
- // 提供更具体的错误信息
- if (error.code === 'ERR_OSSL_BAD_DECRYPT') {
- throw new Error('登录信息已过期,请重新登录');
- } else if (error.name === 'SyntaxError') {
- throw new Error('数据格式错误,解密结果无效');
- } else {
- throw new Error('解密失败,请重试');
- }
- }
-}
-
-// 获取微信session_key
-async function getSessionKey(code) {
- const axios = require('axios');
- const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${WECHAT_CONFIG.APPID}&secret=${WECHAT_CONFIG.APPSECRET}&js_code=${code}&grant_type=authorization_code`;
-
- try {
- const response = await axios.get(url);
- return response.data;
- } catch (error) {
- console.error('获取session_key失败:', error);
- throw new Error('获取session_key失败');
- }
-}
-
-// 创建用户关联记录函数 - 自动为用户创建contacts和usermanagements表的关联记录
-async function createUserAssociations(user) {
- try {
- if (!user || !user.userId) {
- console.error('无效的用户数据,无法创建关联记录');
- return false;
- }
-
- console.log('为用户创建关联记录:', user.userId);
-
- // 使用事务确保操作原子性
- await sequelize.transaction(async (transaction) => {
- // 1. 处理联系人记录 - 使用INSERT ... ON DUPLICATE KEY UPDATE确保无论如何都只保留一条记录
- await sequelize.query(
- `INSERT INTO contacts (userId, nickName, phoneNumber, created_at, updated_at)
- VALUES (?, ?, ?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- nickName = VALUES(nickName),
- phoneNumber = VALUES(phoneNumber),
- updated_at = NOW()`,
- {
- replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || ''],
- transaction: transaction
- }
- );
- console.log('联系人记录已处理(创建或更新):', user.userId);
-
- // 2. 处理用户管理记录 - 使用相同策略
- await sequelize.query(
- `INSERT INTO usermanagements (userId, created_at, updated_at)
- VALUES (?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- updated_at = NOW()`,
- {
- replacements: [user.userId],
- transaction: transaction
- }
- );
- console.log('用户管理记录已处理(创建或更新):', user.userId);
- });
-
- console.log('用户关联记录处理成功:', user.userId);
- return true;
- } catch (error) {
- console.error('创建用户关联记录失败:', error.message);
- return false;
- }
-}
-
-// API路由
-
-// 上传用户信息
-app.post('/api/user/upload', async (req, res) => {
- try {
- const userData = req.body;
- console.log('收到用户信息上传请求:', userData);
-
- // 如果用户信息中包含手机号,检查手机号是否已被其他用户使用
- if (userData.phoneNumber) {
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: userData.phoneNumber,
- openid: { [Sequelize.Op.ne]: userData.openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${userData.phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 创建新对象,移除手机号字段
- const userDataWithoutPhone = { ...userData };
- delete userDataWithoutPhone.phoneNumber;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息(不包含手机号)
- await User.update(
- {
- ...userDataWithoutPhone,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
- } else {
- // 创建新用户
- user = await User.create({
- ...userDataWithoutPhone,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- // 返回成功,但提示手机号已被使用
- return res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功,但手机号已被其他账号绑定',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: true
- });
- }
- }
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息
- await User.update(
- {
- ...userData,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- } else {
- // 创建新用户
- user = await User.create({
- ...userData,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: false
- });
- } catch (error) {
- console.error('保存用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '保存用户信息失败',
- error: error.message
- });
- }
-});
-
-// 解密手机号
-app.post('/api/user/decodePhone', async (req, res) => {
- try {
- const { encryptedData, iv, openid } = req.body;
-
- // 参数校验
- if (!encryptedData || !iv || !openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数'
- });
- }
-
- // 查找用户的session_key
- const user = await User.findOne({ where: { openid } });
-
- if (!user || !user.session_key) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录,请先登录',
- needRelogin: true
- });
- }
-
- // 解密手机号
- let decryptedData, phoneNumber;
- try {
- decryptedData = decryptData(encryptedData, user.session_key, iv);
- phoneNumber = decryptedData.phoneNumber;
- } catch (decryptError) {
- // 解密失败,可能是session_key过期,建议重新登录
- return res.status(401).json({
- success: false,
- code: 401,
- message: decryptError.message || '手机号解密失败',
- needRelogin: true
- });
- }
-
- // 检查手机号是否已被其他用户使用
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: phoneNumber,
- openid: { [Sequelize.Op.ne]: openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 返回成功,但不更新手机号,提示用户
- return res.json({
- success: true,
- code: 200,
- message: '手机号已被其他账号绑定',
- phoneNumber: user.phoneNumber, // 返回原手机号
- isNewPhone: false
- });
- }
-
- // 更新用户手机号
- await User.update(
- {
- phoneNumber: phoneNumber,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 更新用户手机号后,更新关联记录
- const updatedUser = await User.findOne({ where: { openid } });
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '手机号解密成功',
- phoneNumber: phoneNumber,
- isNewPhone: true
- });
- } catch (error) {
- console.error('手机号解密失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '手机号解密失败',
- error: error.message
- });
- }
-});
-
-// 处理微信登录,获取openid和session_key
-app.post('/api/wechat/getOpenid', async (req, res) => {
- try {
- const { code } = req.body;
-
- if (!code) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少code参数'
- });
- }
-
- // 获取openid和session_key
- const wxData = await getSessionKey(code);
-
- if (wxData.errcode) {
- throw new Error(`微信接口错误: ${wxData.errmsg}`);
- }
-
- const { openid, session_key, unionid } = wxData;
-
- // 生成userId
- const userId = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid }
- });
-
- if (user) {
- // 更新用户session_key
- await User.update(
- {
- session_key: session_key,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
- } else {
- // 创建新用户
- // 支持从客户端传入type参数,如果没有则默认为buyer
- const userType = req.body.type || 'buyer';
- await User.create({
- openid,
- userId,
- session_key,
- nickName: '微信用户', // 临时占位,等待用户授权
- type: userType, // 使用客户端传入的类型或默认买家身份
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 为新创建的用户创建关联记录
- const newUser = { userId, openid, nickName: '微信用户' };
- await createUserAssociations(newUser);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取openid成功',
- data: {
- openid,
- userId: user ? user.userId : userId
- }
- });
- } catch (error) {
- console.error('获取openid失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取openid失败',
- error: error.message
- });
- }
-});
-
-// 验证用户登录状态
-app.post('/api/user/validate', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'avatarUrl', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '验证成功',
- data: user
- });
- } catch (error) {
- console.error('验证用户登录状态失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '验证失败',
- error: error.message
- });
- }
-});
-
-// 获取用户信息
-app.post('/api/user/get', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- include: [
- {
- model: Contact,
- as: 'contacts',
- attributes: ['id', 'nickName', 'phoneNumber', 'wechat', 'account', 'accountNumber', 'bank', 'address']
- },
- {
- model: UserManagement,
- as: 'management',
- attributes: ['id', 'managerId', 'company', 'department', 'organization', 'role', 'root']
- }
- ]
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取用户信息成功',
- data: user
- });
- } catch (error) {
- console.error('获取用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户信息失败',
- error: error.message
- });
- }
-});
-
-// 更新用户信息
-app.post('/api/user/update', async (req, res) => {
- try {
- const { openid, ...updateData } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid }
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 更新用户信息
- await User.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 获取更新后的用户信息
- const updatedUser = await User.findOne({
- where: { openid }
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '更新用户信息成功',
- data: updatedUser
- });
- } catch (error) {
- console.error('更新用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新用户信息失败',
- error: error.message
- });
- }
-});
-
-// 获取商品列表 - 优化版本确保状态筛选正确应用
-app.post('/api/product/list', async (req, res) => {
- try {
- const { openid, status, keyword, page = 1, pageSize = 20, testMode = false } = req.body;
-
- // 验证openid参数(测试模式除外)
- if (!openid && !testMode) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 构建查询条件
- const where = {};
-
- // 查找用户
- let user = null;
- if (!testMode) {
- user = await User.findOne({ where: { openid } });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 只有管理员可以查看所有商品,普通用户只能查看自己的商品
- if (user.type !== 'admin') {
- where.sellerId = user.userId;
- }
- }
-
- // 状态筛选 - 直接构建到where对象中,确保不会丢失
- console.log(`当前用户类型: ${user ? user.type : '未知'},请求状态: ${status || '未指定'},测试模式: ${testMode}`);
-
- // 如果有指定status参数,按参数筛选但同时排除hidden
- if (status) {
- console.log(`按状态筛选商品: status=${status},并排除hidden状态`);
- if (status === 'all') {
- // 特殊情况:请求所有商品但仍然排除hidden
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else if (Array.isArray(status)) {
- // 如果status是数组,确保不包含hidden
- where.status = { [Sequelize.Op.in]: status.filter(s => s !== 'hidden') };
- } else {
- // 单个状态值,确保不是hidden
- if (status !== 'hidden') {
- where.status = { [Sequelize.Op.eq]: status };
- } else {
- // 如果明确请求hidden状态,也返回空结果
- where.status = { [Sequelize.Op.not]: 'hidden' };
- }
- }
- } else {
- // 没有指定status参数时 - 直接在where对象中设置状态筛选
- if (user && (user.type === 'seller' || user.type === 'both') && !testMode) {
- // 卖家用户且非测试模式
- console.log(`卖家用户 ${user.userId} (类型:${user.type}) 查看自己的所有商品,但排除hidden状态`);
- // 卖家可以查看自己的所有商品,但仍然排除hidden状态
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else {
- // 测试模式或非卖家用户
- console.log(`测试模式或非卖家用户,使用默认状态筛选: reviewed/published`);
- // 默认只显示已审核和已发布的商品,排除hidden和sold_out状态
- where.status = { [Sequelize.Op.in]: ['reviewed', 'published'] };
- }
- }
-
- console.log(`构建的完整查询条件:`, JSON.stringify(where, null, 2));
-
- // 关键词搜索
- if (keyword) {
- where.productName = { [Sequelize.Op.like]: `%${keyword}%` };
- }
-
- // 计算偏移量
- const offset = (page - 1) * pageSize;
-
- // 查询商品列表
- const { count, rows: products } = await Product.findAndCountAll({
- where,
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- },
- // 添加CartItem关联以获取预约人数
- {
- model: CartItem,
- as: 'cartItems', // 明确指定别名
- attributes: [],
- required: false // 允许没有购物车项的商品也能返回
- }
- ],
- // 添加selected字段,计算商品被加入购物车的次数(预约人数)
- attributes: {
- include: [
- [Sequelize.fn('COUNT', Sequelize.col('CartItems.id')), 'selected']
- ]
- },
- order: [['created_at', 'DESC']],
- limit: pageSize,
- offset,
- // 修复分组问题
- group: ['Product.productId', 'seller.userId'] // 使用正确的字段名
- });
-
- // 添加详细日志,记录查询结果
- console.log(`商品列表查询结果 - 商品数量: ${count}, 商品列表长度: ${products.length}`);
- if (products.length > 0) {
- console.log(`第一个商品数据:`, JSON.stringify(products[0], null, 2));
-
- // 添加selected字段的专门日志
- console.log('商品预约人数(selected字段)统计:');
- products.slice(0, 5).forEach(product => {
- const productJSON = product.toJSON();
- console.log(`- ${productJSON.productName}: 预约人数=${productJSON.selected || 0}, 商品ID=${productJSON.productId}`);
- });
- }
-
- // 处理商品列表中的grossWeight字段,确保是数字类型
- const processedProducts = products.map(product => {
- const productJSON = product.toJSON();
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productJSON.grossWeight,
- type: typeof productJSON.grossWeight,
- isEmpty: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined,
- isNumeric: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined || !isNaN(parseFloat(productJSON.grossWeight)) && isFinite(productJSON.grossWeight),
- parsedValue: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined ? 0 : parseFloat(productJSON.grossWeight)
- };
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- productJSON.grossWeight = finalGrossWeight;
-
- // 确保selected字段存在并设置为数字类型(修复后的代码)
- if ('selected' in productJSON) {
- // 确保selected是数字类型
- productJSON.selected = parseInt(productJSON.selected, 10);
- } else {
- // 如果没有selected字段,设置默认值为0
- productJSON.selected = 0;
- }
-
- // 记录第一个商品的转换信息用于调试
- if (products.indexOf(product) === 0) {
- console.log('商品列表 - 第一个商品毛重字段处理:');
- console.log('- 原始值:', grossWeightDetails.value, '类型:', grossWeightDetails.type);
- console.log('- 转换后的值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('- selected字段: 存在=', 'selected' in productJSON, '值=', productJSON.selected, '类型=', typeof productJSON.selected);
- }
-
- return productJSON;
- });;
-
- // 准备响应数据 - 修改格式以匹配前端期望
- const responseData = {
- success: true,
- code: 200,
- message: '获取商品列表成功',
- products: processedProducts,
- total: count,
- page: page,
- pageSize: pageSize,
- totalPages: Math.ceil(count / pageSize)
- };
-
- console.log(`准备返回的响应数据格式:`, JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
-
- // 添加详细的查询条件日志
- console.log(`最终查询条件:`, JSON.stringify(where, null, 2));
-
- res.json(responseData);
- } catch (error) {
- console.error('获取商品列表失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品列表失败',
- error: error.message
- });
- }
-});
-
-// 上传商品
-app.post('/api/products/upload', async (req, res) => {
- try {
- // 修复毛重字段处理逻辑
- let productData = req.body;
- if (productData && productData.productData) {
- productData = productData.productData; // 使用正确的productData对象
- }
-
- // 改进的毛重字段处理逻辑,与编辑API保持一致
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productData.grossWeight,
- type: typeof productData.grossWeight,
- isEmpty: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined,
- isNumeric: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined || !isNaN(parseFloat(productData.grossWeight)) && isFinite(productData.grossWeight),
- parsedValue: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined ? 0 : parseFloat(productData.grossWeight)
- };
-
- // 详细的日志记录
- console.log('上传商品 - 毛重字段详细分析:');
- console.log('- 原始值:', productData.grossWeight, '类型:', typeof productData.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行精确四舍五入,确保3位小数以上的值正确转换
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- productData.grossWeight = finalGrossWeight;
- console.log('上传商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('收到商品上传请求:', productData);
-
- // 验证必要字段
- if (!productData.sellerId || !productData.productName || !productData.price || !productData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的商品信息'
- });
- }
-
- // 检查sellerId是否为openid,如果是则查找对应的userId
- let actualSellerId = productData.sellerId;
-
- // 如果sellerId看起来像一个openid(包含特殊字符如'-'),则尝试查找对应的userId
- if (productData.sellerId.includes('-')) {
- console.log('sellerId看起来像openid,尝试查找对应的userId');
- const user = await User.findOne({
- where: {
- openid: productData.sellerId
- }
- });
-
- if (user && user.userId) {
- console.log(`找到了对应的userId: ${user.userId}`);
- actualSellerId = user.userId;
- } else {
- console.error(`未找到对应的用户记录,openid: ${productData.sellerId}`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '找不到对应的用户记录'
- });
- }
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 创建商品,使用实际的sellerId
- let product = await Product.create({
- ...productData,
- sellerId: actualSellerId, // 使用查找到的userId
- productId,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- product = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 与编辑API保持一致的处理逻辑
- if (product) {
- console.log('上传商品 - 处理前grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (product.grossWeight === undefined || product.grossWeight === null || product.grossWeight === '') {
- product.grossWeight = 0;
- console.log('上传商品 - 检测到空值,已设置为0');
- } else {
- // 否则转换为浮点数
- product.grossWeight = parseFloat(product.grossWeight);
- }
-
- console.log('上传商品 - 处理后grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品上传成功',
- data: {
- productId: product.productId,
- product: product
- }
- });
- } catch (error) {
- console.error('商品上传失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '商品上传失败',
- error: error.message
- });
- }
-});
-
-// 获取商品详情
-app.post('/api/products/detail', async (req, res) => {
- try {
- const { productId } = req.body;
-
- if (!productId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId参数'
- });
- }
-
- // 查询商品详情 - 排除hidden状态商品
- const product = await Product.findOne({
- where: {
- productId,
- status: { [Sequelize.Op.not]: 'hidden' }
- },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 对返回的商品数据中的grossWeight字段进行处理,确保是数字类型
- let updatedProduct = { ...product.toJSON() };
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: updatedProduct.grossWeight,
- type: typeof updatedProduct.grossWeight,
- isEmpty: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined,
- isNumeric: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined || !isNaN(parseFloat(updatedProduct.grossWeight)) && isFinite(updatedProduct.grossWeight),
- parsedValue: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined ? 0 : parseFloat(updatedProduct.grossWeight)
- };
-
- // 详细的日志记录
- console.log('商品详情 - 毛重字段详细分析:');
- console.log('- 原始值:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- updatedProduct.grossWeight = finalGrossWeight;
- console.log('商品详情 - 最终返回的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- res.json({
- success: true,
- code: 200,
- message: '获取商品详情成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('获取商品详情失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品详情失败',
- error: error.message
- });
- }
-});
-
-// 修改商品
-app.post('/api/products/edit', async (req, res) => {
- try {
- const { productId, ...updateData } = req.body;
- const { sellerId } = req.body;
-
- if (!productId || !sellerId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权修改此商品'
- });
- }
-
- // 更新商品信息
- await Product.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { productId }
- }
- );
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '修改商品成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('修改商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '修改商品失败',
- error: error.message
- });
- }
-});
-
-// 删除商品 - 将商品状态设置为hidden表示已删除
-app.post('/api/products/delete', async (req, res) => {
- console.log('收到删除商品请求:', req.body);
- try {
- const { productId, sellerId } = req.body;
-
- if (!productId || !sellerId) {
- console.error('删除商品失败: 缺少productId或sellerId参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- console.error('删除商品失败: 商品不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- console.error('删除商品失败: 权限不足 - 卖家ID不匹配', { expected: product.sellerId, actual: sellerId });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权删除此商品'
- });
- }
-
- console.log('准备更新商品状态为hidden,当前状态:', product.status);
-
- // 直接使用商品实例更新状态
- product.status = 'hidden';
- product.updated_at = new Date();
-
- try {
- // 先尝试保存商品实例
- await product.save();
- console.log('删除商品成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- // 如果保存失败,尝试使用update方法
- try {
- const updateResult = await Product.update(
- { status: 'hidden', updated_at: new Date() },
- { where: { productId } }
- );
- console.log('删除商品成功(使用update方法):', { productId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- // 如果update方法也失败,尝试直接执行SQL语句绕过ORM验证
- try {
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId',
- {
- replacements: {
- status: 'hidden',
- updatedAt: new Date(),
- productId: productId
- }
- }
- );
- console.log('删除商品成功(使用原始SQL):', { productId });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 从购物车中移除该商品
- const destroyResult = await CartItem.destroy({
- where: { productId }
- });
- console.log('从购物车移除商品结果:', destroyResult);
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除商品成功',
- product: {
- productId: updatedProduct.productId,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('删除商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除商品失败',
- error: error.message
- });
- }
-});
-
-// 添加商品到购物车
-app.post('/api/cart/add', async (req, res) => {
- // 增加全局错误捕获,确保即使在try-catch外部的错误也能被处理
- try {
- console.log('收到添加到购物车请求 - 开始处理', req.url);
- let cartData = req.body;
- console.log('收到添加到购物车请求数据:', cartData);
- console.log('请求头:', req.headers);
- console.log('请求IP:', req.ip);
-
- // 兼容客户端请求格式:客户端可能将数据封装在product对象中,并且使用openid而不是userId
- if (cartData.product && !cartData.productId) {
- // 从product对象中提取数据
- const productData = cartData.product;
- console.log('从product对象提取数据:', productData);
- console.log('客户端提供的openid:', cartData.openid);
-
- // 使用openid作为userId
- cartData = {
- userId: cartData.openid || productData.userId,
- productId: productData.productId || productData.id,
- productName: productData.productName || productData.name,
- quantity: productData.quantity || 1,
- price: productData.price,
- specification: productData.specification || productData.spec || '',
- grossWeight: productData.grossWeight || productData.weight,
- yolk: productData.yolk || productData.variety || '',
- testMode: productData.testMode || cartData.testMode
- };
- console.log('转换后的购物车数据:', cartData);
-
- // 检查转换后的userId是否存在于users表中
- try {
- console.log('开始查询用户信息,openid:', cartData.userId);
- const user = await User.findOne({
- where: { openid: cartData.userId }
- });
- if (user) {
- console.log(`找到对应的用户记录: openid=${cartData.userId}, userId=${user.userId}`);
- // 修正:使用数据库中真实的userId而不是openid
- cartData.userId = user.userId;
- console.log('修正后的userId:', cartData.userId);
- } else {
- console.error(`未找到openid为 ${cartData.userId} 的用户记录,无法添加到购物车`);
- // 重要:找不到用户时返回错误,避免使用无效的userId导致外键约束失败
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `未找到用户记录: ${cartData.userId}`
- });
- }
- } catch (error) {
- console.error('查询用户信息失败:', error);
- // 查询失败时也返回错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '查询用户信息失败',
- error: error.message
- });
- }
- }
-
- // 验证必要字段
- if (!cartData.userId || !cartData.productId || !cartData.productName || !cartData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的购物车信息',
- missingFields: [
- !cartData.userId ? 'userId' : '',
- !cartData.productId ? 'productId' : '',
- !cartData.productName ? 'productName' : '',
- !cartData.quantity ? 'quantity' : ''
- ].filter(Boolean)
- });
- }
-
- // 先验证用户ID是否存在于users表中
- try {
- const userExists = await User.findOne({
- where: { userId: cartData.userId }
- });
- if (!userExists) {
- console.error(`用户ID ${cartData.userId} 不存在于users表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `用户ID ${cartData.userId} 不存在`
- });
- } else {
- console.log(`用户ID ${cartData.userId} 存在于users表中,用户验证通过`);
- }
- } catch (error) {
- console.error('验证用户ID失败:', error);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '验证用户信息失败',
- error: error.message
- });
- }
-
- // 检查商品是否存在以及是否为hidden状态
- console.log(`检查商品ID: ${cartData.productId} 是否存在于products表中`);
- const product = await Product.findOne({
- where: {
- productId: cartData.productId
- }
- });
-
- if (!product) {
- console.error(`商品ID ${cartData.productId} 不存在于products表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '商品不存在或已被移除',
- error: `未找到商品ID: ${cartData.productId}`
- });
- } else {
- console.log(`商品ID ${cartData.productId} 存在于products表中,商品名称: ${product.productName}`);
- }
-
- if (product.status === 'hidden') {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '该商品已下架,无法添加到购物车'
- });
- }
-
- // 在testMode下,不执行实际的数据库操作,直接返回成功
- if (cartData.testMode) {
- console.log('测试模式:跳过实际的数据库操作');
- res.json({
- success: true,
- code: 200,
- message: '测试模式:添加到购物车成功',
- data: {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity
- }
- });
- return;
- }
-
- // 检查是否已存在相同商品
- const existingItem = await CartItem.findOne({
- where: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
-
- // 添加try-catch捕获外键约束错误
- try {
- console.log(`准备创建/更新购物车项: userId=${cartData.userId}, productId=${cartData.productId}`);
- if (existingItem) {
- // 已存在,更新数量
- await CartItem.update(
- {
- quantity: existingItem.quantity + cartData.quantity,
- updated_at: new Date()
- },
- {
- where: {
- id: existingItem.id
- }
- }
- );
- console.log(`更新购物车项成功: id=${existingItem.id}, 新数量=${existingItem.quantity + cartData.quantity}`);
- } else {
- // 不存在,创建新购物车项
- console.log('创建新购物车项,所有字段:', {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity,
- price: cartData.price,
- specification: cartData.specification,
- grossWeight: cartData.grossWeight,
- yolk: cartData.yolk
- });
- // 重要:在创建前再次验证数据完整性
- if (!cartData.userId || !cartData.productId) {
- throw new Error(`数据不完整: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- await CartItem.create({
- ...cartData,
- added_at: new Date()
- });
- console.log(`创建购物车项成功: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- } catch (createError) {
- console.error('创建/更新购物车项失败,可能是外键约束问题:', createError);
- console.error('详细错误信息:', {
- name: createError.name,
- message: createError.message,
- stack: createError.stack,
- sql: createError.sql || '无SQL信息',
- userId: cartData.userId,
- productId: cartData.productId
- });
-
- // 检测是否是外键约束错误
- if (createError.name === 'SequelizeForeignKeyConstraintError' || createError.message.includes('foreign key')) {
- // 区分是用户ID还是商品ID问题
- let errorField = 'productId';
- let errorMessage = '商品信息已更新,请刷新页面后重试';
-
- if (createError.message.includes('userId') || createError.message.includes('user') || createError.message.toLowerCase().includes('user')) {
- errorField = 'userId';
- errorMessage = '用户信息无效,请重新登录后重试';
- }
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: errorMessage,
- error: `外键约束错误: ${errorField} 不存在或已失效`,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 其他类型的错误也返回400状态码,避免500错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '添加购物车项失败,请稍后重试',
- error: createError.message,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 更新商品的预约人数 - 更健壮的实现
- try {
- console.log(`尝试更新商品预约人数: productId=${cartData.productId}`);
-
- // 先验证商品是否存在
- const productCheck = await Product.findOne({where: {productId: cartData.productId}});
- if (productCheck) {
- // 商品存在,才进行更新
- await Product.increment('reservedCount', {by: 1, where: {productId: cartData.productId}});
- console.log(`商品预约人数更新成功: productId=${cartData.productId}, 新数量=${productCheck.reservedCount + 1}`);
- } else {
- console.error(`更新商品预约人数失败: 商品ID ${cartData.productId} 不存在`);
- }
- } catch (updateError) {
- console.error(`更新商品预约人数失败:`, updateError);
- // 继续执行,不中断主要流程
- }
-
- res.json({
- success: true,
- code: 200,
- message: '添加到购物车成功'
- });
- } catch (error) {
- console.error('添加到购物车失败:', error);
- console.error('全局错误捕获,详细信息:', {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- });
-
- // 增强的错误处理 - 强制所有错误返回400状态码
- console.error('全局错误处理 - 捕获到未处理的错误:', error);
- const statusCode = 400; // 强制所有错误返回400状态码,避免前端显示500错误
- let errorMessage = '添加到购物车失败';
-
- // 更精确地检测外键约束错误
- if (error.name === 'SequelizeForeignKeyConstraintError' ||
- error.message.toLowerCase().includes('foreign key') ||
- error.message.toLowerCase().includes('constraint fails') ||
- error.message.toLowerCase().includes('constraint')) {
- errorMessage = '添加到购物车失败:商品或用户信息已更新,请刷新页面后重试';
- console.error('检测到外键约束相关错误,返回400状态码');
- }
-
- console.log(`准备返回错误响应 - 状态码: ${statusCode}, 消息: ${errorMessage}`);
-
- // 确保响应能够正确发送
- try {
- res.status(statusCode).json({
- success: false,
- code: statusCode,
- message: errorMessage,
- error: error.message,
- errorDetails: {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- }
- });
- } catch (resError) {
- console.error('发送错误响应失败:', resError);
- // 即使发送响应失败,也尝试以文本格式发送
- try {
- res.status(400).send('添加到购物车失败,请刷新页面后重试');
- } catch (finalError) {
- console.error('无法发送任何响应:', finalError);
- }
- }
- }
-});
-
-// 获取购物车信息
-app.post('/api/cart/get', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 查询购物车信息 - 排除关联商品为hidden或sold_out状态的项
- const cartItems = await CartItem.findAll({
- where: { userId },
- include: [
- {
- model: Product,
- as: 'product',
- attributes: ['productName', 'price', 'quantity', 'status', 'specification', 'grossWeight', 'yolk'],
- where: {
- status: { [Sequelize.Op.notIn]: ['hidden', 'sold_out'] }
- }
- }
- ],
- order: [['added_at', 'DESC']]
- });
-
- res.json({
- success: true,
- code: 200,
- message: '获取购物车信息成功',
- data: {
- cartItems
- }
- });
- } catch (error) {
- console.error('获取购物车信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取购物车信息失败',
- error: error.message
- });
- }
-});
-
-// 更新购物车项
-app.post('/api/cart/update', async (req, res) => {
- try {
- const { id, quantity, selected } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 构建更新数据
- const updateData = {};
- if (quantity !== undefined) updateData.quantity = quantity;
- if (selected !== undefined) updateData.selected = selected;
- updateData.updated_at = new Date();
-
- // 更新购物车项
- await CartItem.update(updateData, {
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '更新购物车成功'
- });
- } catch (error) {
- console.error('更新购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新购物车失败',
- error: error.message
- });
- }
-});
-
-// 删除购物车项
-app.post('/api/cart/delete', async (req, res) => {
- try {
- const { id } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 删除购物车项
- await CartItem.destroy({
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除购物车项成功'
- });
- } catch (error) {
- console.error('删除购物车项失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除购物车项失败',
- error: error.message
- });
- }
-});
-
-// 清空购物车
-app.post('/api/cart/clear', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 清空购物车
- await CartItem.destroy({
- where: { userId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '清空购物车成功'
- });
- } catch (error) {
- console.error('清空购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '清空购物车失败',
- error: error.message
- });
- }
-});
-
-// 测试连接接口
-app.get('/api/test-connection', async (req, res) => {
- try {
- // 检查数据库连接
- await sequelize.authenticate();
-
- res.json({
- success: true,
- code: 200,
- message: '服务器连接成功,数据库可用',
- timestamp: new Date().toISOString(),
- serverInfo: {
- port: PORT,
- nodeVersion: process.version,
- database: 'MySQL',
- status: 'running'
- }
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- code: 500,
- message: '服务器连接失败',
- error: error.message
- });
- }
-});
-
-// 用户类型调试接口 - 增强版:用于排查用户类型和商品显示问题
-app.post('/api/user/debug', async (req, res) => {
- try {
- const { openid } = req.body;
-
- console.log('收到用户调试请求,openid:', openid);
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查询用户信息
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在',
- debugInfo: {
- searchCriteria: { openid },
- timestamp: new Date().toISOString()
- }
- });
- }
-
- // 查询该用户的商品统计信息
- const totalProducts = await Product.count({ where: { sellerId: user.userId } });
- const pendingProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'pending_review'
- }
- });
- const reviewedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'reviewed'
- }
- });
- const publishedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'published'
- }
- });
- const soldOutProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'sold_out'
- }
- });
-
- // 判断用户是否有权限查看所有商品
- const canViewAllProducts = ['seller', 'both', 'admin'].includes(user.type);
-
- // 获取该用户的最新5个商品信息(用于调试)
- const latestProducts = await Product.findAll({
- where: { sellerId: user.userId },
- limit: 5,
- order: [['created_at', 'DESC']],
- attributes: ['productId', 'productName', 'status', 'created_at']
- });
-
- const responseData = {
- success: true,
- code: 200,
- message: '获取用户调试信息成功',
- userInfo: user,
- productStats: {
- total: totalProducts,
- pendingReview: pendingProducts,
- reviewed: reviewedProducts,
- published: publishedProducts,
- soldOut: soldOutProducts
- },
- permissionInfo: {
- canViewAllProducts: canViewAllProducts,
- userType: user.type,
- allowedTypesForViewingAllProducts: ['seller', 'both', 'admin']
- },
- latestProducts: latestProducts,
- debugInfo: {
- userCount: await User.count(),
- totalProductsInSystem: await Product.count(),
- timestamp: new Date().toISOString(),
- serverTime: new Date().toLocaleString('zh-CN')
- }
- };
-
- console.log('调试信息返回数据:', JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
- res.json(responseData);
- } catch (error) {
- console.error('获取用户调试信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户调试信息失败',
- error: error.message,
- debugInfo: {
- errorStack: error.stack,
- timestamp: new Date().toISOString()
- }
- });
- }
-});
-
-// 下架商品接口 - 将商品状态设置为sold_out表示已下架
-app.post('/api/product/hide', async (req, res) => {
- console.log('收到下架商品请求:', req.body);
-
- try {
- const { openid, productId } = req.body;
-
- // 验证请求参数
- if (!openid || !productId) {
- console.error('下架商品失败: 缺少必要参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要参数: openid和productId都是必需的'
- });
- }
-
- // 查找用户
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('下架商品失败: 用户不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- console.log('找到用户信息:', { userId: user.userId, nickName: user.nickName });
-
- // 查找商品并验证所有权 - 直接使用userId,因为商品创建时使用的就是userId
- const product = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
-
- if (!product) {
- console.error('下架商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 记录当前状态,用于调试
- console.log('当前商品状态:', product.status, '允许的状态列表:', Product.rawAttributes.status.validate.isIn);
- console.log('商品所属卖家ID:', product.sellerId);
- console.log('用户ID信息对比:', { userId: user.userId, id: user.id });
-
- console.log('准备更新商品状态为sold_out,当前状态:', product.status);
-
- // 更新商品状态为已下架(sold_out) - 尝试多种更新方式确保成功
- try {
- // 方法1: 直接保存实例
- product.status = 'sold_out';
- product.updated_at = new Date();
- await product.save();
- console.log('商品下架成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- try {
- // 方法2: 使用update方法
- const updateResult = await Product.update(
- { status: 'sold_out', updated_at: new Date() },
- { where: { productId: productId, sellerId: user.userId } }
- );
- console.log('商品下架成功(使用update方法):', { productId: productId, sellerIdType: typeof user.userId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- try {
- // 方法3: 直接执行SQL语句绕过ORM验证
- const replacements = {
- status: 'sold_out',
- updatedAt: new Date(),
- productId: productId,
- sellerId: user.userId
- };
-
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId AND sellerId = :sellerId',
- {
- replacements: replacements
- }
- );
- console.log('商品下架成功(使用原始SQL):', { productId: product.productId, productName: product.productName });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: product.sellerId // 使用找到的商品的sellerId进行查询
- }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '商品下架成功',
- product: {
- productId: updatedProduct.productId,
- productName: updatedProduct.productName,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('下架商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '下架商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 发布商品API
-app.post('/api/product/publish', async (req, res) => {
- console.log('收到发布商品请求:', req.body); // 记录完整请求体
-
- try {
- const { openid, product } = req.body;
-
- // 验证必填字段
- console.log('验证请求参数: openid=', !!openid, ', product=', !!product);
- if (!openid || !product) {
- console.error('缺少必要参数: openid=', openid, 'product=', product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid或product对象)'
- });
- }
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price, '转换为数字=', parseFloat(product.price));
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity, '转换为数字=', parseInt(product.quantity));
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误和字段值详情
- const validationErrors = [];
- const fieldDetails = {};
-
- // 检查商品名称
- fieldDetails.productName = {
- value: product.productName,
- type: typeof product.productName,
- isEmpty: !product.productName || product.productName.trim() === ''
- };
- if (fieldDetails.productName.isEmpty) {
- console.error('商品名称为空');
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- }
-
- // 检查价格
- fieldDetails.price = {
- value: product.price,
- type: typeof product.price,
- isNumber: !isNaN(parseFloat(product.price)) && isFinite(product.price),
- parsedValue: parseFloat(product.price),
- isValid: !isNaN(parseFloat(product.price)) && isFinite(product.price) && parseFloat(product.price) > 0
- };
- if (!product.price) {
- console.error('价格为空');
- validationErrors.push('价格为必填项');
- } else if (!fieldDetails.price.isNumber) {
- console.error('价格不是有效数字: price=', product.price);
- validationErrors.push('价格必须是有效数字格式');
- } else if (fieldDetails.price.parsedValue <= 0) {
- console.error('价格小于等于0: price=', product.price, '转换为数字后=', fieldDetails.price.parsedValue);
- validationErrors.push('价格必须大于0');
- }
-
- // 检查数量
- fieldDetails.quantity = {
- value: product.quantity,
- type: typeof product.quantity,
- isNumeric: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity),
- parsedValue: Math.floor(parseFloat(product.quantity)),
- isValid: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity) && parseFloat(product.quantity) > 0
- };
- if (!product.quantity) {
- console.error('数量为空');
- validationErrors.push('数量为必填项');
- } else if (!fieldDetails.quantity.isNumeric) {
- console.error('数量不是有效数字: quantity=', product.quantity);
- validationErrors.push('数量必须是有效数字格式');
- } else if (fieldDetails.quantity.parsedValue <= 0) {
- console.error('数量小于等于0: quantity=', product.quantity, '转换为数字后=', fieldDetails.quantity.parsedValue);
- validationErrors.push('数量必须大于0');
- }
-
- // 改进的毛重字段处理逻辑 - 与其他API保持一致
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('发布商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保商品名称不超过数据库字段长度限制
- if (product.productName && product.productName.length > 255) {
- console.error('商品名称过长: 长度=', product.productName.length);
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 如果有验证错误,一次性返回所有错误信息和字段详情
- if (validationErrors.length > 0) {
- console.error('验证失败 - 详细信息:', JSON.stringify({
- errors: validationErrors,
- fieldDetails: fieldDetails
- }, null, 2));
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors,
- detailedMessage: validationErrors.join('; '),
- fieldDetails: fieldDetails
- });
- }
-
- // 查找用户
- console.log('开始查找用户: openid=', openid);
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- console.log('找到用户:', { userId: user.userId, nickName: user.nickName, type: user.type });
-
- // 验证用户类型
- console.log(`验证用户类型: 用户ID=${user.userId}, 类型=${user.type}`);
- if (user.type !== 'seller' && user.type !== 'both') {
- console.error(`商品发布失败: 用户${user.userId}类型为${user.type},需要seller或both类型`);
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有卖家才能发布商品,请在个人资料中修改用户类型'
- });
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
- console.log('生成商品ID:', productId);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行正确的四舍五入
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- console.log('发布商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 创建商品
- console.log('准备创建商品:', {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight,
- sellerId: user.userId
- });
-
- const newProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk || '',
- specification: product.specification || '',
- status: 'pending_review', // 默认状态为待审核
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- const createdProduct = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- if (createdProduct) {
- console.log('发布商品 - 数据库查询后grossWeight:', createdProduct.grossWeight, '类型:', typeof createdProduct.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品发布成功',
- product: createdProduct,
- productId: productId
- });
-
- } catch (error) {
- console.error('发布商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '发布商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 启动服务器
-app.listen(PORT, () => {
- console.log(`服务器运行在 http://localhost:${PORT}`);
- console.log('注意:当前服务器已添加详细日志记录,用于排查发布商品问题');
- console.log('调试API: POST /api/user/debug - 用于查看用户类型信息');
- console.log(`测试连接接口: http://localhost:${PORT}/api/test-connection`);
-});
-
-// 编辑商品API - 用于审核失败商品重新编辑
-app.post('/api/product/edit', async (req, res) => {
- console.log('收到编辑商品请求 - 详细信息:');
- console.log('- 请求路径:', req.url);
- console.log('- 请求方法:', req.method);
- console.log('- 请求完整body:', req.body);
- console.log('- 服务器端口:', PORT);
- console.log('- 环境变量:', process.env.PORT);
-
- try {
- // 正确解析请求参数,处理嵌套的productData结构
- let openid = req.body.openid;
- let productId = req.body.productId;
- let status = req.body.status;
- let testMode = req.body.testMode;
- let product = req.body.product;
-
- // 处理多层嵌套的productData结构
- if (!product && req.body.productData) {
- // 处理第一种情况: { productData: { openid, productId, product: { ... } } }
- if (req.body.productData.product) {
- product = req.body.productData.product;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- // 处理第二种情况: { productData: { openid, productId, productName, price, ... } }
- else {
- product = req.body.productData;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- }
-
- // 调试日志
- console.log('解析后参数:', { openid, productId, status, testMode, product: !!product });
-
- console.log('收到编辑商品请求,包含状态参数:', { openid, productId, status, testMode });
-
- // 验证必填字段
- if (!openid || !productId || !product) {
- console.error('缺少必要参数: openid=', !!openid, 'productId=', !!productId, 'product=', !!product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid、productId或product对象)'
- });
- }
-
- // 查找用户
- let user = null;
-
- // 测试模式下的特殊处理
- if (testMode) {
- console.log('测试模式:尝试查找或创建测试用户');
- // 首先尝试查找openid为'test_openid'的用户
- user = await User.findOne({
- where: { openid: 'test_openid' }
- });
-
- if (!user) {
- // 如果不存在,创建一个新的测试用户
- console.log('测试模式:创建测试用户');
- try {
- user = await User.create({
- openid: 'test_openid',
- userId: 'test_user_id',
- nickName: '测试用户',
- type: 'seller'
- });
- } catch (createError) {
- console.error('测试模式:创建测试用户失败', createError);
- // 如果创建失败,尝试查找数据库中的第一个用户
- user = await User.findOne({
- order: [['id', 'ASC']]
- });
- if (user) {
- console.log('测试模式:使用数据库中的现有用户', user.userId);
- }
- }
- } else {
- console.log('测试模式:使用已存在的测试用户', user.userId);
- }
- } else {
- // 非测试模式:按常规方式查找用户
- user = await User.findOne({ where: { openid } });
- }
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- // 查找商品
- let existingProduct = null;
-
- if (testMode) {
- // 测试模式:如果找不到商品,尝试使用测试商品或创建一个新的测试商品
- existingProduct = await Product.findOne({
- where: {
- productId: productId
- }
- });
-
- // 如果找不到指定的商品,创建一个新的测试商品
- if (!existingProduct) {
- console.log('测试模式:创建测试商品');
- try {
- existingProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: '测试商品',
- price: 99.99,
- quantity: 100,
- grossWeight: 0, // 默认为0而不是5,符合用户需求
- yolk: '测试描述',
- specification: '测试规格',
- status: 'rejected', // 设置为可编辑状态
- created_at: new Date(),
- updated_at: new Date()
- });
- console.log('测试模式:测试商品创建成功');
- } catch (createProductError) {
- console.error('测试模式:创建测试商品失败', createProductError);
- }
- }
- } else {
- // 非测试模式:验证商品所有权
- existingProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
- }
-
- if (!existingProduct) {
- console.error('编辑商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 验证商品状态是否允许编辑
- if (!['rejected', 'sold_out', 'pending_review', 'reviewed'].includes(existingProduct.status)) {
- console.error(`编辑商品失败: 商品状态(${existingProduct.status})不允许编辑`, {
- productId: productId,
- sellerId: user.userId,
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有审核失败、已下架、审核中或已审核的商品才能编辑',
- debugInfo: {
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- }
- });
- }
-
- // 记录商品编辑信息,用于调试
- console.log(`允许编辑商品: productId=${productId}, status=${existingProduct.status}, sellerId=${user.userId}`);
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price);
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity);
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误
- const validationErrors = [];
-
- // 检查商品名称
- if (!product.productName || product.productName.trim() === '') {
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- } else if (product.productName.length > 255) {
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 检查价格
- if (!product.price) {
- validationErrors.push('价格为必填项');
- } else if (isNaN(parseFloat(product.price)) || parseFloat(product.price) <= 0) {
- validationErrors.push('价格必须是大于0的有效数字');
- }
-
- // 检查数量
- if (!product.quantity) {
- validationErrors.push('数量为必填项');
- } else if (isNaN(parseInt(product.quantity)) || parseInt(product.quantity) <= 0) {
- validationErrors.push('数量必须是大于0的有效数字');
- }
-
- // 改进的毛重字段处理逻辑,与其他API保持一致,空值默认设为0
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('编辑商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保grossWeight值是数字类型
- const finalGrossWeight = Number(grossWeightDetails.parsedValue);
- console.log('编辑商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 如果有验证错误,返回错误信息
- if (validationErrors.length > 0) {
- console.error('验证失败 - 错误:', validationErrors.join('; '));
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors
- });
- }
-
- // 准备更新的商品数据
- const updatedProductData = {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk,
- specification: product.specification,
- // 优先使用前端传递的status参数,如果没有传递则使用原来的逻辑
- status: status && ['pending_review', 'published'].includes(status) ? status :
- (product.resubmit && ['rejected', 'sold_out'].includes(existingProduct.status)) ? 'pending_review' : existingProduct.status,
- rejectReason: (status === 'pending_review' || (product.resubmit && existingProduct.status === 'rejected')) ? null : existingProduct.rejectReason, // 提交审核时清除拒绝原因
- updated_at: new Date()
- };
-
- console.log('准备更新商品数据:', { productId, updatedStatus: updatedProductData.status, fromStatus: existingProduct.status });
-
- // 更新商品
- const [updatedCount] = await Product.update(updatedProductData, {
- where: testMode ? {
- // 测试模式:只根据productId更新
- productId: productId
- } : {
- // 非测试模式:验证商品所有权
- productId: productId,
- sellerId: user.userId
- }
- });
-
- // 检查更新是否成功
- if (updatedCount === 0) {
- console.error('商品更新失败: 没有找到匹配的商品或权限不足');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品更新失败: 没有找到匹配的商品或权限不足'
- });
- }
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({ where: { productId: productId } });
-
- console.log('查询数据库后 - 更新的商品信息:', {
- grossWeight: updatedProduct?.grossWeight,
- grossWeightType: typeof updatedProduct?.grossWeight,
- productId: updatedProduct?.productId,
- status: updatedProduct?.status
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 注意:这里检查undefined和null,并且对于空字符串或5的情况也进行处理
- if (updatedProduct) {
- console.log('处理前 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (updatedProduct.grossWeight === undefined || updatedProduct.grossWeight === null || updatedProduct.grossWeight === '') {
- updatedProduct.grossWeight = 0;
- console.log('检测到空值 - 已设置为0');
- } else {
- // 否则转换为浮点数
- updatedProduct.grossWeight = parseFloat(updatedProduct.grossWeight);
- }
-
- console.log('处理后 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- }
-
- console.log('商品编辑成功:', {
- productId: productId,
- productName: product.productName,
- oldStatus: existingProduct.status, // 记录更新前的状态
- newStatus: updatedProduct.status, // 记录更新后的状态
- grossWeight: updatedProduct.grossWeight // 记录处理后的毛重值
- });
-
- // 根据新的状态生成适当的返回消息
- let returnMessage = '';
- if (updatedProduct.status === 'pending_review') {
- returnMessage = '商品编辑成功,已重新提交审核';
- } else if (updatedProduct.status === 'published') {
- returnMessage = '商品编辑成功,已上架';
- } else if (updatedProduct.status === existingProduct.status) {
- returnMessage = '商品编辑成功,状态保持不变';
- } else {
- returnMessage = '商品编辑成功';
- }
-
- res.json({
- success: true,
- code: 200,
- message: returnMessage,
- product: updatedProduct
- });
- } catch (error) {
- console.error('编辑商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '编辑商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 导出模型和Express应用供其他模块使用
-module.exports = {
- User,
- Product,
- CartItem,
- sequelize,
- createUserAssociations,
- app,
- PORT
-};
\ No newline at end of file
diff --git a/server-example/server-mysql-backup-final.js b/server-example/server-mysql-backup-final.js
deleted file mode 100644
index 4398050..0000000
--- a/server-example/server-mysql-backup-final.js
+++ /dev/null
@@ -1,3001 +0,0 @@
-// ECS服务器示例代码 - Node.js版 (MySQL版本)
-const express = require('express');
-const crypto = require('crypto');
-const bodyParser = require('body-parser');
-const { Sequelize, DataTypes, Model, Op } = require('sequelize');
-require('dotenv').config();
-
-// 创建Express应用
-const app = express();
-const PORT = process.env.PORT || 3002;
-
-// 中间件
-app.use(bodyParser.json());
-
-// 添加请求日志中间件,捕获所有到达服务器的请求(必须放在bodyParser之后)
-app.use((req, res, next) => {
- // 将UTC时间转换为北京时间(UTC+8)
- const now = new Date();
- const beijingTime = new Date(now.getTime() + 8 * 60 * 60 * 1000);
- const formattedTime = beijingTime.toISOString().replace('Z', '+08:00');
-
- console.log(`[${formattedTime}] 收到请求: ${req.method} ${req.url}`);
- console.log('请求头:', req.headers);
- console.log('请求体:', req.body);
- next();
-});
-
-// 商品毛重处理中间件 - 确保所有返回的商品数据中毛重字段保持原始值
-app.use((req, res, next) => {
- // 保存原始的json方法
- const originalJson = res.json;
-
- // 重写json方法来处理响应数据
- res.json = function (data) {
- // 检查数据中是否包含商品列表
- if (data && typeof data === 'object') {
- // 处理/products/list接口的响应
- if (data.products && Array.isArray(data.products)) {
- data.products = data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
-
- // 处理/data字段中的商品列表
- if (data.data && data.data.products && Array.isArray(data.data.products)) {
- data.data.products = data.data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
- }
-
- // 调用原始的json方法
- return originalJson.call(this, data);
- };
-
- next();
-});
-
-// 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
- }
- }
-);
-
-// 微信小程序配置
-const WECHAT_CONFIG = {
- APPID: process.env.WECHAT_APPID || 'your-wechat-appid',
- APPSECRET: process.env.WECHAT_APPSECRET || 'your-wechat-appsecret',
- TOKEN: process.env.WECHAT_TOKEN || 'your-wechat-token'
-};
-
-// 显示当前使用的数据库配置(用于调试)
-console.log('当前数据库连接配置:');
-console.log(' 主机:', process.env.DB_HOST || 'localhost');
-console.log(' 端口:', process.env.DB_PORT || 3306);
-console.log(' 数据库名:', process.env.DB_DATABASE || 'wechat_app');
-console.log(' 用户名:', process.env.DB_USER || 'root');
-console.log(' 密码:', process.env.DB_PASSWORD === undefined || process.env.DB_PASSWORD === '' ? '无密码' : '******');
-
-// 测试数据库连接
-async function testDbConnection() {
- try {
- await sequelize.authenticate();
- console.log('数据库连接成功');
- } catch (error) {
- console.error('数据库连接失败:', error);
- console.error('\n请检查以下几点:');
- console.error('1. MySQL服务是否已经启动');
- console.error('2. wechat_app数据库是否已创建');
- console.error('3. .env文件中的数据库用户名和密码是否正确');
- console.error('4. 用户名是否有足够的权限访问数据库');
- console.error('\n如果是首次配置,请参考README文件中的数据库设置指南。');
- process.exit(1);
- }
-}
-
-testDbConnection();
-
-// 定义数据模型
-
-// 用户模型
-class User extends Model { }
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- openid: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 微信名,必填
- },
- avatarUrl: {
- type: DataTypes.TEXT
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 电话号码,必填
- },
- type: {
- type: DataTypes.STRING(20),
- allowNull: false // 用户身份(buyer/seller/both),必填
- },
- gender: {
- type: DataTypes.INTEGER
- },
- country: {
- type: DataTypes.STRING(50)
- },
- province: {
- type: DataTypes.STRING(50)
- },
- city: {
- type: DataTypes.STRING(50)
- },
- language: {
- type: DataTypes.STRING(20)
- },
- session_key: {
- type: DataTypes.STRING(255)
- },
- // 新增字段
- company: {
- type: DataTypes.STRING(255) // 客户公司
- },
- region: {
- type: DataTypes.STRING(255) // 客户地区
- },
- level: {
- type: DataTypes.STRING(255),
- defaultValue: 'company-sea-pools' // 客户等级,默认值为company-sea-pools
- },
- demand: {
- type: DataTypes.TEXT // 基本需求
- },
- spec: {
- type: DataTypes.TEXT // 规格
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'User',
- tableName: 'users',
- timestamps: false
-});
-
-// 商品模型
-class Product extends Model { }
-Product.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- sellerId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- price: {
- type: DataTypes.DECIMAL(10, 2),
- allowNull: false
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- status: {
- type: DataTypes.STRING(20),
- defaultValue: 'pending_review',
- validate: {
- isIn: [['pending_review', 'reviewed', 'published', 'sold_out', 'rejected', 'hidden']]
- }
- },
- rejectReason: {
- type: DataTypes.TEXT
- },
- // 新增预约相关字段
- reservedCount: {
- type: DataTypes.INTEGER,
- defaultValue: 0,
- allowNull: false,
- comment: '已有几人想要'
- },
- 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
-});
-
-// 购物车模型
-class CartItem extends Model { }
-CartItem.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 1
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- price: {
- type: DataTypes.DECIMAL(10, 2)
- },
- selected: {
- type: DataTypes.BOOLEAN,
- defaultValue: true
- },
- added_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'CartItem',
- tableName: 'cart_items',
- timestamps: false
-});
-
-// 联系人表模型
-class Contact extends Model { }
-Contact.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 联系人
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 手机号
- },
- wechat: {
- type: DataTypes.STRING(100) // 微信号
- },
- account: {
- type: DataTypes.STRING(100) // 账户
- },
- accountNumber: {
- type: DataTypes.STRING(100) // 账号
- },
- bank: {
- type: DataTypes.STRING(100) // 开户行
- },
- address: {
- type: DataTypes.TEXT // 地址
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'Contact',
- tableName: 'contacts',
- timestamps: false
-});
-
-// 用户管理表模型
-class UserManagement extends Model { }
-UserManagement.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- managerId: {
- type: DataTypes.STRING(100),
- defaultValue: null // 经理ID,默认值为null
- },
- company: {
- type: DataTypes.STRING(255),
- defaultValue: null // 公司,默认值为null
- },
- department: {
- type: DataTypes.STRING(255),
- defaultValue: null // 部门,默认值为null
- },
- organization: {
- type: DataTypes.STRING(255),
- defaultValue: null // 组织,默认值为null
- },
- role: {
- type: DataTypes.STRING(100),
- defaultValue: null // 角色,默认值为null
- },
- root: {
- type: DataTypes.STRING(100),
- defaultValue: null // 根节点,默认值为null
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'UserManagement',
- tableName: 'usermanagements',
- timestamps: false
-});
-
-// 定义模型之间的关联关系
-
-// 用户和商品的一对多关系 (卖家发布商品)
-User.hasMany(Product, {
- foreignKey: 'sellerId', // 外键字段名
- sourceKey: 'userId', // 源键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'products', // 别名,用于关联查询
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Product.belongsTo(User, {
- foreignKey: 'sellerId',
- targetKey: 'userId', // 目标键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'seller' // 别名,用于关联查询
-});
-
-// 用户和购物车项的一对多关系 (买家的购物需求/购物车)
-User.hasMany(CartItem, {
- foreignKey: 'userId',
- as: 'cartItems', // 用户的购物车(购物需求)列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(User, {
- foreignKey: 'userId',
- as: 'buyer' // 别名,明确表示这是购物需求的买家
-});
-
-// 商品和购物车项的一对多关系 (商品被添加到购物车)
-Product.hasMany(CartItem, {
- foreignKey: 'productId',
- as: 'cartItems', // 商品出现在哪些购物车中
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(Product, {
- foreignKey: 'productId',
- as: 'product' // 购物车项中的商品
-});
-
-// 用户和联系人的一对多关系
-User.hasMany(Contact, {
- foreignKey: 'userId',
- as: 'contacts', // 用户的联系人列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Contact.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 联系人所属用户
-});
-
-// 用户和用户管理的一对一关系
-User.hasOne(UserManagement, {
- foreignKey: 'userId',
- as: 'management', // 用户的管理信息
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-UserManagement.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 管理信息所属用户
-});
-
-// 同步数据库模型到MySQL
-async function syncDatabase() {
- try {
- // 不使用alter: true,避免尝试修改已有表结构导致的外键约束问题
- await sequelize.sync({
- force: false // 不强制重新创建表
- });
- console.log('数据库模型同步成功');
- } catch (error) {
- console.error('数据库模型同步失败:', error);
- // 即使同步失败也继续运行,因为我们只需要API功能
- console.log('数据库模型同步失败,但服务器继续运行,使用现有表结构');
- }
-}
-
-syncDatabase();
-
-// 解密微信加密数据
-function decryptData(encryptedData, sessionKey, iv) {
- try {
- // Base64解码
- const sessionKeyBuf = Buffer.from(sessionKey, 'base64');
- const encryptedDataBuf = Buffer.from(encryptedData, 'base64');
- const ivBuf = Buffer.from(iv, 'base64');
-
- // AES解密
- const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKeyBuf, ivBuf);
- decipher.setAutoPadding(true);
- let decoded = decipher.update(encryptedDataBuf, 'binary', 'utf8');
- decoded += decipher.final('utf8');
-
- // 解析JSON
- return JSON.parse(decoded);
- } catch (error) {
- console.error('解密失败:', error);
- // 提供更具体的错误信息
- if (error.code === 'ERR_OSSL_BAD_DECRYPT') {
- throw new Error('登录信息已过期,请重新登录');
- } else if (error.name === 'SyntaxError') {
- throw new Error('数据格式错误,解密结果无效');
- } else {
- throw new Error('解密失败,请重试');
- }
- }
-}
-
-// 获取微信session_key
-async function getSessionKey(code) {
- const axios = require('axios');
- const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${WECHAT_CONFIG.APPID}&secret=${WECHAT_CONFIG.APPSECRET}&js_code=${code}&grant_type=authorization_code`;
-
- try {
- const response = await axios.get(url);
- return response.data;
- } catch (error) {
- console.error('获取session_key失败:', error);
- throw new Error('获取session_key失败');
- }
-}
-
-// 创建用户关联记录函数 - 自动为用户创建contacts和usermanagements表的关联记录
-async function createUserAssociations(user) {
- try {
- if (!user || !user.userId) {
- console.error('无效的用户数据,无法创建关联记录');
- return false;
- }
-
- console.log('为用户创建关联记录:', user.userId);
-
- // 使用事务确保操作原子性
- await sequelize.transaction(async (transaction) => {
- // 1. 处理联系人记录 - 使用INSERT ... ON DUPLICATE KEY UPDATE确保无论如何都只保留一条记录
- await sequelize.query(
- `INSERT INTO contacts (userId, nickName, phoneNumber, created_at, updated_at)
- VALUES (?, ?, ?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- nickName = VALUES(nickName),
- phoneNumber = VALUES(phoneNumber),
- updated_at = NOW()`,
- {
- replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || ''],
- transaction: transaction
- }
- );
- console.log('联系人记录已处理(创建或更新):', user.userId);
-
- // 2. 处理用户管理记录 - 使用相同策略
- await sequelize.query(
- `INSERT INTO usermanagements (userId, created_at, updated_at)
- VALUES (?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- updated_at = NOW()`,
- {
- replacements: [user.userId],
- transaction: transaction
- }
- );
- console.log('用户管理记录已处理(创建或更新):', user.userId);
- });
-
- console.log('用户关联记录处理成功:', user.userId);
- return true;
- } catch (error) {
- console.error('创建用户关联记录失败:', error.message);
- return false;
- }
-}
-
-// API路由
-
-// 上传用户信息
-app.post('/api/user/upload', async (req, res) => {
- try {
- const userData = req.body;
- console.log('收到用户信息上传请求:', userData);
-
- // 如果用户信息中包含手机号,检查手机号是否已被其他用户使用
- if (userData.phoneNumber) {
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: userData.phoneNumber,
- openid: { [Sequelize.Op.ne]: userData.openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${userData.phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 创建新对象,移除手机号字段
- const userDataWithoutPhone = { ...userData };
- delete userDataWithoutPhone.phoneNumber;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息(不包含手机号)
- await User.update(
- {
- ...userDataWithoutPhone,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
- } else {
- // 创建新用户
- user = await User.create({
- ...userDataWithoutPhone,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- // 返回成功,但提示手机号已被使用
- return res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功,但手机号已被其他账号绑定',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: true
- });
- }
- }
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息
- await User.update(
- {
- ...userData,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- } else {
- // 创建新用户
- user = await User.create({
- ...userData,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: false
- });
- } catch (error) {
- console.error('保存用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '保存用户信息失败',
- error: error.message
- });
- }
-});
-
-// 解密手机号
-app.post('/api/user/decodePhone', async (req, res) => {
- try {
- const { encryptedData, iv, openid } = req.body;
-
- // 参数校验
- if (!encryptedData || !iv || !openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数'
- });
- }
-
- // 查找用户的session_key
- const user = await User.findOne({ where: { openid } });
-
- if (!user || !user.session_key) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录,请先登录',
- needRelogin: true
- });
- }
-
- // 解密手机号
- let decryptedData, phoneNumber;
- try {
- decryptedData = decryptData(encryptedData, user.session_key, iv);
- phoneNumber = decryptedData.phoneNumber;
- } catch (decryptError) {
- // 解密失败,可能是session_key过期,建议重新登录
- return res.status(401).json({
- success: false,
- code: 401,
- message: decryptError.message || '手机号解密失败',
- needRelogin: true
- });
- }
-
- // 检查手机号是否已被其他用户使用
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: phoneNumber,
- openid: { [Sequelize.Op.ne]: openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 返回成功,但不更新手机号,提示用户
- return res.json({
- success: true,
- code: 200,
- message: '手机号已被其他账号绑定',
- phoneNumber: user.phoneNumber, // 返回原手机号
- isNewPhone: false
- });
- }
-
- // 更新用户手机号
- await User.update(
- {
- phoneNumber: phoneNumber,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 更新用户手机号后,更新关联记录
- const updatedUser = await User.findOne({ where: { openid } });
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '手机号解密成功',
- phoneNumber: phoneNumber,
- isNewPhone: true
- });
- } catch (error) {
- console.error('手机号解密失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '手机号解密失败',
- error: error.message
- });
- }
-});
-
-// 处理微信登录,获取openid和session_key
-app.post('/api/wechat/getOpenid', async (req, res) => {
- try {
- const { code } = req.body;
-
- if (!code) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少code参数'
- });
- }
-
- // 获取openid和session_key
- const wxData = await getSessionKey(code);
-
- if (wxData.errcode) {
- throw new Error(`微信接口错误: ${wxData.errmsg}`);
- }
-
- const { openid, session_key, unionid } = wxData;
-
- // 生成userId
- const userId = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid }
- });
-
- if (user) {
- // 更新用户session_key
- await User.update(
- {
- session_key: session_key,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
- } else {
- // 创建新用户
- // 支持从客户端传入type参数,如果没有则默认为buyer
- const userType = req.body.type || 'buyer';
- await User.create({
- openid,
- userId,
- session_key,
- nickName: '微信用户', // 临时占位,等待用户授权
- type: userType, // 使用客户端传入的类型或默认买家身份
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 为新创建的用户创建关联记录
- const newUser = { userId, openid, nickName: '微信用户' };
- await createUserAssociations(newUser);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取openid成功',
- data: {
- openid,
- userId: user ? user.userId : userId
- }
- });
- } catch (error) {
- console.error('获取openid失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取openid失败',
- error: error.message
- });
- }
-});
-
-// 验证用户登录状态
-app.post('/api/user/validate', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'avatarUrl', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '验证成功',
- data: user
- });
- } catch (error) {
- console.error('验证用户登录状态失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '验证失败',
- error: error.message
- });
- }
-});
-
-// 获取用户信息
-app.post('/api/user/get', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- include: [
- {
- model: Contact,
- as: 'contacts',
- attributes: ['id', 'nickName', 'phoneNumber', 'wechat', 'account', 'accountNumber', 'bank', 'address']
- },
- {
- model: UserManagement,
- as: 'management',
- attributes: ['id', 'managerId', 'company', 'department', 'organization', 'role', 'root']
- }
- ]
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取用户信息成功',
- data: user
- });
- } catch (error) {
- console.error('获取用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户信息失败',
- error: error.message
- });
- }
-});
-
-// 更新用户信息
-app.post('/api/user/update', async (req, res) => {
- try {
- const { openid, ...updateData } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid }
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 更新用户信息
- await User.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 获取更新后的用户信息
- const updatedUser = await User.findOne({
- where: { openid }
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '更新用户信息成功',
- data: updatedUser
- });
- } catch (error) {
- console.error('更新用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新用户信息失败',
- error: error.message
- });
- }
-});
-
-// 获取商品列表 - 优化版本确保状态筛选正确应用
-app.post('/api/product/list', async (req, res) => {
- try {
- const { openid, status, keyword, page = 1, pageSize = 20, testMode = false } = req.body;
-
- // 验证openid参数(测试模式除外)
- if (!openid && !testMode) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 构建查询条件
- const where = {};
-
- // 查找用户
- let user = null;
- if (!testMode) {
- user = await User.findOne({ where: { openid } });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 只有管理员可以查看所有商品,普通用户只能查看自己的商品
- if (user.type !== 'admin') {
- where.sellerId = user.userId;
- }
- }
-
- // 状态筛选 - 直接构建到where对象中,确保不会丢失
- console.log(`当前用户类型: ${user ? user.type : '未知'},请求状态: ${status || '未指定'},测试模式: ${testMode}`);
-
- // 如果有指定status参数,按参数筛选但同时排除hidden
- if (status) {
- console.log(`按状态筛选商品: status=${status},并排除hidden状态`);
- if (status === 'all') {
- // 特殊情况:请求所有商品但仍然排除hidden
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else if (Array.isArray(status)) {
- // 如果status是数组,确保不包含hidden
- where.status = { [Sequelize.Op.in]: status.filter(s => s !== 'hidden') };
- } else {
- // 单个状态值,确保不是hidden
- if (status !== 'hidden') {
- where.status = { [Sequelize.Op.eq]: status };
- } else {
- // 如果明确请求hidden状态,也返回空结果
- where.status = { [Sequelize.Op.not]: 'hidden' };
- }
- }
- } else {
- // 没有指定status参数时 - 直接在where对象中设置状态筛选
- if (user && (user.type === 'seller' || user.type === 'both') && !testMode) {
- // 卖家用户且非测试模式
- console.log(`卖家用户 ${user.userId} (类型:${user.type}) 查看自己的所有商品,但排除hidden状态`);
- // 卖家可以查看自己的所有商品,但仍然排除hidden状态
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else {
- // 测试模式或非卖家用户
- console.log(`测试模式或非卖家用户,使用默认状态筛选: reviewed/published`);
- // 默认只显示已审核和已发布的商品,排除hidden和sold_out状态
- where.status = { [Sequelize.Op.in]: ['reviewed', 'published'] };
- }
- }
-
- console.log(`构建的完整查询条件:`, JSON.stringify(where, null, 2));
-
- // 关键词搜索
- if (keyword) {
- where.productName = { [Sequelize.Op.like]: `%${keyword}%` };
- }
-
- // 计算偏移量
- const offset = (page - 1) * pageSize;
-
- // 查询商品列表
- const { count, rows: products } = await Product.findAndCountAll({
- where,
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- },
- // 添加CartItem关联以获取预约人数
- {
- model: CartItem,
- as: 'cartItems', // 明确指定别名
- attributes: [],
- required: false // 允许没有购物车项的商品也能返回
- }
- ],
- // 添加selected字段,计算商品被加入购物车的次数(预约人数)
- attributes: {
- include: [
- [Sequelize.fn('COUNT', Sequelize.col('cartItems.id')), 'selected']
- ]
- },
- order: [['created_at', 'DESC']],
- limit: pageSize,
- offset,
- // 修复分组问题
- group: ['Product.productId', 'seller.userId'] // 使用正确的字段名
- });
-
- // 添加详细日志,记录查询结果
- console.log(`商品列表查询结果 - 商品数量: ${count}, 商品列表长度: ${products.length}`);
- if (products.length > 0) {
- console.log(`第一个商品数据:`, JSON.stringify(products[0], null, 2));
-
- // 添加selected字段的专门日志
- console.log('商品预约人数(selected字段)统计:');
- products.slice(0, 5).forEach(product => {
- const productJSON = product.toJSON();
- console.log(`- ${productJSON.productName}: 预约人数=${productJSON.selected || 0}, 商品ID=${productJSON.productId}`);
- });
- }
-
- // 处理商品列表中的grossWeight字段,确保是数字类型
- const processedProducts = products.map(product => {
- const productJSON = product.toJSON();
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productJSON.grossWeight,
- type: typeof productJSON.grossWeight,
- isEmpty: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined,
- isNumeric: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined || !isNaN(parseFloat(productJSON.grossWeight)) && isFinite(productJSON.grossWeight),
- parsedValue: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined ? 0 : parseFloat(productJSON.grossWeight)
- };
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- productJSON.grossWeight = finalGrossWeight;
-
- // 确保selected字段存在并设置为数字类型(修复后的代码)
- if ('selected' in productJSON) {
- // 确保selected是数字类型
- productJSON.selected = parseInt(productJSON.selected, 10);
- } else {
- // 如果没有selected字段,设置默认值为0
- productJSON.selected = 0;
- }
-
- // 记录第一个商品的转换信息用于调试
- if (products.indexOf(product) === 0) {
- console.log('商品列表 - 第一个商品毛重字段处理:');
- console.log('- 原始值:', grossWeightDetails.value, '类型:', grossWeightDetails.type);
- console.log('- 转换后的值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('- selected字段: 存在=', 'selected' in productJSON, '值=', productJSON.selected, '类型=', typeof productJSON.selected);
- }
-
- return productJSON;
- });;
-
- // 准备响应数据 - 修改格式以匹配前端期望
- const responseData = {
- success: true,
- code: 200,
- message: '获取商品列表成功',
- products: processedProducts,
- total: count,
- page: page,
- pageSize: pageSize,
- totalPages: Math.ceil(count / pageSize)
- };
-
- console.log(`准备返回的响应数据格式:`, JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
-
- // 添加详细的查询条件日志
- console.log(`最终查询条件:`, JSON.stringify(where, null, 2));
-
- res.json(responseData);
- } catch (error) {
- console.error('获取商品列表失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品列表失败',
- error: error.message
- });
- }
-});
-
-// 上传商品
-app.post('/api/products/upload', async (req, res) => {
- try {
- // 修复毛重字段处理逻辑
- let productData = req.body;
- if (productData && productData.productData) {
- productData = productData.productData; // 使用正确的productData对象
- }
-
- // 改进的毛重字段处理逻辑,与编辑API保持一致
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productData.grossWeight,
- type: typeof productData.grossWeight,
- isEmpty: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined,
- isNumeric: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined || !isNaN(parseFloat(productData.grossWeight)) && isFinite(productData.grossWeight),
- parsedValue: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined ? 0 : parseFloat(productData.grossWeight)
- };
-
- // 详细的日志记录
- console.log('上传商品 - 毛重字段详细分析:');
- console.log('- 原始值:', productData.grossWeight, '类型:', typeof productData.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行精确四舍五入,确保3位小数以上的值正确转换
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- productData.grossWeight = finalGrossWeight;
- console.log('上传商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('收到商品上传请求:', productData);
-
- // 验证必要字段
- if (!productData.sellerId || !productData.productName || !productData.price || !productData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的商品信息'
- });
- }
-
- // 检查sellerId是否为openid,如果是则查找对应的userId
- let actualSellerId = productData.sellerId;
-
- // 如果sellerId看起来像一个openid(包含特殊字符如'-'),则尝试查找对应的userId
- if (productData.sellerId.includes('-')) {
- console.log('sellerId看起来像openid,尝试查找对应的userId');
- const user = await User.findOne({
- where: {
- openid: productData.sellerId
- }
- });
-
- if (user && user.userId) {
- console.log(`找到了对应的userId: ${user.userId}`);
- actualSellerId = user.userId;
- } else {
- console.error(`未找到对应的用户记录,openid: ${productData.sellerId}`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '找不到对应的用户记录'
- });
- }
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 创建商品,使用实际的sellerId
- let product = await Product.create({
- ...productData,
- sellerId: actualSellerId, // 使用查找到的userId
- productId,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- product = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 与编辑API保持一致的处理逻辑
- if (product) {
- console.log('上传商品 - 处理前grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (product.grossWeight === undefined || product.grossWeight === null || product.grossWeight === '') {
- product.grossWeight = 0;
- console.log('上传商品 - 检测到空值,已设置为0');
- } else {
- // 否则转换为浮点数
- product.grossWeight = parseFloat(product.grossWeight);
- }
-
- console.log('上传商品 - 处理后grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品上传成功',
- data: {
- productId: product.productId,
- product: product
- }
- });
- } catch (error) {
- console.error('商品上传失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '商品上传失败',
- error: error.message
- });
- }
-});
-
-// 获取商品详情
-app.post('/api/products/detail', async (req, res) => {
- try {
- const { productId } = req.body;
-
- if (!productId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId参数'
- });
- }
-
- // 查询商品详情 - 排除hidden状态商品
- const product = await Product.findOne({
- where: {
- productId,
- status: { [Sequelize.Op.not]: 'hidden' }
- },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 对返回的商品数据中的grossWeight字段进行处理,确保是数字类型
- let updatedProduct = { ...product.toJSON() };
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: updatedProduct.grossWeight,
- type: typeof updatedProduct.grossWeight,
- isEmpty: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined,
- isNumeric: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined || !isNaN(parseFloat(updatedProduct.grossWeight)) && isFinite(updatedProduct.grossWeight),
- parsedValue: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined ? 0 : parseFloat(updatedProduct.grossWeight)
- };
-
- // 详细的日志记录
- console.log('商品详情 - 毛重字段详细分析:');
- console.log('- 原始值:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- updatedProduct.grossWeight = finalGrossWeight;
- console.log('商品详情 - 最终返回的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- res.json({
- success: true,
- code: 200,
- message: '获取商品详情成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('获取商品详情失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品详情失败',
- error: error.message
- });
- }
-});
-
-// 修改商品
-app.post('/api/products/edit', async (req, res) => {
- try {
- const { productId, ...updateData } = req.body;
- const { sellerId } = req.body;
-
- if (!productId || !sellerId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权修改此商品'
- });
- }
-
- // 更新商品信息
- await Product.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { productId }
- }
- );
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '修改商品成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('修改商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '修改商品失败',
- error: error.message
- });
- }
-});
-
-// 删除商品 - 将商品状态设置为hidden表示已删除
-app.post('/api/products/delete', async (req, res) => {
- console.log('收到删除商品请求:', req.body);
- try {
- const { productId, sellerId } = req.body;
-
- if (!productId || !sellerId) {
- console.error('删除商品失败: 缺少productId或sellerId参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- console.error('删除商品失败: 商品不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- console.error('删除商品失败: 权限不足 - 卖家ID不匹配', { expected: product.sellerId, actual: sellerId });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权删除此商品'
- });
- }
-
- console.log('准备更新商品状态为hidden,当前状态:', product.status);
-
- // 直接使用商品实例更新状态
- product.status = 'hidden';
- product.updated_at = new Date();
-
- try {
- // 先尝试保存商品实例
- await product.save();
- console.log('删除商品成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- // 如果保存失败,尝试使用update方法
- try {
- const updateResult = await Product.update(
- { status: 'hidden', updated_at: new Date() },
- { where: { productId } }
- );
- console.log('删除商品成功(使用update方法):', { productId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- // 如果update方法也失败,尝试直接执行SQL语句绕过ORM验证
- try {
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId',
- {
- replacements: {
- status: 'hidden',
- updatedAt: new Date(),
- productId: productId
- }
- }
- );
- console.log('删除商品成功(使用原始SQL):', { productId });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 从购物车中移除该商品
- const destroyResult = await CartItem.destroy({
- where: { productId }
- });
- console.log('从购物车移除商品结果:', destroyResult);
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除商品成功',
- product: {
- productId: updatedProduct.productId,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('删除商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除商品失败',
- error: error.message
- });
- }
-});
-
-// 添加商品到购物车
-app.post('/api/cart/add', async (req, res) => {
- // 增加全局错误捕获,确保即使在try-catch外部的错误也能被处理
- try {
- console.log('收到添加到购物车请求 - 开始处理', req.url);
- let cartData = req.body;
- console.log('收到添加到购物车请求数据:', cartData);
- console.log('请求头:', req.headers);
- console.log('请求IP:', req.ip);
-
- // 兼容客户端请求格式:客户端可能将数据封装在product对象中,并且使用openid而不是userId
- if (cartData.product && !cartData.productId) {
- // 从product对象中提取数据
- const productData = cartData.product;
- console.log('从product对象提取数据:', productData);
- console.log('客户端提供的openid:', cartData.openid);
-
- // 使用openid作为userId
- cartData = {
- userId: cartData.openid || productData.userId,
- productId: productData.productId || productData.id,
- productName: productData.productName || productData.name,
- quantity: productData.quantity || 1,
- price: productData.price,
- specification: productData.specification || productData.spec || '',
- grossWeight: productData.grossWeight || productData.weight,
- yolk: productData.yolk || productData.variety || '',
- testMode: productData.testMode || cartData.testMode
- };
- console.log('转换后的购物车数据:', cartData);
-
- // 检查转换后的userId是否存在于users表中
- try {
- console.log('开始查询用户信息,openid:', cartData.userId);
- const user = await User.findOne({
- where: { openid: cartData.userId }
- });
- if (user) {
- console.log(`找到对应的用户记录: openid=${cartData.userId}, userId=${user.userId}`);
- // 修正:使用数据库中真实的userId而不是openid
- cartData.userId = user.userId;
- console.log('修正后的userId:', cartData.userId);
- } else {
- console.error(`未找到openid为 ${cartData.userId} 的用户记录,无法添加到购物车`);
- // 重要:找不到用户时返回错误,避免使用无效的userId导致外键约束失败
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `未找到用户记录: ${cartData.userId}`
- });
- }
- } catch (error) {
- console.error('查询用户信息失败:', error);
- // 查询失败时也返回错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '查询用户信息失败',
- error: error.message
- });
- }
- }
-
- // 验证必要字段
- if (!cartData.userId || !cartData.productId || !cartData.productName || !cartData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的购物车信息',
- missingFields: [
- !cartData.userId ? 'userId' : '',
- !cartData.productId ? 'productId' : '',
- !cartData.productName ? 'productName' : '',
- !cartData.quantity ? 'quantity' : ''
- ].filter(Boolean)
- });
- }
-
- // 先验证用户ID是否存在于users表中
- try {
- const userExists = await User.findOne({
- where: { userId: cartData.userId }
- });
- if (!userExists) {
- console.error(`用户ID ${cartData.userId} 不存在于users表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `用户ID ${cartData.userId} 不存在`
- });
- } else {
- console.log(`用户ID ${cartData.userId} 存在于users表中,用户验证通过`);
- }
- } catch (error) {
- console.error('验证用户ID失败:', error);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '验证用户信息失败',
- error: error.message
- });
- }
-
- // 检查商品是否存在以及是否为hidden状态
- console.log(`检查商品ID: ${cartData.productId} 是否存在于products表中`);
- const product = await Product.findOne({
- where: {
- productId: cartData.productId
- }
- });
-
- if (!product) {
- console.error(`商品ID ${cartData.productId} 不存在于products表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '商品不存在或已被移除',
- error: `未找到商品ID: ${cartData.productId}`
- });
- } else {
- console.log(`商品ID ${cartData.productId} 存在于products表中,商品名称: ${product.productName}`);
- }
-
- if (product.status === 'hidden') {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '该商品已下架,无法添加到购物车'
- });
- }
-
- // 在testMode下,不执行实际的数据库操作,直接返回成功
- if (cartData.testMode) {
- console.log('测试模式:跳过实际的数据库操作');
- res.json({
- success: true,
- code: 200,
- message: '测试模式:添加到购物车成功',
- data: {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity
- }
- });
- return;
- }
-
- // 检查是否已存在相同商品
- const existingItem = await CartItem.findOne({
- where: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
-
- // 添加try-catch捕获外键约束错误
- try {
- console.log(`准备创建/更新购物车项: userId=${cartData.userId}, productId=${cartData.productId}`);
- if (existingItem) {
- // 已存在,更新数量
- await CartItem.update(
- {
- quantity: existingItem.quantity + cartData.quantity,
- updated_at: new Date()
- },
- {
- where: {
- id: existingItem.id
- }
- }
- );
- console.log(`更新购物车项成功: id=${existingItem.id}, 新数量=${existingItem.quantity + cartData.quantity}`);
- } else {
- // 不存在,创建新购物车项
- console.log('创建新购物车项,所有字段:', {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity,
- price: cartData.price,
- specification: cartData.specification,
- grossWeight: cartData.grossWeight,
- yolk: cartData.yolk
- });
- // 重要:在创建前再次验证数据完整性
- if (!cartData.userId || !cartData.productId) {
- throw new Error(`数据不完整: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- await CartItem.create({
- ...cartData,
- added_at: new Date()
- });
- console.log(`创建购物车项成功: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- } catch (createError) {
- console.error('创建/更新购物车项失败,可能是外键约束问题:', createError);
- console.error('详细错误信息:', {
- name: createError.name,
- message: createError.message,
- stack: createError.stack,
- sql: createError.sql || '无SQL信息',
- userId: cartData.userId,
- productId: cartData.productId
- });
-
- // 检测是否是外键约束错误
- if (createError.name === 'SequelizeForeignKeyConstraintError' || createError.message.includes('foreign key')) {
- // 区分是用户ID还是商品ID问题
- let errorField = 'productId';
- let errorMessage = '商品信息已更新,请刷新页面后重试';
-
- if (createError.message.includes('userId') || createError.message.includes('user') || createError.message.toLowerCase().includes('user')) {
- errorField = 'userId';
- errorMessage = '用户信息无效,请重新登录后重试';
- }
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: errorMessage,
- error: `外键约束错误: ${errorField} 不存在或已失效`,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 其他类型的错误也返回400状态码,避免500错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '添加购物车项失败,请稍后重试',
- error: createError.message,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 更新商品的预约人数 - 更健壮的实现
- try {
- console.log(`尝试更新商品预约人数: productId=${cartData.productId}`);
-
- // 先验证商品是否存在
- const productCheck = await Product.findOne({where: {productId: cartData.productId}});
- if (productCheck) {
- // 商品存在,才进行更新
- await Product.increment('reservedCount', {by: 1, where: {productId: cartData.productId}});
- console.log(`商品预约人数更新成功: productId=${cartData.productId}, 新数量=${productCheck.reservedCount + 1}`);
- } else {
- console.error(`更新商品预约人数失败: 商品ID ${cartData.productId} 不存在`);
- }
- } catch (updateError) {
- console.error(`更新商品预约人数失败:`, updateError);
- // 继续执行,不中断主要流程
- }
-
- res.json({
- success: true,
- code: 200,
- message: '添加到购物车成功'
- });
- } catch (error) {
- console.error('添加到购物车失败:', error);
- console.error('全局错误捕获,详细信息:', {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- });
-
- // 增强的错误处理 - 强制所有错误返回400状态码
- console.error('全局错误处理 - 捕获到未处理的错误:', error);
- const statusCode = 400; // 强制所有错误返回400状态码,避免前端显示500错误
- let errorMessage = '添加到购物车失败';
-
- // 更精确地检测外键约束错误
- if (error.name === 'SequelizeForeignKeyConstraintError' ||
- error.message.toLowerCase().includes('foreign key') ||
- error.message.toLowerCase().includes('constraint fails') ||
- error.message.toLowerCase().includes('constraint')) {
- errorMessage = '添加到购物车失败:商品或用户信息已更新,请刷新页面后重试';
- console.error('检测到外键约束相关错误,返回400状态码');
- }
-
- console.log(`准备返回错误响应 - 状态码: ${statusCode}, 消息: ${errorMessage}`);
-
- // 确保响应能够正确发送
- try {
- res.status(statusCode).json({
- success: false,
- code: statusCode,
- message: errorMessage,
- error: error.message,
- errorDetails: {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- }
- });
- } catch (resError) {
- console.error('发送错误响应失败:', resError);
- // 即使发送响应失败,也尝试以文本格式发送
- try {
- res.status(400).send('添加到购物车失败,请刷新页面后重试');
- } catch (finalError) {
- console.error('无法发送任何响应:', finalError);
- }
- }
- }
-});
-
-// 获取购物车信息
-app.post('/api/cart/get', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 查询购物车信息 - 排除关联商品为hidden或sold_out状态的项
- const cartItems = await CartItem.findAll({
- where: { userId },
- include: [
- {
- model: Product,
- as: 'product',
- attributes: ['productName', 'price', 'quantity', 'status', 'specification', 'grossWeight', 'yolk'],
- where: {
- status: { [Sequelize.Op.notIn]: ['hidden', 'sold_out'] }
- }
- }
- ],
- order: [['added_at', 'DESC']]
- });
-
- res.json({
- success: true,
- code: 200,
- message: '获取购物车信息成功',
- data: {
- cartItems
- }
- });
- } catch (error) {
- console.error('获取购物车信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取购物车信息失败',
- error: error.message
- });
- }
-});
-
-// 更新购物车项
-app.post('/api/cart/update', async (req, res) => {
- try {
- const { id, quantity, selected } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 构建更新数据
- const updateData = {};
- if (quantity !== undefined) updateData.quantity = quantity;
- if (selected !== undefined) updateData.selected = selected;
- updateData.updated_at = new Date();
-
- // 更新购物车项
- await CartItem.update(updateData, {
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '更新购物车成功'
- });
- } catch (error) {
- console.error('更新购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新购物车失败',
- error: error.message
- });
- }
-});
-
-// 删除购物车项
-app.post('/api/cart/delete', async (req, res) => {
- try {
- const { id } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 删除购物车项
- await CartItem.destroy({
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除购物车项成功'
- });
- } catch (error) {
- console.error('删除购物车项失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除购物车项失败',
- error: error.message
- });
- }
-});
-
-// 清空购物车
-app.post('/api/cart/clear', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 清空购物车
- await CartItem.destroy({
- where: { userId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '清空购物车成功'
- });
- } catch (error) {
- console.error('清空购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '清空购物车失败',
- error: error.message
- });
- }
-});
-
-// 测试连接接口
-app.get('/api/test-connection', async (req, res) => {
- try {
- // 检查数据库连接
- await sequelize.authenticate();
-
- res.json({
- success: true,
- code: 200,
- message: '服务器连接成功,数据库可用',
- timestamp: new Date().toISOString(),
- serverInfo: {
- port: PORT,
- nodeVersion: process.version,
- database: 'MySQL',
- status: 'running'
- }
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- code: 500,
- message: '服务器连接失败',
- error: error.message
- });
- }
-});
-
-// 用户类型调试接口 - 增强版:用于排查用户类型和商品显示问题
-app.post('/api/user/debug', async (req, res) => {
- try {
- const { openid } = req.body;
-
- console.log('收到用户调试请求,openid:', openid);
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查询用户信息
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在',
- debugInfo: {
- searchCriteria: { openid },
- timestamp: new Date().toISOString()
- }
- });
- }
-
- // 查询该用户的商品统计信息
- const totalProducts = await Product.count({ where: { sellerId: user.userId } });
- const pendingProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'pending_review'
- }
- });
- const reviewedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'reviewed'
- }
- });
- const publishedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'published'
- }
- });
- const soldOutProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'sold_out'
- }
- });
-
- // 判断用户是否有权限查看所有商品
- const canViewAllProducts = ['seller', 'both', 'admin'].includes(user.type);
-
- // 获取该用户的最新5个商品信息(用于调试)
- const latestProducts = await Product.findAll({
- where: { sellerId: user.userId },
- limit: 5,
- order: [['created_at', 'DESC']],
- attributes: ['productId', 'productName', 'status', 'created_at']
- });
-
- const responseData = {
- success: true,
- code: 200,
- message: '获取用户调试信息成功',
- userInfo: user,
- productStats: {
- total: totalProducts,
- pendingReview: pendingProducts,
- reviewed: reviewedProducts,
- published: publishedProducts,
- soldOut: soldOutProducts
- },
- permissionInfo: {
- canViewAllProducts: canViewAllProducts,
- userType: user.type,
- allowedTypesForViewingAllProducts: ['seller', 'both', 'admin']
- },
- latestProducts: latestProducts,
- debugInfo: {
- userCount: await User.count(),
- totalProductsInSystem: await Product.count(),
- timestamp: new Date().toISOString(),
- serverTime: new Date().toLocaleString('zh-CN')
- }
- };
-
- console.log('调试信息返回数据:', JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
- res.json(responseData);
- } catch (error) {
- console.error('获取用户调试信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户调试信息失败',
- error: error.message,
- debugInfo: {
- errorStack: error.stack,
- timestamp: new Date().toISOString()
- }
- });
- }
-});
-
-// 下架商品接口 - 将商品状态设置为sold_out表示已下架
-app.post('/api/product/hide', async (req, res) => {
- console.log('收到下架商品请求:', req.body);
-
- try {
- const { openid, productId } = req.body;
-
- // 验证请求参数
- if (!openid || !productId) {
- console.error('下架商品失败: 缺少必要参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要参数: openid和productId都是必需的'
- });
- }
-
- // 查找用户
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('下架商品失败: 用户不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- console.log('找到用户信息:', { userId: user.userId, nickName: user.nickName });
-
- // 查找商品并验证所有权 - 直接使用userId,因为商品创建时使用的就是userId
- const product = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
-
- if (!product) {
- console.error('下架商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 记录当前状态,用于调试
- console.log('当前商品状态:', product.status, '允许的状态列表:', Product.rawAttributes.status.validate.isIn);
- console.log('商品所属卖家ID:', product.sellerId);
- console.log('用户ID信息对比:', { userId: user.userId, id: user.id });
-
- console.log('准备更新商品状态为sold_out,当前状态:', product.status);
-
- // 更新商品状态为已下架(sold_out) - 尝试多种更新方式确保成功
- try {
- // 方法1: 直接保存实例
- product.status = 'sold_out';
- product.updated_at = new Date();
- await product.save();
- console.log('商品下架成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- try {
- // 方法2: 使用update方法
- const updateResult = await Product.update(
- { status: 'sold_out', updated_at: new Date() },
- { where: { productId: productId, sellerId: user.userId } }
- );
- console.log('商品下架成功(使用update方法):', { productId: productId, sellerIdType: typeof user.userId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- try {
- // 方法3: 直接执行SQL语句绕过ORM验证
- const replacements = {
- status: 'sold_out',
- updatedAt: new Date(),
- productId: productId,
- sellerId: user.userId
- };
-
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId AND sellerId = :sellerId',
- {
- replacements: replacements
- }
- );
- console.log('商品下架成功(使用原始SQL):', { productId: product.productId, productName: product.productName });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: product.sellerId // 使用找到的商品的sellerId进行查询
- }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '商品下架成功',
- product: {
- productId: updatedProduct.productId,
- productName: updatedProduct.productName,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('下架商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '下架商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 发布商品API
-app.post('/api/product/publish', async (req, res) => {
- console.log('收到发布商品请求:', req.body); // 记录完整请求体
-
- try {
- const { openid, product } = req.body;
-
- // 验证必填字段
- console.log('验证请求参数: openid=', !!openid, ', product=', !!product);
- if (!openid || !product) {
- console.error('缺少必要参数: openid=', openid, 'product=', product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid或product对象)'
- });
- }
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price, '转换为数字=', parseFloat(product.price));
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity, '转换为数字=', parseInt(product.quantity));
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误和字段值详情
- const validationErrors = [];
- const fieldDetails = {};
-
- // 检查商品名称
- fieldDetails.productName = {
- value: product.productName,
- type: typeof product.productName,
- isEmpty: !product.productName || product.productName.trim() === ''
- };
- if (fieldDetails.productName.isEmpty) {
- console.error('商品名称为空');
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- }
-
- // 检查价格
- fieldDetails.price = {
- value: product.price,
- type: typeof product.price,
- isNumber: !isNaN(parseFloat(product.price)) && isFinite(product.price),
- parsedValue: parseFloat(product.price),
- isValid: !isNaN(parseFloat(product.price)) && isFinite(product.price) && parseFloat(product.price) > 0
- };
- if (!product.price) {
- console.error('价格为空');
- validationErrors.push('价格为必填项');
- } else if (!fieldDetails.price.isNumber) {
- console.error('价格不是有效数字: price=', product.price);
- validationErrors.push('价格必须是有效数字格式');
- } else if (fieldDetails.price.parsedValue <= 0) {
- console.error('价格小于等于0: price=', product.price, '转换为数字后=', fieldDetails.price.parsedValue);
- validationErrors.push('价格必须大于0');
- }
-
- // 检查数量
- fieldDetails.quantity = {
- value: product.quantity,
- type: typeof product.quantity,
- isNumeric: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity),
- parsedValue: Math.floor(parseFloat(product.quantity)),
- isValid: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity) && parseFloat(product.quantity) > 0
- };
- if (!product.quantity) {
- console.error('数量为空');
- validationErrors.push('数量为必填项');
- } else if (!fieldDetails.quantity.isNumeric) {
- console.error('数量不是有效数字: quantity=', product.quantity);
- validationErrors.push('数量必须是有效数字格式');
- } else if (fieldDetails.quantity.parsedValue <= 0) {
- console.error('数量小于等于0: quantity=', product.quantity, '转换为数字后=', fieldDetails.quantity.parsedValue);
- validationErrors.push('数量必须大于0');
- }
-
- // 改进的毛重字段处理逻辑 - 与其他API保持一致
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('发布商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保商品名称不超过数据库字段长度限制
- if (product.productName && product.productName.length > 255) {
- console.error('商品名称过长: 长度=', product.productName.length);
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 如果有验证错误,一次性返回所有错误信息和字段详情
- if (validationErrors.length > 0) {
- console.error('验证失败 - 详细信息:', JSON.stringify({
- errors: validationErrors,
- fieldDetails: fieldDetails
- }, null, 2));
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors,
- detailedMessage: validationErrors.join('; '),
- fieldDetails: fieldDetails
- });
- }
-
- // 查找用户
- console.log('开始查找用户: openid=', openid);
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- console.log('找到用户:', { userId: user.userId, nickName: user.nickName, type: user.type });
-
- // 验证用户类型
- console.log(`验证用户类型: 用户ID=${user.userId}, 类型=${user.type}`);
- if (user.type !== 'seller' && user.type !== 'both') {
- console.error(`商品发布失败: 用户${user.userId}类型为${user.type},需要seller或both类型`);
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有卖家才能发布商品,请在个人资料中修改用户类型'
- });
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
- console.log('生成商品ID:', productId);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行正确的四舍五入
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- console.log('发布商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 创建商品
- console.log('准备创建商品:', {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight,
- sellerId: user.userId
- });
-
- const newProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk || '',
- specification: product.specification || '',
- status: 'pending_review', // 默认状态为待审核
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- const createdProduct = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- if (createdProduct) {
- console.log('发布商品 - 数据库查询后grossWeight:', createdProduct.grossWeight, '类型:', typeof createdProduct.grossWeight);
- }
-
- // 更新用户类型:如果字段为空则填入seller,如果为buyer则修改为both
- try {
- // 重新获取用户信息以确保获取到最新数据
- const currentUser = await User.findOne({ where: { userId: user.userId } });
- if (currentUser) {
- console.log('更新用户类型前 - 当前用户类型:', currentUser.type);
-
- // 检查用户类型并根据需求更新
- if ((currentUser.type === null || currentUser.type === undefined || currentUser.type === '') || currentUser.type === 'buyer') {
- let newType = '';
- if (currentUser.type === 'buyer') {
- newType = 'both';
- } else {
- newType = 'seller';
- }
-
- // 更新用户类型
- await User.update(
- { type: newType },
- { where: { userId: user.userId } }
- );
- console.log('用户类型更新成功 - 用户ID:', user.userId, '旧类型:', currentUser.type, '新类型:', newType);
- } else {
- console.log('不需要更新用户类型 - 用户ID:', user.userId, '当前类型:', currentUser.type);
- }
- }
- } catch (updateError) {
- console.error('更新用户类型失败:', updateError);
- // 不影响商品发布结果,仅记录错误
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品发布成功',
- product: createdProduct,
- productId: productId
- });
-
- } catch (error) {
- console.error('发布商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '发布商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 启动服务器
-app.listen(PORT, () => {
- console.log(`服务器运行在 http://localhost:${PORT}`);
- console.log('注意:当前服务器已添加详细日志记录,用于排查发布商品问题');
- console.log('调试API: POST /api/user/debug - 用于查看用户类型信息');
- console.log(`测试连接接口: http://localhost:${PORT}/api/test-connection`);
-});
-
-// 编辑商品API - 用于审核失败商品重新编辑
-app.post('/api/product/edit', async (req, res) => {
- console.log('收到编辑商品请求 - 详细信息:');
- console.log('- 请求路径:', req.url);
- console.log('- 请求方法:', req.method);
- console.log('- 请求完整body:', req.body);
- console.log('- 服务器端口:', PORT);
- console.log('- 环境变量:', process.env.PORT);
-
- try {
- // 正确解析请求参数,处理嵌套的productData结构
- let openid = req.body.openid;
- let productId = req.body.productId;
- let status = req.body.status;
- let testMode = req.body.testMode;
- let product = req.body.product;
-
- // 处理多层嵌套的productData结构
- if (!product && req.body.productData) {
- // 处理第一种情况: { productData: { openid, productId, product: { ... } } }
- if (req.body.productData.product) {
- product = req.body.productData.product;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- // 处理第二种情况: { productData: { openid, productId, productName, price, ... } }
- else {
- product = req.body.productData;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- }
-
- // 调试日志
- console.log('解析后参数:', { openid, productId, status, testMode, product: !!product });
-
- console.log('收到编辑商品请求,包含状态参数:', { openid, productId, status, testMode });
-
- // 验证必填字段
- if (!openid || !productId || !product) {
- console.error('缺少必要参数: openid=', !!openid, 'productId=', !!productId, 'product=', !!product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid、productId或product对象)'
- });
- }
-
- // 查找用户
- let user = null;
-
- // 测试模式下的特殊处理
- if (testMode) {
- console.log('测试模式:尝试查找或创建测试用户');
- // 首先尝试查找openid为'test_openid'的用户
- user = await User.findOne({
- where: { openid: 'test_openid' }
- });
-
- if (!user) {
- // 如果不存在,创建一个新的测试用户
- console.log('测试模式:创建测试用户');
- try {
- user = await User.create({
- openid: 'test_openid',
- userId: 'test_user_id',
- nickName: '测试用户',
- type: 'seller'
- });
- } catch (createError) {
- console.error('测试模式:创建测试用户失败', createError);
- // 如果创建失败,尝试查找数据库中的第一个用户
- user = await User.findOne({
- order: [['id', 'ASC']]
- });
- if (user) {
- console.log('测试模式:使用数据库中的现有用户', user.userId);
- }
- }
- } else {
- console.log('测试模式:使用已存在的测试用户', user.userId);
- }
- } else {
- // 非测试模式:按常规方式查找用户
- user = await User.findOne({ where: { openid } });
- }
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- // 查找商品
- let existingProduct = null;
-
- if (testMode) {
- // 测试模式:如果找不到商品,尝试使用测试商品或创建一个新的测试商品
- existingProduct = await Product.findOne({
- where: {
- productId: productId
- }
- });
-
- // 如果找不到指定的商品,创建一个新的测试商品
- if (!existingProduct) {
- console.log('测试模式:创建测试商品');
- try {
- existingProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: '测试商品',
- price: 99.99,
- quantity: 100,
- grossWeight: 0, // 默认为0而不是5,符合用户需求
- yolk: '测试描述',
- specification: '测试规格',
- status: 'rejected', // 设置为可编辑状态
- created_at: new Date(),
- updated_at: new Date()
- });
- console.log('测试模式:测试商品创建成功');
- } catch (createProductError) {
- console.error('测试模式:创建测试商品失败', createProductError);
- }
- }
- } else {
- // 非测试模式:验证商品所有权
- existingProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
- }
-
- if (!existingProduct) {
- console.error('编辑商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 验证商品状态是否允许编辑
- if (!['rejected', 'sold_out', 'pending_review', 'reviewed'].includes(existingProduct.status)) {
- console.error(`编辑商品失败: 商品状态(${existingProduct.status})不允许编辑`, {
- productId: productId,
- sellerId: user.userId,
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有审核失败、已下架、审核中或已审核的商品才能编辑',
- debugInfo: {
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- }
- });
- }
-
- // 记录商品编辑信息,用于调试
- console.log(`允许编辑商品: productId=${productId}, status=${existingProduct.status}, sellerId=${user.userId}`);
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price);
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity);
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误
- const validationErrors = [];
-
- // 检查商品名称
- if (!product.productName || product.productName.trim() === '') {
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- } else if (product.productName.length > 255) {
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 检查价格
- if (!product.price) {
- validationErrors.push('价格为必填项');
- } else if (isNaN(parseFloat(product.price)) || parseFloat(product.price) <= 0) {
- validationErrors.push('价格必须是大于0的有效数字');
- }
-
- // 检查数量
- if (!product.quantity) {
- validationErrors.push('数量为必填项');
- } else if (isNaN(parseInt(product.quantity)) || parseInt(product.quantity) <= 0) {
- validationErrors.push('数量必须是大于0的有效数字');
- }
-
- // 改进的毛重字段处理逻辑,与其他API保持一致,空值默认设为0
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('编辑商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保grossWeight值是数字类型
- const finalGrossWeight = Number(grossWeightDetails.parsedValue);
- console.log('编辑商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 如果有验证错误,返回错误信息
- if (validationErrors.length > 0) {
- console.error('验证失败 - 错误:', validationErrors.join('; '));
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors
- });
- }
-
- // 准备更新的商品数据
- const updatedProductData = {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk,
- specification: product.specification,
- // 优先使用前端传递的status参数,如果没有传递则使用原来的逻辑
- status: status && ['pending_review', 'published'].includes(status) ? status :
- (product.resubmit && ['rejected', 'sold_out'].includes(existingProduct.status)) ? 'pending_review' : existingProduct.status,
- rejectReason: (status === 'pending_review' || (product.resubmit && existingProduct.status === 'rejected')) ? null : existingProduct.rejectReason, // 提交审核时清除拒绝原因
- updated_at: new Date()
- };
-
- console.log('准备更新商品数据:', { productId, updatedStatus: updatedProductData.status, fromStatus: existingProduct.status });
-
- // 更新商品
- const [updatedCount] = await Product.update(updatedProductData, {
- where: testMode ? {
- // 测试模式:只根据productId更新
- productId: productId
- } : {
- // 非测试模式:验证商品所有权
- productId: productId,
- sellerId: user.userId
- }
- });
-
- // 检查更新是否成功
- if (updatedCount === 0) {
- console.error('商品更新失败: 没有找到匹配的商品或权限不足');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品更新失败: 没有找到匹配的商品或权限不足'
- });
- }
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({ where: { productId: productId } });
-
- console.log('查询数据库后 - 更新的商品信息:', {
- grossWeight: updatedProduct?.grossWeight,
- grossWeightType: typeof updatedProduct?.grossWeight,
- productId: updatedProduct?.productId,
- status: updatedProduct?.status
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 注意:这里检查undefined和null,并且对于空字符串或5的情况也进行处理
- if (updatedProduct) {
- console.log('处理前 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (updatedProduct.grossWeight === undefined || updatedProduct.grossWeight === null || updatedProduct.grossWeight === '') {
- updatedProduct.grossWeight = 0;
- console.log('检测到空值 - 已设置为0');
- } else {
- // 否则转换为浮点数
- updatedProduct.grossWeight = parseFloat(updatedProduct.grossWeight);
- }
-
- console.log('处理后 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- }
-
- console.log('商品编辑成功:', {
- productId: productId,
- productName: product.productName,
- oldStatus: existingProduct.status, // 记录更新前的状态
- newStatus: updatedProduct.status, // 记录更新后的状态
- grossWeight: updatedProduct.grossWeight // 记录处理后的毛重值
- });
-
- // 根据新的状态生成适当的返回消息
- let returnMessage = '';
- if (updatedProduct.status === 'pending_review') {
- returnMessage = '商品编辑成功,已重新提交审核';
- } else if (updatedProduct.status === 'published') {
- returnMessage = '商品编辑成功,已上架';
- } else if (updatedProduct.status === existingProduct.status) {
- returnMessage = '商品编辑成功,状态保持不变';
- } else {
- returnMessage = '商品编辑成功';
- }
-
- res.json({
- success: true,
- code: 200,
- message: returnMessage,
- product: updatedProduct
- });
- } catch (error) {
- console.error('编辑商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '编辑商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 导出模型和Express应用供其他模块使用
-module.exports = {
- User,
- Product,
- CartItem,
- sequelize,
- createUserAssociations,
- app,
- PORT
-};
\ No newline at end of file
diff --git a/server-example/server-mysql.backup.js b/server-example/server-mysql.backup.js
deleted file mode 100644
index bd8f719..0000000
--- a/server-example/server-mysql.backup.js
+++ /dev/null
@@ -1,2960 +0,0 @@
-// ECS服务器示例代码 - Node.js版 (MySQL版本)
-const express = require('express');
-const crypto = require('crypto');
-const bodyParser = require('body-parser');
-const { Sequelize, DataTypes, Model, Op } = require('sequelize');
-require('dotenv').config();
-
-// 创建Express应用
-const app = express();
-const PORT = process.env.PORT || 3002;
-
-// 中间件
-app.use(bodyParser.json());
-
-// 添加请求日志中间件,捕获所有到达服务器的请求(必须放在bodyParser之后)
-app.use((req, res, next) => {
- // 将UTC时间转换为北京时间(UTC+8)
- const now = new Date();
- const beijingTime = new Date(now.getTime() + 8 * 60 * 60 * 1000);
- const formattedTime = beijingTime.toISOString().replace('Z', '+08:00');
-
- console.log(`[${formattedTime}] 收到请求: ${req.method} ${req.url}`);
- console.log('请求头:', req.headers);
- console.log('请求体:', req.body);
- next();
-});
-
-// 商品毛重处理中间件 - 确保所有返回的商品数据中毛重字段保持原始值
-app.use((req, res, next) => {
- // 保存原始的json方法
- const originalJson = res.json;
-
- // 重写json方法来处理响应数据
- res.json = function (data) {
- // 检查数据中是否包含商品列表
- if (data && typeof data === 'object') {
- // 处理/products/list接口的响应
- if (data.products && Array.isArray(data.products)) {
- data.products = data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
-
- // 处理/data字段中的商品列表
- if (data.data && data.data.products && Array.isArray(data.data.products)) {
- data.data.products = data.data.products.map(product => {
- // 保持毛重字段的原始值,只做类型转换确保是数字
- if (product.grossWeight === null || product.grossWeight === undefined || product.grossWeight === '') {
- product.grossWeight = 0; // 空值设置为0
- } else {
- product.grossWeight = parseFloat(product.grossWeight);
- }
- return product;
- });
- }
- }
-
- // 调用原始的json方法
- return originalJson.call(this, data);
- };
-
- next();
-});
-
-// 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
- }
- }
-);
-
-// 微信小程序配置
-const WECHAT_CONFIG = {
- APPID: process.env.WECHAT_APPID || 'your-wechat-appid',
- APPSECRET: process.env.WECHAT_APPSECRET || 'your-wechat-appsecret',
- TOKEN: process.env.WECHAT_TOKEN || 'your-wechat-token'
-};
-
-// 显示当前使用的数据库配置(用于调试)
-console.log('当前数据库连接配置:');
-console.log(' 主机:', process.env.DB_HOST || 'localhost');
-console.log(' 端口:', process.env.DB_PORT || 3306);
-console.log(' 数据库名:', process.env.DB_DATABASE || 'wechat_app');
-console.log(' 用户名:', process.env.DB_USER || 'root');
-console.log(' 密码:', process.env.DB_PASSWORD === undefined || process.env.DB_PASSWORD === '' ? '无密码' : '******');
-
-// 测试数据库连接
-async function testDbConnection() {
- try {
- await sequelize.authenticate();
- console.log('数据库连接成功');
- } catch (error) {
- console.error('数据库连接失败:', error);
- console.error('\n请检查以下几点:');
- console.error('1. MySQL服务是否已经启动');
- console.error('2. wechat_app数据库是否已创建');
- console.error('3. .env文件中的数据库用户名和密码是否正确');
- console.error('4. 用户名是否有足够的权限访问数据库');
- console.error('\n如果是首次配置,请参考README文件中的数据库设置指南。');
- process.exit(1);
- }
-}
-
-testDbConnection();
-
-// 定义数据模型
-
-// 用户模型
-class User extends Model { }
-User.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- openid: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 微信名,必填
- },
- avatarUrl: {
- type: DataTypes.TEXT
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 电话号码,必填
- },
- type: {
- type: DataTypes.STRING(20),
- allowNull: false // 用户身份(buyer/seller/both),必填
- },
- gender: {
- type: DataTypes.INTEGER
- },
- country: {
- type: DataTypes.STRING(50)
- },
- province: {
- type: DataTypes.STRING(50)
- },
- city: {
- type: DataTypes.STRING(50)
- },
- language: {
- type: DataTypes.STRING(20)
- },
- session_key: {
- type: DataTypes.STRING(255)
- },
- // 新增字段
- company: {
- type: DataTypes.STRING(255) // 客户公司
- },
- region: {
- type: DataTypes.STRING(255) // 客户地区
- },
- level: {
- type: DataTypes.STRING(255),
- defaultValue: 'company-sea-pools' // 客户等级,默认值为company-sea-pools
- },
- demand: {
- type: DataTypes.TEXT // 基本需求
- },
- spec: {
- type: DataTypes.TEXT // 规格
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'User',
- tableName: 'users',
- timestamps: false
-});
-
-// 商品模型
-class Product extends Model { }
-Product.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- sellerId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- price: {
- type: DataTypes.DECIMAL(10, 2),
- allowNull: false
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- status: {
- type: DataTypes.STRING(20),
- defaultValue: 'pending_review',
- validate: {
- isIn: [['pending_review', 'reviewed', 'published', 'sold_out', 'rejected', 'hidden']]
- }
- },
- rejectReason: {
- type: DataTypes.TEXT
- },
- // 新增预约相关字段
- reservedCount: {
- type: DataTypes.INTEGER,
- defaultValue: 0,
- allowNull: false,
- comment: '已有几人想要'
- },
- 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
-});
-
-// 购物车模型
-class CartItem extends Model { }
-CartItem.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- productId: {
- type: DataTypes.STRING(100),
- allowNull: false
- },
- productName: {
- type: DataTypes.STRING(255),
- allowNull: false
- },
- specification: {
- type: DataTypes.STRING(255)
- },
- quantity: {
- type: DataTypes.INTEGER,
- allowNull: false,
- defaultValue: 1
- },
- grossWeight: {
- type: DataTypes.DECIMAL(10, 2)
- },
- yolk: {
- type: DataTypes.STRING(100)
- },
- price: {
- type: DataTypes.DECIMAL(10, 2)
- },
- selected: {
- type: DataTypes.BOOLEAN,
- defaultValue: true
- },
- added_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'CartItem',
- tableName: 'cart_items',
- timestamps: false
-});
-
-// 联系人表模型
-class Contact extends Model { }
-Contact.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- nickName: {
- type: DataTypes.STRING(100),
- allowNull: false // 联系人
- },
- phoneNumber: {
- type: DataTypes.STRING(20),
- allowNull: false // 手机号
- },
- wechat: {
- type: DataTypes.STRING(100) // 微信号
- },
- account: {
- type: DataTypes.STRING(100) // 账户
- },
- accountNumber: {
- type: DataTypes.STRING(100) // 账号
- },
- bank: {
- type: DataTypes.STRING(100) // 开户行
- },
- address: {
- type: DataTypes.TEXT // 地址
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'Contact',
- tableName: 'contacts',
- timestamps: false
-});
-
-// 用户管理表模型
-class UserManagement extends Model { }
-UserManagement.init({
- id: {
- type: DataTypes.INTEGER,
- autoIncrement: true,
- primaryKey: true
- },
- userId: {
- type: DataTypes.STRING(100),
- allowNull: false,
- unique: true
- },
- managerId: {
- type: DataTypes.STRING(100),
- defaultValue: null // 经理ID,默认值为null
- },
- company: {
- type: DataTypes.STRING(255),
- defaultValue: null // 公司,默认值为null
- },
- department: {
- type: DataTypes.STRING(255),
- defaultValue: null // 部门,默认值为null
- },
- organization: {
- type: DataTypes.STRING(255),
- defaultValue: null // 组织,默认值为null
- },
- role: {
- type: DataTypes.STRING(100),
- defaultValue: null // 角色,默认值为null
- },
- root: {
- type: DataTypes.STRING(100),
- defaultValue: null // 根节点,默认值为null
- },
- created_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW
- },
- updated_at: {
- type: DataTypes.DATE,
- defaultValue: Sequelize.NOW,
- onUpdate: Sequelize.NOW
- }
-}, {
- sequelize,
- modelName: 'UserManagement',
- tableName: 'usermanagements',
- timestamps: false
-});
-
-// 定义模型之间的关联关系
-
-// 用户和商品的一对多关系 (卖家发布商品)
-User.hasMany(Product, {
- foreignKey: 'sellerId', // 外键字段名
- sourceKey: 'userId', // 源键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'products', // 别名,用于关联查询
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Product.belongsTo(User, {
- foreignKey: 'sellerId',
- targetKey: 'userId', // 目标键,使用userId字段(STRING类型)而非默认的id字段(INTEGER类型)
- as: 'seller' // 别名,用于关联查询
-});
-
-// 用户和购物车项的一对多关系 (买家的购物需求/购物车)
-User.hasMany(CartItem, {
- foreignKey: 'userId',
- as: 'cartItems', // 用户的购物车(购物需求)列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(User, {
- foreignKey: 'userId',
- as: 'buyer' // 别名,明确表示这是购物需求的买家
-});
-
-// 商品和购物车项的一对多关系 (商品被添加到购物车)
-Product.hasMany(CartItem, {
- foreignKey: 'productId',
- as: 'cartItems', // 商品出现在哪些购物车中
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-CartItem.belongsTo(Product, {
- foreignKey: 'productId',
- as: 'product' // 购物车项中的商品
-});
-
-// 用户和联系人的一对多关系
-User.hasMany(Contact, {
- foreignKey: 'userId',
- as: 'contacts', // 用户的联系人列表
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-Contact.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 联系人所属用户
-});
-
-// 用户和用户管理的一对一关系
-User.hasOne(UserManagement, {
- foreignKey: 'userId',
- as: 'management', // 用户的管理信息
- onDelete: 'CASCADE', // 级联删除
- onUpdate: 'CASCADE' // 级联更新
-});
-
-UserManagement.belongsTo(User, {
- foreignKey: 'userId',
- as: 'user' // 管理信息所属用户
-});
-
-// 同步数据库模型到MySQL
-async function syncDatabase() {
- try {
- // 不使用alter: true,避免尝试修改已有表结构导致的外键约束问题
- await sequelize.sync({
- force: false // 不强制重新创建表
- });
- console.log('数据库模型同步成功');
- } catch (error) {
- console.error('数据库模型同步失败:', error);
- // 即使同步失败也继续运行,因为我们只需要API功能
- console.log('数据库模型同步失败,但服务器继续运行,使用现有表结构');
- }
-}
-
-syncDatabase();
-
-// 解密微信加密数据
-function decryptData(encryptedData, sessionKey, iv) {
- try {
- // Base64解码
- const sessionKeyBuf = Buffer.from(sessionKey, 'base64');
- const encryptedDataBuf = Buffer.from(encryptedData, 'base64');
- const ivBuf = Buffer.from(iv, 'base64');
-
- // AES解密
- const decipher = crypto.createDecipheriv('aes-128-cbc', sessionKeyBuf, ivBuf);
- decipher.setAutoPadding(true);
- let decoded = decipher.update(encryptedDataBuf, 'binary', 'utf8');
- decoded += decipher.final('utf8');
-
- // 解析JSON
- return JSON.parse(decoded);
- } catch (error) {
- console.error('解密失败:', error);
- // 提供更具体的错误信息
- if (error.code === 'ERR_OSSL_BAD_DECRYPT') {
- throw new Error('登录信息已过期,请重新登录');
- } else if (error.name === 'SyntaxError') {
- throw new Error('数据格式错误,解密结果无效');
- } else {
- throw new Error('解密失败,请重试');
- }
- }
-}
-
-// 获取微信session_key
-async function getSessionKey(code) {
- const axios = require('axios');
- const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${WECHAT_CONFIG.APPID}&secret=${WECHAT_CONFIG.APPSECRET}&js_code=${code}&grant_type=authorization_code`;
-
- try {
- const response = await axios.get(url);
- return response.data;
- } catch (error) {
- console.error('获取session_key失败:', error);
- throw new Error('获取session_key失败');
- }
-}
-
-// 创建用户关联记录函数 - 自动为用户创建contacts和usermanagements表的关联记录
-async function createUserAssociations(user) {
- try {
- if (!user || !user.userId) {
- console.error('无效的用户数据,无法创建关联记录');
- return false;
- }
-
- console.log('为用户创建关联记录:', user.userId);
-
- // 使用事务确保操作原子性
- await sequelize.transaction(async (transaction) => {
- // 1. 处理联系人记录 - 使用INSERT ... ON DUPLICATE KEY UPDATE确保无论如何都只保留一条记录
- await sequelize.query(
- `INSERT INTO contacts (userId, nickName, phoneNumber, created_at, updated_at)
- VALUES (?, ?, ?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- nickName = VALUES(nickName),
- phoneNumber = VALUES(phoneNumber),
- updated_at = NOW()`,
- {
- replacements: [user.userId, user.nickName || '默认联系人', user.phoneNumber || ''],
- transaction: transaction
- }
- );
- console.log('联系人记录已处理(创建或更新):', user.userId);
-
- // 2. 处理用户管理记录 - 使用相同策略
- await sequelize.query(
- `INSERT INTO usermanagements (userId, created_at, updated_at)
- VALUES (?, NOW(), NOW())
- ON DUPLICATE KEY UPDATE
- updated_at = NOW()`,
- {
- replacements: [user.userId],
- transaction: transaction
- }
- );
- console.log('用户管理记录已处理(创建或更新):', user.userId);
- });
-
- console.log('用户关联记录处理成功:', user.userId);
- return true;
- } catch (error) {
- console.error('创建用户关联记录失败:', error.message);
- return false;
- }
-}
-
-// API路由
-
-// 上传用户信息
-app.post('/api/user/upload', async (req, res) => {
- try {
- const userData = req.body;
- console.log('收到用户信息上传请求:', userData);
-
- // 如果用户信息中包含手机号,检查手机号是否已被其他用户使用
- if (userData.phoneNumber) {
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: userData.phoneNumber,
- openid: { [Sequelize.Op.ne]: userData.openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${userData.phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 创建新对象,移除手机号字段
- const userDataWithoutPhone = { ...userData };
- delete userDataWithoutPhone.phoneNumber;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息(不包含手机号)
- await User.update(
- {
- ...userDataWithoutPhone,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
- } else {
- // 创建新用户
- user = await User.create({
- ...userDataWithoutPhone,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- // 返回成功,但提示手机号已被使用
- return res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功,但手机号已被其他账号绑定',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: true
- });
- }
- }
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid: userData.openid }
- });
-
- if (user) {
- // 更新用户信息
- await User.update(
- {
- ...userData,
- updated_at: new Date()
- },
- {
- where: { openid: userData.openid }
- }
- );
- user = await User.findOne({ where: { openid: userData.openid } });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- } else {
- // 创建新用户
- user = await User.create({
- ...userData,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(user);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '用户信息保存成功',
- data: {
- userId: user.userId
- },
- phoneNumberConflict: false
- });
- } catch (error) {
- console.error('保存用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '保存用户信息失败',
- error: error.message
- });
- }
-});
-
-// 解密手机号
-app.post('/api/user/decodePhone', async (req, res) => {
- try {
- const { encryptedData, iv, openid } = req.body;
-
- // 参数校验
- if (!encryptedData || !iv || !openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数'
- });
- }
-
- // 查找用户的session_key
- const user = await User.findOne({ where: { openid } });
-
- if (!user || !user.session_key) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录,请先登录',
- needRelogin: true
- });
- }
-
- // 解密手机号
- let decryptedData, phoneNumber;
- try {
- decryptedData = decryptData(encryptedData, user.session_key, iv);
- phoneNumber = decryptedData.phoneNumber;
- } catch (decryptError) {
- // 解密失败,可能是session_key过期,建议重新登录
- return res.status(401).json({
- success: false,
- code: 401,
- message: decryptError.message || '手机号解密失败',
- needRelogin: true
- });
- }
-
- // 检查手机号是否已被其他用户使用
- const existingUserWithPhone = await User.findOne({
- where: {
- phoneNumber: phoneNumber,
- openid: { [Sequelize.Op.ne]: openid } // 排除当前用户
- }
- });
-
- if (existingUserWithPhone) {
- // 手机号已被其他用户使用,不更新手机号
- console.warn(`手机号 ${phoneNumber} 已被其他用户使用,用户ID: ${existingUserWithPhone.userId}`);
-
- // 返回成功,但不更新手机号,提示用户
- return res.json({
- success: true,
- code: 200,
- message: '手机号已被其他账号绑定',
- phoneNumber: user.phoneNumber, // 返回原手机号
- isNewPhone: false
- });
- }
-
- // 更新用户手机号
- await User.update(
- {
- phoneNumber: phoneNumber,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 更新用户手机号后,更新关联记录
- const updatedUser = await User.findOne({ where: { openid } });
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '手机号解密成功',
- phoneNumber: phoneNumber,
- isNewPhone: true
- });
- } catch (error) {
- console.error('手机号解密失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '手机号解密失败',
- error: error.message
- });
- }
-});
-
-// 处理微信登录,获取openid和session_key
-app.post('/api/wechat/getOpenid', async (req, res) => {
- try {
- const { code } = req.body;
-
- if (!code) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少code参数'
- });
- }
-
- // 获取openid和session_key
- const wxData = await getSessionKey(code);
-
- if (wxData.errcode) {
- throw new Error(`微信接口错误: ${wxData.errmsg}`);
- }
-
- const { openid, session_key, unionid } = wxData;
-
- // 生成userId
- const userId = `user_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 查找用户是否已存在
- let user = await User.findOne({
- where: { openid }
- });
-
- if (user) {
- // 更新用户session_key
- await User.update(
- {
- session_key: session_key,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
- } else {
- // 创建新用户
- // 支持从客户端传入type参数,如果没有则默认为buyer
- const userType = req.body.type || 'buyer';
- await User.create({
- openid,
- userId,
- session_key,
- nickName: '微信用户', // 临时占位,等待用户授权
- type: userType, // 使用客户端传入的类型或默认买家身份
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 为新创建的用户创建关联记录
- const newUser = { userId, openid, nickName: '微信用户' };
- await createUserAssociations(newUser);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取openid成功',
- data: {
- openid,
- userId: user ? user.userId : userId
- }
- });
- } catch (error) {
- console.error('获取openid失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取openid失败',
- error: error.message
- });
- }
-});
-
-// 验证用户登录状态
-app.post('/api/user/validate', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'avatarUrl', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(401).json({
- success: false,
- code: 401,
- message: '用户未登录'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '验证成功',
- data: user
- });
- } catch (error) {
- console.error('验证用户登录状态失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '验证失败',
- error: error.message
- });
- }
-});
-
-// 获取用户信息
-app.post('/api/user/get', async (req, res) => {
- try {
- const { openid } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid },
- include: [
- {
- model: Contact,
- as: 'contacts',
- attributes: ['id', 'nickName', 'phoneNumber', 'wechat', 'account', 'accountNumber', 'bank', 'address']
- },
- {
- model: UserManagement,
- as: 'management',
- attributes: ['id', 'managerId', 'company', 'department', 'organization', 'role', 'root']
- }
- ]
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- res.json({
- success: true,
- code: 200,
- message: '获取用户信息成功',
- data: user
- });
- } catch (error) {
- console.error('获取用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户信息失败',
- error: error.message
- });
- }
-});
-
-// 更新用户信息
-app.post('/api/user/update', async (req, res) => {
- try {
- const { openid, ...updateData } = req.body;
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查找用户
- const user = await User.findOne({
- where: { openid }
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 更新用户信息
- await User.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { openid }
- }
- );
-
- // 获取更新后的用户信息
- const updatedUser = await User.findOne({
- where: { openid }
- });
-
- // 使用统一的关联记录创建函数
- await createUserAssociations(updatedUser);
-
- res.json({
- success: true,
- code: 200,
- message: '更新用户信息成功',
- data: updatedUser
- });
- } catch (error) {
- console.error('更新用户信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新用户信息失败',
- error: error.message
- });
- }
-});
-
-// 获取商品列表 - 优化版本确保状态筛选正确应用
-app.post('/api/product/list', async (req, res) => {
- try {
- const { openid, status, keyword, page = 1, pageSize = 20, testMode = false } = req.body;
-
- // 验证openid参数(测试模式除外)
- if (!openid && !testMode) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 构建查询条件
- const where = {};
-
- // 查找用户
- let user = null;
- if (!testMode) {
- user = await User.findOne({ where: { openid } });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- // 只有管理员可以查看所有商品,普通用户只能查看自己的商品
- if (user.type !== 'admin') {
- where.sellerId = user.userId;
- }
- }
-
- // 状态筛选 - 直接构建到where对象中,确保不会丢失
- console.log(`当前用户类型: ${user ? user.type : '未知'},请求状态: ${status || '未指定'},测试模式: ${testMode}`);
-
- // 如果有指定status参数,按参数筛选但同时排除hidden
- if (status) {
- console.log(`按状态筛选商品: status=${status},并排除hidden状态`);
- if (status === 'all') {
- // 特殊情况:请求所有商品但仍然排除hidden
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else if (Array.isArray(status)) {
- // 如果status是数组,确保不包含hidden
- where.status = { [Sequelize.Op.in]: status.filter(s => s !== 'hidden') };
- } else {
- // 单个状态值,确保不是hidden
- if (status !== 'hidden') {
- where.status = { [Sequelize.Op.eq]: status };
- } else {
- // 如果明确请求hidden状态,也返回空结果
- where.status = { [Sequelize.Op.not]: 'hidden' };
- }
- }
- } else {
- // 没有指定status参数时 - 直接在where对象中设置状态筛选
- if (user && (user.type === 'seller' || user.type === 'both') && !testMode) {
- // 卖家用户且非测试模式
- console.log(`卖家用户 ${user.userId} (类型:${user.type}) 查看自己的所有商品,但排除hidden状态`);
- // 卖家可以查看自己的所有商品,但仍然排除hidden状态
- where.status = { [Sequelize.Op.not]: 'hidden' };
- } else {
- // 测试模式或非卖家用户
- console.log(`测试模式或非卖家用户,使用默认状态筛选: reviewed/published`);
- // 默认只显示已审核和已发布的商品,排除hidden和sold_out状态
- where.status = { [Sequelize.Op.in]: ['reviewed', 'published'] };
- }
- }
-
- console.log(`构建的完整查询条件:`, JSON.stringify(where, null, 2));
-
- // 关键词搜索
- if (keyword) {
- where.productName = { [Sequelize.Op.like]: `%${keyword}%` };
- }
-
- // 计算偏移量
- const offset = (page - 1) * pageSize;
-
- // 查询商品列表
- const { count, rows: products } = await Product.findAndCountAll({
- where,
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- },
- // 添加CartItem关联以获取预约人数
- {
- model: CartItem,
- as: 'CartItems', // 明确指定别名
- attributes: [],
- required: false // 允许没有购物车项的商品也能返回
- }
- ],
- // 添加selected字段,计算商品被加入购物车的次数(预约人数)
- attributes: {
- include: [
- [Sequelize.fn('COUNT', Sequelize.col('CartItems.id')), 'selected']
- ]
- },
- order: [['created_at', 'DESC']],
- limit: pageSize,
- offset,
- // 修复分组问题
- group: ['Product.productId', 'seller.userId'] // 使用正确的字段名
- });
-
- // 添加详细日志,记录查询结果
- console.log(`商品列表查询结果 - 商品数量: ${count}, 商品列表长度: ${products.length}`);
- if (products.length > 0) {
- console.log(`第一个商品数据:`, JSON.stringify(products[0], null, 2));
-
- // 添加selected字段的专门日志
- console.log('商品预约人数(selected字段)统计:');
- products.slice(0, 5).forEach(product => {
- const productJSON = product.toJSON();
- console.log(`- ${productJSON.productName}: 预约人数=${productJSON.selected || 0}, 商品ID=${productJSON.productId}`);
- });
- }
-
- // 处理商品列表中的grossWeight字段,确保是数字类型
- const processedProducts = products.map(product => {
- const productJSON = product.toJSON();
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productJSON.grossWeight,
- type: typeof productJSON.grossWeight,
- isEmpty: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined,
- isNumeric: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined || !isNaN(parseFloat(productJSON.grossWeight)) && isFinite(productJSON.grossWeight),
- parsedValue: productJSON.grossWeight === '' || productJSON.grossWeight === null || productJSON.grossWeight === undefined ? 0 : parseFloat(productJSON.grossWeight)
- };
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- productJSON.grossWeight = finalGrossWeight;
-
- // 记录第一个商品的转换信息用于调试
- if (products.indexOf(product) === 0) {
- console.log('商品列表 - 第一个商品毛重字段处理:');
- console.log('- 原始值:', grossWeightDetails.value, '类型:', grossWeightDetails.type);
- console.log('- 转换后的值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- }
-
- return productJSON;
- });
-
- // 准备响应数据 - 修改格式以匹配前端期望
- const responseData = {
- success: true,
- code: 200,
- message: '获取商品列表成功',
- products: processedProducts,
- total: count,
- page: page,
- pageSize: pageSize,
- totalPages: Math.ceil(count / pageSize)
- };
-
- console.log(`准备返回的响应数据格式:`, JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
-
- // 添加详细的查询条件日志
- console.log(`最终查询条件:`, JSON.stringify(where, null, 2));
-
- res.json(responseData);
- } catch (error) {
- console.error('获取商品列表失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品列表失败',
- error: error.message
- });
- }
-});
-
-// 上传商品
-app.post('/api/products/upload', async (req, res) => {
- try {
- // 修复毛重字段处理逻辑
- let productData = req.body;
- if (productData && productData.productData) {
- productData = productData.productData; // 使用正确的productData对象
- }
-
- // 改进的毛重字段处理逻辑,与编辑API保持一致
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: productData.grossWeight,
- type: typeof productData.grossWeight,
- isEmpty: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined,
- isNumeric: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined || !isNaN(parseFloat(productData.grossWeight)) && isFinite(productData.grossWeight),
- parsedValue: productData.grossWeight === '' || productData.grossWeight === null || productData.grossWeight === undefined ? 0 : parseFloat(productData.grossWeight)
- };
-
- // 详细的日志记录
- console.log('上传商品 - 毛重字段详细分析:');
- console.log('- 原始值:', productData.grossWeight, '类型:', typeof productData.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行精确四舍五入,确保3位小数以上的值正确转换
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- productData.grossWeight = finalGrossWeight;
- console.log('上传商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
- console.log('收到商品上传请求:', productData);
-
- // 验证必要字段
- if (!productData.sellerId || !productData.productName || !productData.price || !productData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的商品信息'
- });
- }
-
- // 检查sellerId是否为openid,如果是则查找对应的userId
- let actualSellerId = productData.sellerId;
-
- // 如果sellerId看起来像一个openid(包含特殊字符如'-'),则尝试查找对应的userId
- if (productData.sellerId.includes('-')) {
- console.log('sellerId看起来像openid,尝试查找对应的userId');
- const user = await User.findOne({
- where: {
- openid: productData.sellerId
- }
- });
-
- if (user && user.userId) {
- console.log(`找到了对应的userId: ${user.userId}`);
- actualSellerId = user.userId;
- } else {
- console.error(`未找到对应的用户记录,openid: ${productData.sellerId}`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '找不到对应的用户记录'
- });
- }
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
-
- // 创建商品,使用实际的sellerId
- let product = await Product.create({
- ...productData,
- sellerId: actualSellerId, // 使用查找到的userId
- productId,
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- product = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 与编辑API保持一致的处理逻辑
- if (product) {
- console.log('上传商品 - 处理前grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (product.grossWeight === undefined || product.grossWeight === null || product.grossWeight === '') {
- product.grossWeight = 0;
- console.log('上传商品 - 检测到空值,已设置为0');
- } else {
- // 否则转换为浮点数
- product.grossWeight = parseFloat(product.grossWeight);
- }
-
- console.log('上传商品 - 处理后grossWeight:', product.grossWeight, '类型:', typeof product.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品上传成功',
- data: {
- productId: product.productId,
- product: product
- }
- });
- } catch (error) {
- console.error('商品上传失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '商品上传失败',
- error: error.message
- });
- }
-});
-
-// 获取商品详情
-app.post('/api/products/detail', async (req, res) => {
- try {
- const { productId } = req.body;
-
- if (!productId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId参数'
- });
- }
-
- // 查询商品详情 - 排除hidden状态商品
- const product = await Product.findOne({
- where: {
- productId,
- status: { [Sequelize.Op.not]: 'hidden' }
- },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 对返回的商品数据中的grossWeight字段进行处理,确保是数字类型
- let updatedProduct = { ...product.toJSON() };
-
- // 详细分析毛重字段
- const grossWeightDetails = {
- value: updatedProduct.grossWeight,
- type: typeof updatedProduct.grossWeight,
- isEmpty: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined,
- isNumeric: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined || !isNaN(parseFloat(updatedProduct.grossWeight)) && isFinite(updatedProduct.grossWeight),
- parsedValue: updatedProduct.grossWeight === '' || updatedProduct.grossWeight === null || updatedProduct.grossWeight === undefined ? 0 : parseFloat(updatedProduct.grossWeight)
- };
-
- // 详细的日志记录
- console.log('商品详情 - 毛重字段详细分析:');
- console.log('- 原始值:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- const finalGrossWeight = parseFloat(grossWeightDetails.parsedValue.toFixed(2));
- updatedProduct.grossWeight = finalGrossWeight;
- console.log('商品详情 - 最终返回的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- res.json({
- success: true,
- code: 200,
- message: '获取商品详情成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('获取商品详情失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取商品详情失败',
- error: error.message
- });
- }
-});
-
-// 修改商品
-app.post('/api/products/edit', async (req, res) => {
- try {
- const { productId, ...updateData } = req.body;
- const { sellerId } = req.body;
-
- if (!productId || !sellerId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权修改此商品'
- });
- }
-
- // 更新商品信息
- await Product.update(
- {
- ...updateData,
- updated_at: new Date()
- },
- {
- where: { productId }
- }
- );
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '修改商品成功',
- data: updatedProduct
- });
- } catch (error) {
- console.error('修改商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '修改商品失败',
- error: error.message
- });
- }
-});
-
-// 删除商品 - 将商品状态设置为hidden表示已删除
-app.post('/api/products/delete', async (req, res) => {
- console.log('收到删除商品请求:', req.body);
- try {
- const { productId, sellerId } = req.body;
-
- if (!productId || !sellerId) {
- console.error('删除商品失败: 缺少productId或sellerId参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少productId或sellerId参数'
- });
- }
-
- // 查找商品
- const product = await Product.findOne({
- where: { productId }
- });
-
- if (!product) {
- console.error('删除商品失败: 商品不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在'
- });
- }
-
- // 检查是否为卖家本人
- if (product.sellerId !== sellerId) {
- console.error('删除商品失败: 权限不足 - 卖家ID不匹配', { expected: product.sellerId, actual: sellerId });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '您无权删除此商品'
- });
- }
-
- console.log('准备更新商品状态为hidden,当前状态:', product.status);
-
- // 直接使用商品实例更新状态
- product.status = 'hidden';
- product.updated_at = new Date();
-
- try {
- // 先尝试保存商品实例
- await product.save();
- console.log('删除商品成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- // 如果保存失败,尝试使用update方法
- try {
- const updateResult = await Product.update(
- { status: 'hidden', updated_at: new Date() },
- { where: { productId } }
- );
- console.log('删除商品成功(使用update方法):', { productId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- // 如果update方法也失败,尝试直接执行SQL语句绕过ORM验证
- try {
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId',
- {
- replacements: {
- status: 'hidden',
- updatedAt: new Date(),
- productId: productId
- }
- }
- );
- console.log('删除商品成功(使用原始SQL):', { productId });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 从购物车中移除该商品
- const destroyResult = await CartItem.destroy({
- where: { productId }
- });
- console.log('从购物车移除商品结果:', destroyResult);
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: { productId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除商品成功',
- product: {
- productId: updatedProduct.productId,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('删除商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除商品失败',
- error: error.message
- });
- }
-});
-
-// 添加商品到购物车
-app.post('/api/cart/add', async (req, res) => {
- // 增加全局错误捕获,确保即使在try-catch外部的错误也能被处理
- try {
- console.log('收到添加到购物车请求 - 开始处理', req.url);
- let cartData = req.body;
- console.log('收到添加到购物车请求数据:', cartData);
- console.log('请求头:', req.headers);
- console.log('请求IP:', req.ip);
-
- // 兼容客户端请求格式:客户端可能将数据封装在product对象中,并且使用openid而不是userId
- if (cartData.product && !cartData.productId) {
- // 从product对象中提取数据
- const productData = cartData.product;
- console.log('从product对象提取数据:', productData);
- console.log('客户端提供的openid:', cartData.openid);
-
- // 使用openid作为userId
- cartData = {
- userId: cartData.openid || productData.userId,
- productId: productData.productId || productData.id,
- productName: productData.productName || productData.name,
- quantity: productData.quantity || 1,
- price: productData.price,
- specification: productData.specification || productData.spec || '',
- grossWeight: productData.grossWeight || productData.weight,
- yolk: productData.yolk || productData.variety || '',
- testMode: productData.testMode || cartData.testMode
- };
- console.log('转换后的购物车数据:', cartData);
-
- // 检查转换后的userId是否存在于users表中
- try {
- console.log('开始查询用户信息,openid:', cartData.userId);
- const user = await User.findOne({
- where: { openid: cartData.userId }
- });
- if (user) {
- console.log(`找到对应的用户记录: openid=${cartData.userId}, userId=${user.userId}`);
- // 修正:使用数据库中真实的userId而不是openid
- cartData.userId = user.userId;
- console.log('修正后的userId:', cartData.userId);
- } else {
- console.error(`未找到openid为 ${cartData.userId} 的用户记录,无法添加到购物车`);
- // 重要:找不到用户时返回错误,避免使用无效的userId导致外键约束失败
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `未找到用户记录: ${cartData.userId}`
- });
- }
- } catch (error) {
- console.error('查询用户信息失败:', error);
- // 查询失败时也返回错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '查询用户信息失败',
- error: error.message
- });
- }
- }
-
- // 验证必要字段
- if (!cartData.userId || !cartData.productId || !cartData.productName || !cartData.quantity) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的购物车信息',
- missingFields: [
- !cartData.userId ? 'userId' : '',
- !cartData.productId ? 'productId' : '',
- !cartData.productName ? 'productName' : '',
- !cartData.quantity ? 'quantity' : ''
- ].filter(Boolean)
- });
- }
-
- // 先验证用户ID是否存在于users表中
- try {
- const userExists = await User.findOne({
- where: { userId: cartData.userId }
- });
- if (!userExists) {
- console.error(`用户ID ${cartData.userId} 不存在于users表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '用户信息无效,请重新登录后重试',
- error: `用户ID ${cartData.userId} 不存在`
- });
- } else {
- console.log(`用户ID ${cartData.userId} 存在于users表中,用户验证通过`);
- }
- } catch (error) {
- console.error('验证用户ID失败:', error);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '验证用户信息失败',
- error: error.message
- });
- }
-
- // 检查商品是否存在以及是否为hidden状态
- console.log(`检查商品ID: ${cartData.productId} 是否存在于products表中`);
- const product = await Product.findOne({
- where: {
- productId: cartData.productId
- }
- });
-
- if (!product) {
- console.error(`商品ID ${cartData.productId} 不存在于products表中`);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '商品不存在或已被移除',
- error: `未找到商品ID: ${cartData.productId}`
- });
- } else {
- console.log(`商品ID ${cartData.productId} 存在于products表中,商品名称: ${product.productName}`);
- }
-
- if (product.status === 'hidden') {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '该商品已下架,无法添加到购物车'
- });
- }
-
- // 在testMode下,不执行实际的数据库操作,直接返回成功
- if (cartData.testMode) {
- console.log('测试模式:跳过实际的数据库操作');
- res.json({
- success: true,
- code: 200,
- message: '测试模式:添加到购物车成功',
- data: {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity
- }
- });
- return;
- }
-
- // 检查是否已存在相同商品
- const existingItem = await CartItem.findOne({
- where: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
-
- // 添加try-catch捕获外键约束错误
- try {
- console.log(`准备创建/更新购物车项: userId=${cartData.userId}, productId=${cartData.productId}`);
- if (existingItem) {
- // 已存在,更新数量
- await CartItem.update(
- {
- quantity: existingItem.quantity + cartData.quantity,
- updated_at: new Date()
- },
- {
- where: {
- id: existingItem.id
- }
- }
- );
- console.log(`更新购物车项成功: id=${existingItem.id}, 新数量=${existingItem.quantity + cartData.quantity}`);
- } else {
- // 不存在,创建新购物车项
- console.log('创建新购物车项,所有字段:', {
- userId: cartData.userId,
- productId: cartData.productId,
- productName: cartData.productName,
- quantity: cartData.quantity,
- price: cartData.price,
- specification: cartData.specification,
- grossWeight: cartData.grossWeight,
- yolk: cartData.yolk
- });
- // 重要:在创建前再次验证数据完整性
- if (!cartData.userId || !cartData.productId) {
- throw new Error(`数据不完整: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- await CartItem.create({
- ...cartData,
- added_at: new Date()
- });
- console.log(`创建购物车项成功: userId=${cartData.userId}, productId=${cartData.productId}`);
- }
- } catch (createError) {
- console.error('创建/更新购物车项失败,可能是外键约束问题:', createError);
- console.error('详细错误信息:', {
- name: createError.name,
- message: createError.message,
- stack: createError.stack,
- sql: createError.sql || '无SQL信息',
- userId: cartData.userId,
- productId: cartData.productId
- });
-
- // 检测是否是外键约束错误
- if (createError.name === 'SequelizeForeignKeyConstraintError' || createError.message.includes('foreign key')) {
- // 区分是用户ID还是商品ID问题
- let errorField = 'productId';
- let errorMessage = '商品信息已更新,请刷新页面后重试';
-
- if (createError.message.includes('userId') || createError.message.includes('user') || createError.message.toLowerCase().includes('user')) {
- errorField = 'userId';
- errorMessage = '用户信息无效,请重新登录后重试';
- }
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: errorMessage,
- error: `外键约束错误: ${errorField} 不存在或已失效`,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 其他类型的错误也返回400状态码,避免500错误
- return res.status(400).json({
- success: false,
- code: 400,
- message: '添加购物车项失败,请稍后重试',
- error: createError.message,
- details: {
- userId: cartData.userId,
- productId: cartData.productId
- }
- });
- }
-
- // 更新商品的预约人数 - 更健壮的实现
- try {
- console.log(`尝试更新商品预约人数: productId=${cartData.productId}`);
-
- // 先验证商品是否存在
- const productCheck = await Product.findOne({where: {productId: cartData.productId}});
- if (productCheck) {
- // 商品存在,才进行更新
- await Product.increment('reservedCount', {by: 1, where: {productId: cartData.productId}});
- console.log(`商品预约人数更新成功: productId=${cartData.productId}, 新数量=${productCheck.reservedCount + 1}`);
- } else {
- console.error(`更新商品预约人数失败: 商品ID ${cartData.productId} 不存在`);
- }
- } catch (updateError) {
- console.error(`更新商品预约人数失败:`, updateError);
- // 继续执行,不中断主要流程
- }
-
- res.json({
- success: true,
- code: 200,
- message: '添加到购物车成功'
- });
- } catch (error) {
- console.error('添加到购物车失败:', error);
- console.error('全局错误捕获,详细信息:', {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- });
-
- // 增强的错误处理 - 强制所有错误返回400状态码
- console.error('全局错误处理 - 捕获到未处理的错误:', error);
- const statusCode = 400; // 强制所有错误返回400状态码,避免前端显示500错误
- let errorMessage = '添加到购物车失败';
-
- // 更精确地检测外键约束错误
- if (error.name === 'SequelizeForeignKeyConstraintError' ||
- error.message.toLowerCase().includes('foreign key') ||
- error.message.toLowerCase().includes('constraint fails') ||
- error.message.toLowerCase().includes('constraint')) {
- errorMessage = '添加到购物车失败:商品或用户信息已更新,请刷新页面后重试';
- console.error('检测到外键约束相关错误,返回400状态码');
- }
-
- console.log(`准备返回错误响应 - 状态码: ${statusCode}, 消息: ${errorMessage}`);
-
- // 确保响应能够正确发送
- try {
- res.status(statusCode).json({
- success: false,
- code: statusCode,
- message: errorMessage,
- error: error.message,
- errorDetails: {
- name: error.name,
- message: error.message,
- stack: error.stack,
- sql: error.sql || '无SQL信息'
- }
- });
- } catch (resError) {
- console.error('发送错误响应失败:', resError);
- // 即使发送响应失败,也尝试以文本格式发送
- try {
- res.status(400).send('添加到购物车失败,请刷新页面后重试');
- } catch (finalError) {
- console.error('无法发送任何响应:', finalError);
- }
- }
- }
-});
-
-// 获取购物车信息
-app.post('/api/cart/get', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 查询购物车信息 - 排除关联商品为hidden或sold_out状态的项
- const cartItems = await CartItem.findAll({
- where: { userId },
- include: [
- {
- model: Product,
- as: 'product',
- attributes: ['productName', 'price', 'quantity', 'status', 'specification', 'grossWeight', 'yolk'],
- where: {
- status: { [Sequelize.Op.notIn]: ['hidden', 'sold_out'] }
- }
- }
- ],
- order: [['added_at', 'DESC']]
- });
-
- res.json({
- success: true,
- code: 200,
- message: '获取购物车信息成功',
- data: {
- cartItems
- }
- });
- } catch (error) {
- console.error('获取购物车信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取购物车信息失败',
- error: error.message
- });
- }
-});
-
-// 更新购物车项
-app.post('/api/cart/update', async (req, res) => {
- try {
- const { id, quantity, selected } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 构建更新数据
- const updateData = {};
- if (quantity !== undefined) updateData.quantity = quantity;
- if (selected !== undefined) updateData.selected = selected;
- updateData.updated_at = new Date();
-
- // 更新购物车项
- await CartItem.update(updateData, {
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '更新购物车成功'
- });
- } catch (error) {
- console.error('更新购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '更新购物车失败',
- error: error.message
- });
- }
-});
-
-// 删除购物车项
-app.post('/api/cart/delete', async (req, res) => {
- try {
- const { id } = req.body;
-
- if (!id) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少id参数'
- });
- }
-
- // 删除购物车项
- await CartItem.destroy({
- where: { id }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '删除购物车项成功'
- });
- } catch (error) {
- console.error('删除购物车项失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '删除购物车项失败',
- error: error.message
- });
- }
-});
-
-// 清空购物车
-app.post('/api/cart/clear', async (req, res) => {
- try {
- const { userId } = req.body;
-
- if (!userId) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少userId参数'
- });
- }
-
- // 清空购物车
- await CartItem.destroy({
- where: { userId }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '清空购物车成功'
- });
- } catch (error) {
- console.error('清空购物车失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '清空购物车失败',
- error: error.message
- });
- }
-});
-
-// 测试连接接口
-app.get('/api/test-connection', async (req, res) => {
- try {
- // 检查数据库连接
- await sequelize.authenticate();
-
- res.json({
- success: true,
- code: 200,
- message: '服务器连接成功,数据库可用',
- timestamp: new Date().toISOString(),
- serverInfo: {
- port: PORT,
- nodeVersion: process.version,
- database: 'MySQL',
- status: 'running'
- }
- });
- } catch (error) {
- res.status(500).json({
- success: false,
- code: 500,
- message: '服务器连接失败',
- error: error.message
- });
- }
-});
-
-// 用户类型调试接口 - 增强版:用于排查用户类型和商品显示问题
-app.post('/api/user/debug', async (req, res) => {
- try {
- const { openid } = req.body;
-
- console.log('收到用户调试请求,openid:', openid);
-
- if (!openid) {
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少openid参数'
- });
- }
-
- // 查询用户信息
- const user = await User.findOne({
- where: { openid },
- attributes: ['openid', 'userId', 'nickName', 'phoneNumber', 'type']
- });
-
- if (!user) {
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在',
- debugInfo: {
- searchCriteria: { openid },
- timestamp: new Date().toISOString()
- }
- });
- }
-
- // 查询该用户的商品统计信息
- const totalProducts = await Product.count({ where: { sellerId: user.userId } });
- const pendingProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'pending_review'
- }
- });
- const reviewedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'reviewed'
- }
- });
- const publishedProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'published'
- }
- });
- const soldOutProducts = await Product.count({
- where: {
- sellerId: user.userId,
- status: 'sold_out'
- }
- });
-
- // 判断用户是否有权限查看所有商品
- const canViewAllProducts = ['seller', 'both', 'admin'].includes(user.type);
-
- // 获取该用户的最新5个商品信息(用于调试)
- const latestProducts = await Product.findAll({
- where: { sellerId: user.userId },
- limit: 5,
- order: [['created_at', 'DESC']],
- attributes: ['productId', 'productName', 'status', 'created_at']
- });
-
- const responseData = {
- success: true,
- code: 200,
- message: '获取用户调试信息成功',
- userInfo: user,
- productStats: {
- total: totalProducts,
- pendingReview: pendingProducts,
- reviewed: reviewedProducts,
- published: publishedProducts,
- soldOut: soldOutProducts
- },
- permissionInfo: {
- canViewAllProducts: canViewAllProducts,
- userType: user.type,
- allowedTypesForViewingAllProducts: ['seller', 'both', 'admin']
- },
- latestProducts: latestProducts,
- debugInfo: {
- userCount: await User.count(),
- totalProductsInSystem: await Product.count(),
- timestamp: new Date().toISOString(),
- serverTime: new Date().toLocaleString('zh-CN')
- }
- };
-
- console.log('调试信息返回数据:', JSON.stringify(responseData, null, 2).substring(0, 500) + '...');
- res.json(responseData);
- } catch (error) {
- console.error('获取用户调试信息失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '获取用户调试信息失败',
- error: error.message,
- debugInfo: {
- errorStack: error.stack,
- timestamp: new Date().toISOString()
- }
- });
- }
-});
-
-// 下架商品接口 - 将商品状态设置为sold_out表示已下架
-app.post('/api/product/hide', async (req, res) => {
- console.log('收到下架商品请求:', req.body);
-
- try {
- const { openid, productId } = req.body;
-
- // 验证请求参数
- if (!openid || !productId) {
- console.error('下架商品失败: 缺少必要参数');
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要参数: openid和productId都是必需的'
- });
- }
-
- // 查找用户
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('下架商品失败: 用户不存在');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在'
- });
- }
-
- console.log('找到用户信息:', { userId: user.userId, nickName: user.nickName });
-
- // 查找商品并验证所有权 - 直接使用userId,因为商品创建时使用的就是userId
- const product = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
-
- if (!product) {
- console.error('下架商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 记录当前状态,用于调试
- console.log('当前商品状态:', product.status, '允许的状态列表:', Product.rawAttributes.status.validate.isIn);
- console.log('商品所属卖家ID:', product.sellerId);
- console.log('用户ID信息对比:', { userId: user.userId, id: user.id });
-
- console.log('准备更新商品状态为sold_out,当前状态:', product.status);
-
- // 更新商品状态为已下架(sold_out) - 尝试多种更新方式确保成功
- try {
- // 方法1: 直接保存实例
- product.status = 'sold_out';
- product.updated_at = new Date();
- await product.save();
- console.log('商品下架成功(使用save方法):', { productId: product.productId, newStatus: product.status });
- } catch (saveError) {
- console.error('使用save方法更新失败,尝试使用update方法:', saveError);
-
- try {
- // 方法2: 使用update方法
- const updateResult = await Product.update(
- { status: 'sold_out', updated_at: new Date() },
- { where: { productId: productId, sellerId: user.userId } }
- );
- console.log('商品下架成功(使用update方法):', { productId: productId, sellerIdType: typeof user.userId, updateResult });
- } catch (updateError) {
- console.error('使用update方法也失败:', updateError);
-
- try {
- // 方法3: 直接执行SQL语句绕过ORM验证
- const replacements = {
- status: 'sold_out',
- updatedAt: new Date(),
- productId: productId,
- sellerId: user.userId
- };
-
- await sequelize.query(
- 'UPDATE products SET status = :status, updated_at = :updatedAt WHERE productId = :productId AND sellerId = :sellerId',
- {
- replacements: replacements
- }
- );
- console.log('商品下架成功(使用原始SQL):', { productId: product.productId, productName: product.productName });
- } catch (sqlError) {
- console.error('使用原始SQL也失败:', sqlError);
- throw new Error('所有更新方法都失败: ' + sqlError.message);
- }
- }
- }
-
- // 重新查询商品以确保返回最新状态
- const updatedProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: product.sellerId // 使用找到的商品的sellerId进行查询
- }
- });
-
- res.json({
- success: true,
- code: 200,
- message: '商品下架成功',
- product: {
- productId: updatedProduct.productId,
- productName: updatedProduct.productName,
- status: updatedProduct.status
- }
- });
- } catch (error) {
- console.error('下架商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '下架商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 发布商品API
-app.post('/api/product/publish', async (req, res) => {
- console.log('收到发布商品请求:', req.body); // 记录完整请求体
-
- try {
- const { openid, product } = req.body;
-
- // 验证必填字段
- console.log('验证请求参数: openid=', !!openid, ', product=', !!product);
- if (!openid || !product) {
- console.error('缺少必要参数: openid=', openid, 'product=', product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid或product对象)'
- });
- }
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price, '转换为数字=', parseFloat(product.price));
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity, '转换为数字=', parseInt(product.quantity));
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误和字段值详情
- const validationErrors = [];
- const fieldDetails = {};
-
- // 检查商品名称
- fieldDetails.productName = {
- value: product.productName,
- type: typeof product.productName,
- isEmpty: !product.productName || product.productName.trim() === ''
- };
- if (fieldDetails.productName.isEmpty) {
- console.error('商品名称为空');
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- }
-
- // 检查价格
- fieldDetails.price = {
- value: product.price,
- type: typeof product.price,
- isNumber: !isNaN(parseFloat(product.price)) && isFinite(product.price),
- parsedValue: parseFloat(product.price),
- isValid: !isNaN(parseFloat(product.price)) && isFinite(product.price) && parseFloat(product.price) > 0
- };
- if (!product.price) {
- console.error('价格为空');
- validationErrors.push('价格为必填项');
- } else if (!fieldDetails.price.isNumber) {
- console.error('价格不是有效数字: price=', product.price);
- validationErrors.push('价格必须是有效数字格式');
- } else if (fieldDetails.price.parsedValue <= 0) {
- console.error('价格小于等于0: price=', product.price, '转换为数字后=', fieldDetails.price.parsedValue);
- validationErrors.push('价格必须大于0');
- }
-
- // 检查数量
- fieldDetails.quantity = {
- value: product.quantity,
- type: typeof product.quantity,
- isNumeric: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity),
- parsedValue: Math.floor(parseFloat(product.quantity)),
- isValid: !isNaN(parseFloat(product.quantity)) && isFinite(product.quantity) && parseFloat(product.quantity) > 0
- };
- if (!product.quantity) {
- console.error('数量为空');
- validationErrors.push('数量为必填项');
- } else if (!fieldDetails.quantity.isNumeric) {
- console.error('数量不是有效数字: quantity=', product.quantity);
- validationErrors.push('数量必须是有效数字格式');
- } else if (fieldDetails.quantity.parsedValue <= 0) {
- console.error('数量小于等于0: quantity=', product.quantity, '转换为数字后=', fieldDetails.quantity.parsedValue);
- validationErrors.push('数量必须大于0');
- }
-
- // 改进的毛重字段处理逻辑 - 与其他API保持一致
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('发布商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保商品名称不超过数据库字段长度限制
- if (product.productName && product.productName.length > 255) {
- console.error('商品名称过长: 长度=', product.productName.length);
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 如果有验证错误,一次性返回所有错误信息和字段详情
- if (validationErrors.length > 0) {
- console.error('验证失败 - 详细信息:', JSON.stringify({
- errors: validationErrors,
- fieldDetails: fieldDetails
- }, null, 2));
-
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors,
- detailedMessage: validationErrors.join('; '),
- fieldDetails: fieldDetails
- });
- }
-
- // 查找用户
- console.log('开始查找用户: openid=', openid);
- const user = await User.findOne({ where: { openid } });
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- console.log('找到用户:', { userId: user.userId, nickName: user.nickName, type: user.type });
-
- // 验证用户类型
- console.log(`验证用户类型: 用户ID=${user.userId}, 类型=${user.type}`);
- if (user.type !== 'seller' && user.type !== 'both') {
- console.error(`商品发布失败: 用户${user.userId}类型为${user.type},需要seller或both类型`);
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有卖家才能发布商品,请在个人资料中修改用户类型'
- });
- }
-
- // 生成商品ID
- const productId = `product_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
- console.log('生成商品ID:', productId);
-
- // 确保grossWeight值是数字类型并保留2位小数(与数据库decimal(10,2)类型保持一致)
- // 使用Math.round进行正确的四舍五入
- const finalGrossWeight = Math.round(grossWeightDetails.parsedValue * 100) / 100;
- console.log('发布商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 创建商品
- console.log('准备创建商品:', {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight,
- sellerId: user.userId
- });
-
- const newProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk || '',
- specification: product.specification || '',
- status: 'pending_review', // 默认状态为待审核
- created_at: new Date(),
- updated_at: new Date()
- });
-
- // 查询完整商品信息以确保返回正确的毛重值
- const createdProduct = await Product.findOne({
- where: { productId },
- include: [
- {
- model: User,
- as: 'seller',
- attributes: ['userId', 'nickName', 'avatarUrl']
- }
- ]
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- if (createdProduct) {
- console.log('发布商品 - 数据库查询后grossWeight:', createdProduct.grossWeight, '类型:', typeof createdProduct.grossWeight);
- }
-
- res.json({
- success: true,
- code: 200,
- message: '商品发布成功',
- product: createdProduct,
- productId: productId
- });
-
- } catch (error) {
- console.error('发布商品失败:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '发布商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 启动服务器
-app.listen(PORT, () => {
- console.log(`服务器运行在 http://localhost:${PORT}`);
- console.log('注意:当前服务器已添加详细日志记录,用于排查发布商品问题');
- console.log('调试API: POST /api/user/debug - 用于查看用户类型信息');
- console.log(`测试连接接口: http://localhost:${PORT}/api/test-connection`);
-});
-
-// 编辑商品API - 用于审核失败商品重新编辑
-app.post('/api/product/edit', async (req, res) => {
- console.log('收到编辑商品请求 - 详细信息:');
- console.log('- 请求路径:', req.url);
- console.log('- 请求方法:', req.method);
- console.log('- 请求完整body:', req.body);
- console.log('- 服务器端口:', PORT);
- console.log('- 环境变量:', process.env.PORT);
-
- try {
- // 正确解析请求参数,处理嵌套的productData结构
- let openid = req.body.openid;
- let productId = req.body.productId;
- let status = req.body.status;
- let testMode = req.body.testMode;
- let product = req.body.product;
-
- // 处理多层嵌套的productData结构
- if (!product && req.body.productData) {
- // 处理第一种情况: { productData: { openid, productId, product: { ... } } }
- if (req.body.productData.product) {
- product = req.body.productData.product;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- // 处理第二种情况: { productData: { openid, productId, productName, price, ... } }
- else {
- product = req.body.productData;
- openid = req.body.productData.openid || openid;
- productId = req.body.productData.productId || productId;
- status = req.body.productData.status || status;
- testMode = req.body.productData.testMode !== undefined ? req.body.productData.testMode : testMode;
- }
- }
-
- // 调试日志
- console.log('解析后参数:', { openid, productId, status, testMode, product: !!product });
-
- console.log('收到编辑商品请求,包含状态参数:', { openid, productId, status, testMode });
-
- // 验证必填字段
- if (!openid || !productId || !product) {
- console.error('缺少必要参数: openid=', !!openid, 'productId=', !!productId, 'product=', !!product);
- return res.status(400).json({
- success: false,
- code: 400,
- message: '缺少必要的参数(openid、productId或product对象)'
- });
- }
-
- // 查找用户
- let user = null;
-
- // 测试模式下的特殊处理
- if (testMode) {
- console.log('测试模式:尝试查找或创建测试用户');
- // 首先尝试查找openid为'test_openid'的用户
- user = await User.findOne({
- where: { openid: 'test_openid' }
- });
-
- if (!user) {
- // 如果不存在,创建一个新的测试用户
- console.log('测试模式:创建测试用户');
- try {
- user = await User.create({
- openid: 'test_openid',
- userId: 'test_user_id',
- nickName: '测试用户',
- type: 'seller'
- });
- } catch (createError) {
- console.error('测试模式:创建测试用户失败', createError);
- // 如果创建失败,尝试查找数据库中的第一个用户
- user = await User.findOne({
- order: [['id', 'ASC']]
- });
- if (user) {
- console.log('测试模式:使用数据库中的现有用户', user.userId);
- }
- }
- } else {
- console.log('测试模式:使用已存在的测试用户', user.userId);
- }
- } else {
- // 非测试模式:按常规方式查找用户
- user = await User.findOne({ where: { openid } });
- }
-
- if (!user) {
- console.error('用户不存在: openid=', openid);
- return res.status(404).json({
- success: false,
- code: 404,
- message: '用户不存在,请先登录'
- });
- }
-
- // 查找商品
- let existingProduct = null;
-
- if (testMode) {
- // 测试模式:如果找不到商品,尝试使用测试商品或创建一个新的测试商品
- existingProduct = await Product.findOne({
- where: {
- productId: productId
- }
- });
-
- // 如果找不到指定的商品,创建一个新的测试商品
- if (!existingProduct) {
- console.log('测试模式:创建测试商品');
- try {
- existingProduct = await Product.create({
- productId: productId,
- sellerId: user.userId,
- productName: '测试商品',
- price: 99.99,
- quantity: 100,
- grossWeight: 0, // 默认为0而不是5,符合用户需求
- yolk: '测试描述',
- specification: '测试规格',
- status: 'rejected', // 设置为可编辑状态
- created_at: new Date(),
- updated_at: new Date()
- });
- console.log('测试模式:测试商品创建成功');
- } catch (createProductError) {
- console.error('测试模式:创建测试商品失败', createProductError);
- }
- }
- } else {
- // 非测试模式:验证商品所有权
- existingProduct = await Product.findOne({
- where: {
- productId: productId,
- sellerId: user.userId
- }
- });
- }
-
- if (!existingProduct) {
- console.error('编辑商品失败: 商品不存在或不属于当前用户');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品不存在或不属于当前用户'
- });
- }
-
- // 验证商品状态是否允许编辑
- if (!['rejected', 'sold_out', 'pending_review', 'reviewed'].includes(existingProduct.status)) {
- console.error(`编辑商品失败: 商品状态(${existingProduct.status})不允许编辑`, {
- productId: productId,
- sellerId: user.userId,
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- });
- return res.status(403).json({
- success: false,
- code: 403,
- message: '只有审核失败、已下架、审核中或已审核的商品才能编辑',
- debugInfo: {
- allowedStatuses: ['rejected', 'sold_out', 'pending_review', 'reviewed'],
- actualStatus: existingProduct.status
- }
- });
- }
-
- // 记录商品编辑信息,用于调试
- console.log(`允许编辑商品: productId=${productId}, status=${existingProduct.status}, sellerId=${user.userId}`);
-
- // 详细检查每个必填字段并记录其类型和值
- console.log('商品字段详细检查:');
- console.log('- productName: 存在=', !!product.productName, '类型=', typeof product.productName, '值=', product.productName);
- console.log('- price: 存在=', !!product.price, '类型=', typeof product.price, '值=', product.price);
- console.log('- quantity: 存在=', !!product.quantity, '类型=', typeof product.quantity, '值=', product.quantity);
- console.log('- grossWeight: 存在=', !!product.grossWeight, '类型=', typeof product.grossWeight, '值=', product.grossWeight, '转换为数字=', parseFloat(product.grossWeight));
-
- // 收集所有验证错误
- const validationErrors = [];
-
- // 检查商品名称
- if (!product.productName || product.productName.trim() === '') {
- validationErrors.push('商品名称为必填项,不能为空或仅包含空格');
- } else if (product.productName.length > 255) {
- validationErrors.push('商品名称不能超过255个字符');
- }
-
- // 检查价格
- if (!product.price) {
- validationErrors.push('价格为必填项');
- } else if (isNaN(parseFloat(product.price)) || parseFloat(product.price) <= 0) {
- validationErrors.push('价格必须是大于0的有效数字');
- }
-
- // 检查数量
- if (!product.quantity) {
- validationErrors.push('数量为必填项');
- } else if (isNaN(parseInt(product.quantity)) || parseInt(product.quantity) <= 0) {
- validationErrors.push('数量必须是大于0的有效数字');
- }
-
- // 改进的毛重字段处理逻辑,与其他API保持一致,空值默认设为0
- const grossWeightDetails = {
- value: product.grossWeight,
- type: typeof product.grossWeight,
- isEmpty: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined,
- isNumeric: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined || !isNaN(parseFloat(product.grossWeight)) && isFinite(product.grossWeight),
- parsedValue: product.grossWeight === '' || product.grossWeight === null || product.grossWeight === undefined ? 0 : parseFloat(product.grossWeight)
- };
-
- // 详细的日志记录
- console.log('编辑商品 - 毛重字段详细分析:');
- console.log('- 原始值:', product.grossWeight, '类型:', typeof product.grossWeight);
- console.log('- 是否为空值:', grossWeightDetails.isEmpty);
- console.log('- 是否为有效数字:', grossWeightDetails.isNumeric);
- console.log('- 转换后的值:', grossWeightDetails.parsedValue, '类型:', typeof grossWeightDetails.parsedValue);
-
- // 验证毛重值
- if (!grossWeightDetails.isEmpty && !grossWeightDetails.isNumeric) {
- console.error('毛重不是有效数字: grossWeight=', product.grossWeight);
- validationErrors.push('毛重必须是有效数字格式');
- }
-
- // 确保grossWeight值是数字类型
- const finalGrossWeight = Number(grossWeightDetails.parsedValue);
- console.log('编辑商品 - 最终存储的毛重值:', finalGrossWeight, '类型:', typeof finalGrossWeight);
-
- // 如果有验证错误,返回错误信息
- if (validationErrors.length > 0) {
- console.error('验证失败 - 错误:', validationErrors.join('; '));
- return res.status(400).json({
- success: false,
- code: 400,
- message: '请填写完整信息',
- errors: validationErrors
- });
- }
-
- // 准备更新的商品数据
- const updatedProductData = {
- productName: product.productName,
- price: product.price,
- quantity: product.quantity,
- grossWeight: finalGrossWeight, // 使用最终转换的数字值
- yolk: product.yolk,
- specification: product.specification,
- // 优先使用前端传递的status参数,如果没有传递则使用原来的逻辑
- status: status && ['pending_review', 'published'].includes(status) ? status :
- (product.resubmit && ['rejected', 'sold_out'].includes(existingProduct.status)) ? 'pending_review' : existingProduct.status,
- rejectReason: (status === 'pending_review' || (product.resubmit && existingProduct.status === 'rejected')) ? null : existingProduct.rejectReason, // 提交审核时清除拒绝原因
- updated_at: new Date()
- };
-
- console.log('准备更新商品数据:', { productId, updatedStatus: updatedProductData.status, fromStatus: existingProduct.status });
-
- // 更新商品
- const [updatedCount] = await Product.update(updatedProductData, {
- where: testMode ? {
- // 测试模式:只根据productId更新
- productId: productId
- } : {
- // 非测试模式:验证商品所有权
- productId: productId,
- sellerId: user.userId
- }
- });
-
- // 检查更新是否成功
- if (updatedCount === 0) {
- console.error('商品更新失败: 没有找到匹配的商品或权限不足');
- return res.status(404).json({
- success: false,
- code: 404,
- message: '商品更新失败: 没有找到匹配的商品或权限不足'
- });
- }
-
- // 获取更新后的商品信息
- const updatedProduct = await Product.findOne({ where: { productId: productId } });
-
- console.log('查询数据库后 - 更新的商品信息:', {
- grossWeight: updatedProduct?.grossWeight,
- grossWeightType: typeof updatedProduct?.grossWeight,
- productId: updatedProduct?.productId,
- status: updatedProduct?.status
- });
-
- // 确保返回给前端的grossWeight是正确的数字值
- // 注意:这里检查undefined和null,并且对于空字符串或5的情况也进行处理
- if (updatedProduct) {
- console.log('处理前 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
-
- // 如果grossWeight是undefined、null或空字符串,设置为0
- if (updatedProduct.grossWeight === undefined || updatedProduct.grossWeight === null || updatedProduct.grossWeight === '') {
- updatedProduct.grossWeight = 0;
- console.log('检测到空值 - 已设置为0');
- } else {
- // 否则转换为浮点数
- updatedProduct.grossWeight = parseFloat(updatedProduct.grossWeight);
- }
-
- console.log('处理后 - grossWeight:', updatedProduct.grossWeight, '类型:', typeof updatedProduct.grossWeight);
- }
-
- console.log('商品编辑成功:', {
- productId: productId,
- productName: product.productName,
- oldStatus: existingProduct.status, // 记录更新前的状态
- newStatus: updatedProduct.status, // 记录更新后的状态
- grossWeight: updatedProduct.grossWeight // 记录处理后的毛重值
- });
-
- // 根据新的状态生成适当的返回消息
- let returnMessage = '';
- if (updatedProduct.status === 'pending_review') {
- returnMessage = '商品编辑成功,已重新提交审核';
- } else if (updatedProduct.status === 'published') {
- returnMessage = '商品编辑成功,已上架';
- } else if (updatedProduct.status === existingProduct.status) {
- returnMessage = '商品编辑成功,状态保持不变';
- } else {
- returnMessage = '商品编辑成功';
- }
-
- res.json({
- success: true,
- code: 200,
- message: returnMessage,
- product: updatedProduct
- });
- } catch (error) {
- console.error('编辑商品过程发生异常:', error);
- res.status(500).json({
- success: false,
- code: 500,
- message: '编辑商品失败: ' + error.message,
- error: error.message
- });
- }
-});
-
-// 导出模型和Express应用供其他模块使用
-module.exports = {
- User,
- Product,
- CartItem,
- sequelize,
- createUserAssociations,
- app,
- PORT
-};
\ No newline at end of file
diff --git a/server-example/simple-fix.js b/server-example/simple-fix.js
deleted file mode 100644
index c0227ca..0000000
--- a/server-example/simple-fix.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/simple-gross-weight-check.js b/server-example/simple-gross-weight-check.js
deleted file mode 100644
index db5a31b..0000000
--- a/server-example/simple-gross-weight-check.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/simple-gross-weight-verification.js b/server-example/simple-gross-weight-verification.js
deleted file mode 100644
index ea00ec6..0000000
--- a/server-example/simple-gross-weight-verification.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/simple-port-check.js b/server-example/simple-port-check.js
deleted file mode 100644
index 02a8555..0000000
--- a/server-example/simple-port-check.js
+++ /dev/null
@@ -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检查完成。');
-});
\ No newline at end of file
diff --git a/server-example/simple_verify.js b/server-example/simple_verify.js
deleted file mode 100644
index d2b3d47..0000000
--- a/server-example/simple_verify.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/sync-review-status.js b/server-example/sync-review-status.js
deleted file mode 100644
index e430c62..0000000
--- a/server-example/sync-review-status.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/test-new-auth-mechanism.js b/server-example/test-new-auth-mechanism.js
deleted file mode 100644
index 29f5b8e..0000000
--- a/server-example/test-new-auth-mechanism.js
+++ /dev/null
@@ -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();
diff --git a/server-example/test-post.js b/server-example/test-post.js
deleted file mode 100644
index c824fe6..0000000
--- a/server-example/test-post.js
+++ /dev/null
@@ -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();
diff --git a/server-example/test-settlement.js b/server-example/test-settlement.js
deleted file mode 100644
index 1811357..0000000
--- a/server-example/test-settlement.js
+++ /dev/null
@@ -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();
diff --git a/server-example/test-type-sync-fix.js b/server-example/test-type-sync-fix.js
deleted file mode 100644
index ed27cb3..0000000
--- a/server-example/test-type-sync-fix.js
+++ /dev/null
@@ -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();
diff --git a/server-example/test-user-auth-validation.js b/server-example/test-user-auth-validation.js
deleted file mode 100644
index 09fd256..0000000
--- a/server-example/test-user-auth-validation.js
+++ /dev/null
@@ -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();
- });
diff --git a/server-example/update-product-contacts.js b/server-example/update-product-contacts.js
deleted file mode 100644
index a238432..0000000
--- a/server-example/update-product-contacts.js
+++ /dev/null
@@ -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();
-}
diff --git a/server-example/update-product-contacts.zip b/server-example/update-product-contacts.zip
deleted file mode 100644
index 88028e4..0000000
Binary files a/server-example/update-product-contacts.zip and /dev/null differ
diff --git a/server-example/update-product-review-status.js b/server-example/update-product-review-status.js
deleted file mode 100644
index 3a44624..0000000
--- a/server-example/update-product-review-status.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/server-example/user-association-auto-fix.js b/server-example/user-association-auto-fix.js
deleted file mode 100644
index ebfde43..0000000
--- a/server-example/user-association-auto-fix.js
+++ /dev/null
@@ -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();
-}
\ No newline at end of file
diff --git a/server-example/user-association-patch.js b/server-example/user-association-patch.js
deleted file mode 100644
index aebdcc6..0000000
--- a/server-example/user-association-patch.js
+++ /dev/null
@@ -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);
\ No newline at end of file
diff --git a/server-example/view-users-table-structure.js b/server-example/view-users-table-structure.js
deleted file mode 100644
index 283b402..0000000
--- a/server-example/view-users-table-structure.js
+++ /dev/null
@@ -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();
\ No newline at end of file
diff --git a/update.sh b/update.sh
new file mode 100644
index 0000000..3214120
--- /dev/null
+++ b/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"