# 商品定价服务(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 秒)触发一次: 1. 从 kfz_config 表读取价格参数 2. 从 kfz_goods 表取一条记录(按 fail_count ASC, updated_at DESC) └→ 优先处理失败次数少的记录 3. 调用 outGetAllGoods(isbn, quality, queryIndex) 爬孔网搜索 API ├→ 使用轮询方式从 kfz_token 表选一个启用 Token └→ URL: https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list 4. 计算最终价格: finalPrice = totalPrice - placeholderDownPrice - minShippingFee if (finalPrice < minPrice) → finalPrice = minPrice 5. 更新 kfz_goods 表(price / shipping_fee / final_price / updated_at) 6. 调用 sendCallback(outID, userID, finalPrice, minShippingFee) └→ POST product_id=xxx&user_id=xxx&sale_price=xxx&cost=xxx (sale_price 和 cost 均为整数,单位:分 = 原价 × 100) 7. 失败处理: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 ` **启动后**: 1. 加载 config/config.yaml 2. 初始化 data/goods_pricing.db(SQLite,自动建表) 3. 启动 HTTP 服务(默认端口 8080) 4. 启动 GUI 窗口(阻塞主线程) 5. 启动定时器(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(本地运行) |