15 KiB
商品定价服务(kfz-goods-pricing)项目交接文档
生成时间:2026-05-27 项目路径:D:\work\newProject\kfz-goods-pricing
一、项目概述
项目名称:商品定价服务(kfz-goods-pricing) 技术栈:Go 1.25 + SQLite + HTTP API + 原生 GUI(无 Vue/Wails,纯 Go) 输出可执行文件:kfz-goods-pricing.exe 核心功能:接收商品 ISBN 查询请求,通过孔网搜索 API 获取市场定价,计算最优价格并回调通知下游系统
⚠️ 重要区别:本项目与之前生成的 kfz-move-kfz / xy-verify-price / kfz-verify-price 项目完全不同——不是 Wails 桌面应用,而是纯 Go HTTP 服务 + 原生 GUI 窗口,没有前端框架,没有子进程 B 程序。
目录结构
kfz-goods-pricing/ ├── cmd/ │ ├── server/ │ │ ├── main.go # 程序入口(HTTP服务 + GUI窗口 + 定时器) │ │ └── gui_windows.go # GUI 窗口实现 ├── internal/ │ ├── config/ # 配置加载(YAML + 全局单例) │ │ └── config.go │ ├── database/ # SQLite 数据库初始化 │ │ └── db.go │ ├── handler/ # HTTP 处理器(4个文件) │ │ ├── goods_handler.go # /api/goods/query — 商品查询 │ │ ├── token_handler.go # /api/token/* — Token 管理(增删改查/启用) │ │ ├── kfz_handler.go # /api/kfz/login — 孔网登录 │ │ └── config_handler.go # /api/config/price/* — 价格配置 │ ├── model/ │ │ └── book.go # BookInfo 图书信息结构体 │ ├── repository/ # 数据访问层 │ │ ├── token_repository.go # Token CRUD + 启用状态 │ │ ├── goods_repository.go # 商品记录 CRUD + 失败计数 │ │ └── config_repository.go # kfz_config 表(永远只有 ID=1 一条) │ └── service/ │ └── goods_service.go # 核心业务逻辑(定时器/限速/爬虫/回调) ├── pkg/ # 公共库目录(当前为空) ├── config/ │ └── config.yaml # 程序配置文件 ├── data/ │ └── goods_pricing.db # SQLite 数据库文件 ├── go.mod / go.sum ├── kfz-goods-pricing.exe # 编译后的可执行文件 └── README.md
二、技术栈
| 组件 | 依赖 | 版本 | 说明 |
|---|---|---|---|
| HTTP 服务 | Go 标准库 | 内置 | |
| et/http,无框架,裸serveMux路由 | |||
| 数据库 | modernc.org/sqlite | v1.50.0 | 纯 Go SQLite,无 CGO |
| HTTP 客户端 | github.com/parnurzeal/gorequest | v0.3.0 | 孔网 API 调用 |
| 配置解析 | gopkg.in/yaml.v3 | v3.0.1 | config.yaml 读取 |
| 日志 | log | 内置 | 输出到 GUI 窗口(guiLogWriter) |
| GUI | 原生实现 | — | gui_windows.go 中的 GUI 窗口(GTK/原生混合) |
三、架构设计
┌─────────────────────────────────────────────────────────────────┐ │ kfz-goods-pricing.exe │ │ │ │ ┌────────────┐ ┌──────────────┐ ┌─────────────────┐ │ │ │ GUI 窗口 │ │ HTTP 服务 │ │ 定时器 (Timer) │ │ │ │(gui_windows)│ │(net/http) │ │ 每 N 秒执行一次 │ │ │ └────────────┘ └──────┬───────┘ └────────┬────────┘ │ │ │ │ │ │ ┌──────────────────┬┴─────────────────────┘ │ │ ▼ ▼ │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │ │ │ TokenHandler │ │GoodsHandler │ │ GoodsService │ │ │ │ /api/token/* │ │/api/goods/...│ │ (核心业务逻辑) │ │ │ └──────┬───────┘ └──────┬───────┘ └─────────┬──────────────┘ │ │ │ │ │ │ │ ┌──────▼────────────────▼────────────────────▼──────────────┐ │ │ │ Repository 层 │ │ │ │ TokenRepo │ GoodsRepo │ ConfigRepo │ │ │ └────────────────────────┬───────────────────────────────────┘ │ │ ▼ │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ SQLite (goods_pricing.db) │ │ │ kfz_token (Token管理) │ kfz_goods (商品记录) │ kfz_config │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ 外部调用: │ │ ① 上游系统 → POST /api/goods/query → 写入 kfz_goods │ │ ② 定时器 → 查询 kfz_goods(按失败次数/更新时间排序) │ │ → 爬孔网搜索API → 计算价格 → POST callbackURL │ │ ③ 回调 URL: http://192.168.101.213:9090/api/product/updatePrice│ └─────────────────────────────────────────────────────────────────┘
四、HTTP API 接口
4.1 商品查询(上游系统调用入口)
POST /api/goods/query
请求参数(form-data 或 x-www-form-urlencoded):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| isbn | string | 商品 ISBN | |
| book_name | string | 商品 名称 | |
| author | string | 商品 作者 | |
| publishing | string | 商品 出版社 | |
| out_id | string | 输出 ID(回调时透传) | |
| quality | string | 品相(100=全新,90=九品等) | |
| query_index | int | 想排第几位(默认取最后一条) | |
| user_id | string | 用户 ID(回调时透传) | |
| placeholder_down_price | float | 占位降价 | |
| min_shipping_fee | float | 最低运费 | |
| min_price | float | 最低书价 |
响应:
json {"code":200,"message":"success","id":123}
说明:只写入数据库,返回插入记录的 ID。实际核价由定时器异步完成。
4.2 Token 管理
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/token/add | 批量添加 Token(JSON数组) |
| GET | /api/token/list | 查询所有 Token |
| POST | /api/token/delete | 删除 Token(id 参数) |
| POST | /api/token/update | 修改 Token(id/username/token/is_enable) |
| GET | /api/token/enabled | 获取所有启用的 Token |
批量添加请求体:
json [{"username":"孔网账号1","token":"PHPSESSID_xxx"},{"username":"孔网账号2","token":"PHPSESSID_yyy"}]
4.3 孔网登录
POST /api/kfz/login
| 参数 | 说明 |
|---|---|
| username | 孔网用户名 |
| password | 孔网密码 |
响应:
json {"code":200,"message":"success","data":{"userId":123456,"nickname":"xxx","mobile":"138xxxx","token":"PHPSESSID_xxx"}}
4.4 价格配置
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /api/config/price/get | 获取完整配置(yaml + DB 覆盖层合并) |
| POST | /api/config/price/set | 修改价格配置(只更新入参字段) |
配置合并逻辑(GetConfigPrice): ` yaml配置(Port / TimerInterval / APIRateLimit / CallbackURL)
- DB配置(NewPrice / PlaceholderDownPrice / MinShippingFee / MinPrice / QueryIndex) = 最终配置(DB 优先覆盖 yaml 的价格字段) `
五、定时器核心逻辑(GoodsService.syncGoodsPricing)
` 每 N 秒(默认 5 秒)触发一次:
- 从 kfz_config 表读取价格参数
- 从 kfz_goods 表取一条记录(按 fail_count ASC, updated_at DESC) └→ 优先处理失败次数少的记录
- 调用 outGetAllGoods(isbn, quality, queryIndex) 爬孔网搜索 API ├→ 使用轮询方式从 kfz_token 表选一个启用 Token └→ URL: https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list
- 计算最终价格: finalPrice = totalPrice - placeholderDownPrice - minShippingFee if (finalPrice < minPrice) → finalPrice = minPrice
- 更新 kfz_goods 表(price / shipping_fee / final_price / updated_at)
- 调用 sendCallback(outID, userID, finalPrice, minShippingFee) └→ POST product_id=xxx&user_id=xxx&sale_price=xxx&cost=xxx (sale_price 和 cost 均为整数,单位:分 = 原价 × 100)
- 失败处理:MarkFailed → fail_count++,下次优先重试 `
六、数据库设计
6.1 kfz_token 表
sql CREATE TABLE kfz_token ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL, token TEXT NOT NULL, is_enable INTEGER DEFAULT 1 );
用途:存储多个孔网账号 Token,支持轮询切换,防止单个 Token 请求过快被限
6.2 kfz_goods 表
sql CREATE TABLE kfz_goods ( id INTEGER PRIMARY KEY AUTOINCREMENT, isbn TEXT NOT NULL, out_id TEXT, quality TEXT, user_id TEXT, query_index INTEGER DEFAULT 1, placeholder_down_price REAL DEFAULT 0.01, min_shipping_fee REAL DEFAULT 5.0, min_price REAL DEFAULT 1.0, price REAL DEFAULT 0, shipping_fee REAL DEFAULT 0, final_price REAL DEFAULT 0, fail_count INTEGER DEFAULT 0, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP );
用途:上游调用 QueryGoods 时写入记录,定时器按 fail_count ASC 排序依次处理
6.3 kfz_config 表
sql CREATE TABLE kfz_config ( id INTEGER PRIMARY KEY CHECK (id = 1), new_price REAL DEFAULT 0, placeholder_down_price REAL DEFAULT 0.01, min_shipping_fee REAL DEFAULT 5.0, min_price REAL DEFAULT 1.0, query_index INTEGER DEFAULT 1 );
用途:始终只有一条记录(ID=1),与 config.yaml 共同构成配置体系。yaml 负责基础配置(Port/Timer/APIRateLimit/CallbackURL),DB 负责价格参数
七、关键设计
Token 轮询机制
` tokens = GetEnabledTokens() // 从 DB 取出所有启用 Token currentTokenIndex = 0 // 原子操作的轮询索引
每次爬孔网 API 时: token = tokens[currentTokenIndex % len(tokens)] currentTokenIndex++ `
目的:多个孔网账号 Token 轮询使用,防止单个账号请求过快被限
价格计算公式
totalPrice = price + shippingFee finalPrice = totalPrice - placeholderDownPrice - minShippingFee if finalPrice < minPrice → finalPrice = minPrice
最终回调中的 sale_price 和 cost 均为整数(单位:分 = float × 100)
配置双层机制
| 来源 | 字段 | 说明 |
|---|---|---|
| config/config.yaml | Port, TimerInterval, APIRateLimit, CallbackURL | 基础运行时配置 |
| kfz_config DB | NewPrice, PlaceholderDownPrice, MinShippingFee, MinPrice, QueryIndex | 价格参数 |
yaml 不可动态修改,DB 配置可通过 API 动态修改(实时生效)。
八、第三方接口
孔网登录
| 步骤 | URL | 方法 |
|---|---|---|
| 登录 | https://login.kongfz.com/Pc/Login/account | POST (form-data) |
| 获取用户信息 | https://user.kongfz.com/User/Index/getUserInfo/ | GET (Cookie: PHPSESSID=xxx) |
孔网商品搜索
| 用途 | URL |
|---|---|
| 搜索商品 | https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list?dataType=0&page=1&sortType=7&quaSelect=2&keyword=ISBN&quality=品相~ |
回调
| 回调 URL | 参数 |
|---|---|
| http://192.168.101.213:9090/api/product/updatePrice | product_id, user_id, sale_price(分), cost(分) |
九、运行与构建
`ash
运行(开发)
go run cmd/server/main.go
编译
go build -o kfz-goods-pricing.exe ./cmd/server `
启动后:
- 加载 config/config.yaml
- 初始化 data/goods_pricing.db(SQLite,自动建表)
- 启动 HTTP 服务(默认端口 8080)
- 启动 GUI 窗口(阻塞主线程)
- 启动定时器(5秒间隔,开始首次同步)
默认配置(config/config.yaml):
yaml port: "8080" timer_interval: 5 # 每5秒执行一次定时任务 api_rate_limit: 2 # API 请求间隔2秒 callback_url: "http://192.168.101.213:9090/api/product/updatePrice"
十、交接注意事项
| 文件/目录 | 说明 | 注意点 |
|---|---|---|
| config/config.yaml | 基础配置 | Port/TimerInterval/APIRateLimit/CallbackURL |
| data/goods_pricing.db | SQLite 数据库 | 运行时自动创建,无需手动初始化 |
| internal/handler/kfz_handler.go | 孔网登录逻辑 | HTTP 直接调用孔网官网,无需 kfz.dll(与之前的 Wails 项目不同) |
| internal/service/goods_service.go | 定时器核心 | 异步处理,依赖 Token 池 |
| kfz-goods-pricing.exe | 可执行文件 | 需与 config/、data/ 目录同目录运行 |
快速排查
- 上游调用无响应:检查 HTTP 服务是否正常(默认端口 8080),检查防火墙
- 定时器无动作:检查 kfz_goods 是否有数据;检查 Token 是否为空或已过期
- 核价结果全为默认值:孔网搜索 API 无返回,kfzConfig.NewPrice 作为兜底
- 回调失败:检查 callbackURL 是否可达(192.168.101.213:9090)
- Token 失效:上游系统调用 /api/kfz/login 获取新 Token,或手动 /api/token/add 添加
与 Wails 桌面项目的核心区别
| 对比项 | 本项目(kfz-goods-pricing) | Wails 项目们 |
|---|---|---|
| 类型 | 纯 Go HTTP 服务 + GUI 窗口 | Wails 桌面应用 |
| 前端 | 无(无 Vue/HTML) | Vue 3 + Element Plus |
| 子进程 | 无 | 有(verify-price-b) |
| Token 来源 | HTTP 直接登录孔网 | kfz.dll(Windows DLL) |
| 数据库 | SQLite(本地文件) | SQLite(同) |
| 部署方式 | 后台 HTTP 服务(可远程调用) | 桌面 exe(本地运行) |