impara/OrderAuditor

GitHub: impara/OrderAuditor

一个基于 Shopify 与 PostgreSQL 的重复订单检测应用,通过规则匹配自动标记并协助商家审核重复交易。

Stars: 0 | Forks: 0

# Duplicate Guard - Shopify 重复订单检测应用 ## 概述 Duplicate Guard 是一个 Shopify 应用,它使用可配置的检测规则自动检测并标记来自同一客户的重复订单。它通过 Shopify webhook 处理订单,基于电子邮件、发货地址和其他标准分析重复项,并自动在 Shopify 中为商家审核标记可疑订单。 ## 当前状态 **状态**: MVP 完成 & 生产就绪 **最后更新**: 2025年11月22日 ### 已完成功能 - ✅ 支持订单创建事件的 Shopify webhook 监听端点(带 HMAC 验证) - ✅ **通过 Shopify 管理 API 自动注册 webhook** - ✅ 基于客户电子邮件、发货地址和可配置时间窗口的重复检测逻辑 - ✅ **检测设置的自动初始化** - webhook 首次触发时自动创建设置 - ✅ 通过 Shopify 管理 API 自动标记订单以标识重复项 - ✅ 仪表板展示已标记订单列表,显示客户信息、订单详情和重复匹配理由 - ✅ **实时仪表板更新** - 每 30 秒自动刷新以显示最新的已标记订单 - ✅ **订单详情弹窗** - 查看包含重复检测元数据、客户详情、发货地址以及直接链接到 Shopify 管理后台的完整订单信息 - ✅ **增强的客户名称提取** - 当客户字段不可用时回退到发货地址字段 - ✅ **订单解决系统** - 从仪表板忽略已标记订单,或在 Shopify 中移除标签时自动同步 - ✅ 统计卡片展示总已标记订单数、潜在重复价值以及最近活动指标 - ✅ 设置页面用于配置检测规则(时间窗口、匹配条件)和通知偏好 - ✅ PostgreSQL 数据库存储订单数据、检测规则和审计历史 - ✅ 分离路由、服务和存储层的 MVC 架构 ## 本地开发环境搭建 ### 前置条件 - Node.js(推荐 v20 或更高版本) - PostgreSQL 数据库(本地或云托管) - Shopify 商店并拥有管理访问权限 - npm 或 yarn 包管理器 ### 步骤 1:克隆并安装依赖 ``` # 导航到项目目录 cd duplicate-guard # 安装依赖 npm install ``` **注意**:如果在安装过程中遇到 Windows/WSL 路径问题,请尝试: - 在 WSL 环境中运行 npm(而非 Windows) - 使用 `npm install --no-optional` 跳过可选依赖 - 确保使用的是 WSL 的 Node.js 安装,而非 Windows 的 Node.js ### 步骤 2:设置环境变量 1. 复制示例环境文件: ``` cp .env.example .env ``` 2. 编辑 `.env` 并填写你的配置: ``` # 数据库配置 DATABASE_URL=postgresql://user:password@localhost:5432/duplicate-guard # Shopify 配置 SHOPIFY_SHOP_DOMAIN=yourstore.myshopify.com SHOPIFY_ACCESS_TOKEN=shpat_your_admin_api_access_token SHOPIFY_WEBHOOK_SECRET=shpss_your_webhook_secret_key # 应用程序配置 PORT=5000 APP_URL=http://localhost:5000 LOG_LEVEL=debug # 客户端配置(可选) VITE_SHOPIFY_SHOP_DOMAIN=yourstore.myshopify.com ``` #### 获取 Shopify 凭证 1. **在 Shopify 中创建自定义应用**: - 进入 Shopify 管理后台 → 设置 → 应用和销售渠道 → 开发应用 - 点击“创建应用” - 输入应用名称(例如:“Duplicate Guard”) 2. **配置 API 权限范围**: - 点击“配置管理 API 权限” - 启用以下权限范围: - `read_orders` - 用于接收订单 webhook - `write_orders` - 用于将订单标记为重复 - `read_customers` - **必需**,用于在 webhook 中访问客户电子邮件/名称 - 保存配置 3. **启用受保护的客户数据访问**(**至关重要**): **⚠️ 没有此设置,webhook 中将无法获取客户数据(电子邮件、姓名、电话)!** **⚠️ 计划要求**:访问客户 PII(电子邮件、姓名、电话)需要 **Shopify**、**高级** 或 **专业** 计划。**基础版/免费计划不提供此功能**。 - 在应用设置中,进入 **API 访问** → **受保护的客户数据访问** - 点击 **管理** - 在 **受保护的客户字段(可选)** 中选择: - ✅ `email` - ✅ `first_name` - ✅ `last_name` - ✅ `phone` - 提供理由:“重复订单检测需要客户电子邮件和姓名来识别重复订单” - 点击 **保存** - **商家必须批准此请求** - 他们将在 Shopify 管理后台看到一条通知 **注意**: - 在受保护客户数据访问获得批准之前,应用将显示“未知客户”和“unknown@example.com” - 如果你在基础版/免费计划上,即使启用了受保护客户数据访问,客户数据也无法获取 - 开发商店可能有不同的限制,请检查你的 Shopify 计划 4. **安装应用**: - 点击“安装应用” - 复制 **管理 API 访问令牌**(以 `shpat_` 开头) - 将其作为 `SHOPIFY_ACCESS_TOKEN` 使用 5. **获取 Webhook 密钥**: - 在应用设置中,进入“API 凭证” - 复制 **API 密钥**(以 `shpss_` 开头) - 将其作为 `SHOPIFY_WEBHOOK_SECRET` 使用 6. **设置店铺域名**: - 店铺域名格式为:`yourstore.myshopify.com` - 将其作为 `SHOPIFY_SHOP_DOMAIN` 使用 ### 步骤 3:设置数据库 #### 选项 A:使用 Docker Compose(推荐) 1. **使用 Docker Compose 启动 PostgreSQL**: docker-compose up -d 这将启动一个 PostgreSQL 16 容器,包含: - 数据库:`duplicate-guard` - 用户:`duplicate-guard` - 密码:`duplicate-guard` - 端口:`5432` 2. **在 `.env` 中更新 DATABASE_URL**: DATABASE_URL=postgresql://duplicate-guard:duplicate-guard@localhost:5432/duplicate-guard 3. **推送数据库架构**: npm run db:push 这将创建必要的表: - `orders` - 存储来自 Shopify webhook 的订单数据 - `detection_settings` - 重复检测规则配置 - `audit_logs` - 记录所有重复检测事件 **有用的 Docker 命令**: - 停止数据库:`docker-compose down` - 停止并移除数据:`docker-compose down -v` - 查看日志:`docker-compose logs -f postgres` - 重启:`docker-compose restart` #### 选项 B:手动安装 PostgreSQL 1. **安装 PostgreSQL**(如果尚未安装): # Ubuntu/Debian sudo apt update && sudo apt install postgresql postgresql-contrib # macOS(使用 Homebrew) brew install postgresql@16 brew services start postgresql@16 2. **创建 PostgreSQL 数据库**: # 使用 psql createdb duplicate-guard # 或使用 SQL psql -U postgres CREATE DATABASE "duplicate-guard"; 3. **在 `.env` 中更新 DATABASE_URL**: DATABASE_URL=postgresql://username:password@localhost:5432/duplicate-guard 4. **推送数据库架构**: npm run db:push ### 步骤 4:设置 Webhook 测试(本地开发) 对于本地开发,需要将本地服务器暴露到互联网,以便 Shopify 可以发送 webhook。请选择以下选项之一: #### 选项 A:使用 ngrok(推荐) 1. **安装 ngrok**:https://ngrok.com/download 2. **启动开发服务器**: npm run dev 3. **在另一个终端中启动 ngrok**: ngrok http 5000 4. **更新 `.env` 文件**,使用 ngrok URL: APP_URL=https://your-ngrok-url.ngrok.io 5. **注册 webhook**(请参见步骤 5) #### 选项 B:使用其他隧道服务 按照隧道服务的说明将端口 5000 暴露出来,然后在 `.env` 中更新 `APP_URL`。 ### 步骤 5:注册 Shopify Webhook 本地服务器通过公共 URL 可访问后: 1. **启动开发服务器**: npm run dev 2. **自动注册 webhook**: curl -X POST http://localhost:5000/api/webhooks/register 或使用 webhook 状态端点检查注册状态: curl http://localhost:5000/api/webhooks/status 3. **验证 webhook 注册**: - 响应应显示 `orders/create` 和 `orders/updated` webhook 已成功注册 - 你也可以在 Shopify 管理后台 → 设置 → 通知 → Webhook 中检查 - 两个 webhook 都需要:`orders/create` 用于重复检测,`orders/updated` 用于自动解决 ### 步骤 :运行应用 ``` # 开发模式(热重载) npm run dev # 生产构建 npm run build npm start ``` 应用将在 `http://localhost:5000` 处可用 ## 项目架构 ### 技术栈 - **前端**:React + TypeScript,使用 Wouter 路由 - **UI 组件**:Shadcn UI(灵感来自 Shopify Polaris 设计) - **样式**:Tailwind CSS 与 Inter 字体 - **后端**:Node.js + Express + TypeScript - **数据库**:PostgreSQL 与 Drizzle ORM - **外部 API**:Shopify 管理 API 用于订单标记 ### 目录结构 ``` ├── client/ │ ├── src/ │ │ ├── components/ui/ # Reusable UI components (Shadcn) │ │ ├── pages/ # Page components (Dashboard, Settings) │ │ ├── lib/ # Utilities (React Query client, utils) │ │ └── App.tsx # Main app with routing ├── server/ │ ├── services/ # Business logic services │ │ ├── duplicate-detection.service.ts │ │ └── shopify.service.ts │ ├── db.ts # Database connection │ ├── storage.ts # Data access layer │ └── routes.ts # API routes └── shared/ └── schema.ts # Shared TypeScript types and Drizzle schema ``` ## API 端点 ### 仪表板 - `GET /api/dashboard/stats` - 获取仪表板统计数据 - `GET /api/orders/flagged` - 获取已标记订单列表 - `POST /api/orders/:orderId/dismiss` - 忽略已标记订单(从列表移除并移除 Shopify 标签) ### 设置 - `GET /api/settings` - 获取检测设置(如果不存在则自动初始化) - `PATCH /api/settings` - 更新检测设置 ### Webhook 管理 - `GET /api/webhooks/status` - 检查 webhook 注册状态(显示 `orders/create` 和 `orders/updated`) - `POST /api/webhooks/register` - 自动注册 `orders/create` 和 `orders/updated` webhook ### Webhook - `POST /api/webhooks/shopify/orders/create` - Shopify 订单创建 webhook - `POST /api/webhooks/shopify/orders/updated` - Shopify 订单更新 webhook(检测标签移除以实现自动解决) ## 重复检测逻辑 ### 匹配标准 系统根据以下条件计算置信度分数(0-100 分): - **电子邮件匹配**(50 分):相同的客户电子邮件 - **电话匹配**(50 分):相同的客户电话号码(支持格式差异标准化) - **地址匹配**(45 或 25 分):相似的发货地址 - 完整匹配(街道 + 城市 + 邮编):45 分 - 部分匹配(街道 + 城市 或 街道 + 邮编):25 分 - 缺失地址自动跳过(数字产品无惩罚) - **名称匹配**(20 分):相同的客户名称(不区分大小写,仅作证据支持) ### 计分策略 - **透明计分**:每种匹配类型使用固定分值,无隐藏加成或特殊情况 - **70 分阈值**:订单需达到 70 分或以上才会被标记为重复 - **自动处理**:缺失数据(如数字产品的地址)自动跳过 - **电话标准化**:电话号码会标准化以处理格式差异(例如 `+1234567890` 与 `(123) 456-7890`) - **示例**: - 电子邮件 + 名称:50 + 20 = 70 分 → 标记 ✓ - 电话 + 名称:50 + 20 = 70 分 → 标记 ✓ - 地址 + 名称:45 + 20 = 65 分 → 不标记(低于阈值) - 仅电子邮件:50 分 → 不标记(需要名称匹配达到 70 分) ### 阈值 订单置信度 ≥ 70 分时会被标记为重复 ## 订单解决与忽略 订单被标记为重复后,商家可通过两种方式解决: ### 手动忽略(仪表板) 1. 在仪表板中点击“查看详情” 2. 点击“忽略订单”按钮 3. 确认忽略对话框 4. 订单将从已标记列表移除,且 Shopify 中的 “Merge_Review_Candidate” 标签会被移除 ### 自动解决(Shopify 管理后台) 1. 商家在 Shopify 管理后台直接移除 “Merge_Review_Candidate” 标签 2. 系统通过 `orders/updated` webhook 自动检测到标签移除 3. 订单自动解决并从已标记列表移除 4. 所有解决操作都会记录在审计日志中以便追溯 ### 解决追踪 - 所有已解决订单在数据库中保留,`isFlagged: false` - `resolvedAt` 时间戳记录解决时间 - `resolvedBy` 字段记录解决方式:`'manual_dashboard'` 或 `'shopify_tag_removed'` - 审计日志记录所有忽略和解决事件,用于合规和统计分析 ## 电子邮件通知 应用可在检测到重复订单时发送电子邮件通知。要启用此功能: ### 1. 配置 SMTP 设置 在 `.env` 文件中添加 SMTP 配置: ``` # SMTP 配置 SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password SMTP_FROM=your-email@gmail.com ``` ### 2. 在设置中启用通知 1. 进入仪表板的设置页面 2. 启用“通知”开关 3. 输入接收通知的电子邮件地址 4. 可选调整通知阈值(默认:80% 置信度) 5. 保存设置 ### 3. 常用 SMTP 提供商 **Gmail:** - `SMTP_HOST=smtp.gmail.com` - `SMTP_PORT=587`(TLS)或 `465`(SSL) - 使用 [应用专用密码](https://support.google.com/accounts/answer/185833) 而非常规密码 - `SMTP_FROM` 应与 `SMTP_USER` 一致 **SendGrid:** - `SMTP_HOST=smtp.sendgrid.net` - `SMTP_PORT=587` - `SMTP_USER=apikey` - `SMTP_PASS=your-sendgrid-api-key` - `SMTP_FROM=your-verified-sender@example.com` **Mailgun:** - `SMTP_HOST=smtp.mailgun.org` - `SMTP_PORT=587` - `SMTP_USER=postmaster@your-domain.mailgun.org` - `SMTP_PASS=your-mailgun-password` - `SMTP_FROM=noreply@your-domain.com` **注意**:生产环境部署请参阅详细的 [生产部署指南](./docs/deployment/production.md) 以获取完整的 SMTP 配置说明。 ## 环境变量参考 | 变量 | 必需 | 描述 | 示例 | |------|------|------|------| | `DATABASE_URL` | 是 | PostgreSQL 连接字符串 | `postgresql://user:pass@localhost:5432/db` | | `SHOPIFY_API_KEY` | 是 | Shopify 客户端 ID(合作伙伴应用) | `your_client_id` | | `SHOPIFY_API_SECRET` | 是 | Shopify 客户端密钥(合作伙伴应用) | `your_client_secret` | | `SHOPIFY_WEBHOOK_SECRET` | 否\*\*\* | 旧版 webhook 密钥(自定义应用) | `shpss_...`(旧版,合作伙伴应用不需要) | | `PORT` | 否 | 服务器端口(默认:5000) | `5000` | | `APP_URL` | 是\* | webhook 注册的公共 URL | `http://localhost:5000` 或 `https://your-domain.com` | | `LOG_LEVEL` | 否 | 日志详细程度(默认:debug) | `error`、`warn`、`info`、`debug` | | `SMTP_HOST` | 否\*\* | SMTP 服务器主机名 | `smtp.gmail.com` | | `SMTP_PORT` | 否\*\* | SMTP 服务器端口 | `587` | | `SMTP_USER` | 否\*\* | SMTP 认证用户名 | `your-email@gmail.com` | | `SMTP_PASS` | 否\*\* | SMTP 认证密码 | `your-app-password` | | `SMTP_FROM` | 否\*\* | 发件人邮箱地址 | `your-email@gmail.com` | \* webhook 注册必需。使用 ngrok 或类似隧道服务进行本地开发。 \*\* 必需用于电子邮件通知。参见 [电子邮件通知](#email-notifications) 章节。 \*\*\*** 对于合作伙伴应用(多租户嵌入式应用):`SHOPIFY_WEBHOOK_SECRET` **不需要**。应用使用 `SHOPIFY_API_SECRET`(客户端密钥)进行 webhook 验证。`SHOPIFY_WEBHOOK_SECRET` 仅用于旧版自定义应用,应移除。 ### Shopify 凭证映射 **对于合作伙伴应用(当前设置):** - **客户端 ID** → `SHOPIFY_API_KEY` 和 `VITE_SHOPIFY_API_KEY` - **客户端密钥** → `SHOPIFY_API_SECRET(用于 OAuth 和 webhook 验证) - `SHOPIFY_WEBHOOK_SECRET` → **不需要**(旧版,可移除) **对于旧版自定义应用(已弃用):** - `SHOPIFY_WEBHOOK_SECRET` 曾是独立的“API 密钥” - 这已不再用于合作伙伴应用 ## 故障排除 ### 数据库连接问题 - 确认 PostgreSQL 正在运行:`pg_isready` - 检查 `.env` 中的连接字符串格式 - 确保数据库存在:`psql -l | grep duplicate-guard` ### Webhook 未接收事件 - 确认 webhook 已注册:`GET /api/webhooks/status` - 检查 `APP_URL` 是否公开可访问 - 对于合作伙伴应用:确认 `SHOPIFY_API_SECRET` 与应用客户端密钥匹配 - 对于旧版自定义应用:确认 `SHOPIFY_WEBHOOK_SECRET` 与应用凭证匹配 - 检查服务器日志中的 HMAC 验证错误 - 检查服务器日志中的 webhook 验证错误和配置问题 ### npm 安装问题(Windows/WSL) - 确保在 WSL 环境中使用 Node.js,而非 Windows - 尝试:`npm install --no-optional` - 清除 npm 缓存:`npm cache clean --force` - 重新安装:`rm -rf node_modules package-lock.json && npm install` ## 脚本 - `npm run dev` - 启动开发服务器(热重载) - `npm run build` - 构建生产版本 - `npm start` - 启动生产服务器 - `npm run check` - TypeScript 类型检查 - `npm run db:push` - 推送数据库架构变更(开发环境) - `npm run db:generate` - 生成迁移文件(可选,用于版本控制) ## 设计系统 ### 颜色 - **主色**:绿色(#008060)- Shopify 品牌色,用于 CTA - **危险色**:红色 - 关键告警和高置信度重复 - **图表色-4**:琥珀色 - 中等置信度警告 ### 字体 - **字体**:Inter(Shopify Polaris 标准) - **页面标题**:20px 半粗 - **节标题**:16px 半粗 - **正文文本**:14px 常规 - **数据标签**:12px 中等 - **统计数字**:24px 粗体 ## 已知限制 - **仅地址匹配**:仅地址匹配(45 分)+ 名称(20 分)= 65 分,低于 70 分阈值。需启用电子邮件或电话匹配才能生效。 - **仅电子邮件/电话匹配**:仅电子邮件或电话(50 分)需要名称匹配(20 分)才能达到 70 分。仅含电子邮件/电话的订单不会被标记。 - 单检测设置配置文件(暂不支持多店铺) - 仪表板平均解决时间目前为占位值 ## 计划增强功能 - 批量操作用于审核和忽略已标记订单 - ~~检测到重复时发送电子邮件/Slack 通知~~ ✅ 已实现 - 详细的订单对比视图,显示并排重复分析 - 分析仪表板,展示趋势、模式、欺诈风险评分、解决指标和投资回报率 - OAuth 流程用于多店铺 Shopify 应用分发 ## 生产部署 使用 Docker 进行生产部署时,请参阅详细的 [生产部署指南](./docs/deployment/production.md) 以获取完整说明,包括: - 服务器设置与 Docker 安装 - 环境配置 - 数据库初始化 - Webhook 注册 - 维护与故障排除 - 备份流程 ## 许可证 MIT
标签:30 秒自动刷新, HMAC 签名验证, MITM代理, MVC 架构, PostgreSQL 数据库, SEO:Shopify 插件, SEO:订单去重, SEO:防重复下单, Shopify 应用, Webhook 监听, 商户风控, 地址匹配, 实时仪表盘, 审计日志, 客户信息提取, 时间窗口检测, 测试用例, 电商平台安全, 自动初始化, 自动化攻击, 自动标签, 计价值分析, 订单去重, 订单审核, 订单详情弹窗, 请求拦截, 通知偏好, 邮箱匹配, 配置化规则, 重复订单检测