24 KiB
API Documentation
Base URL:
http://<host>:<port>
Response content type:
application/json; charset=utf-8
Authorization
所有 API 都要求固定的 Authorization 请求头。
配置文件:
auth:
secret: "your-fixed-authorization-secret"
请求示例:
Authorization: your-fixed-authorization-secret
说明:
- 服务端会把
Authorization头的原始值与auth.secret做精确匹配 - 如果你想使用
Bearer xxx风格,也可以直接把auth.secret配成完整的Bearer xxx
未授权响应:
{
"error": "unauthorized"
}
Logging
服务端会输出两级日志:
WARNINGERROR
当前会重点记录:
- 未授权访问
- 参数错误
- 数据库相关失败
- 图片查找失败
当图片不存在时,日志里会带上尝试过的完整路径,便于排查 Windows 部署目录问题。
1. Health Check
GET /healthz
用途:
- 检查 API 进程是否存活
- 检查数据库连接是否可用
成功响应:
{
"ok": true
}
2. Query Records
GET /api/v1/records
2.1 增量同步模式
不传 id_start / id_end 时,接口按当前游标做增量读取:
- 条件:
ID > cursor - 排序:
ORDER BY ID ASC - 默认
limit=100 - 默认
update_cursor=true - 如果查到数据且
update_cursor=true,游标推进到本次结果最后一条记录的ID - 返回结果会自动补上主表里的
KhID,并额外附带客户表dbo.B_Khzl的客户信息 - 返回结果会根据客户表
KHLbID自动关联地区表dbo.B_Khlb,并平铺返回area - 返回结果也会根据订单表
HpID自动关联布料表dbo.B_Hpzl,并平铺返回HpName
参数:
update_cursor: 可选,布尔值,默认truelimit: 可选,正整数,默认100
示例:
GET /api/v1/records
Authorization: your-fixed-authorization-secret
GET /api/v1/records?update_cursor=false&limit=20
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "incremental",
"cursor_before": 1200,
"cursor_after": 1250,
"origin_cursor": 1000,
"last_record_id": 1250,
"updated": true,
"count": 50,
"records": [
{
"ID": 1201,
"KhID": 88,
"BianHaoID": "...",
"area": "华东地区",
"HpName": "某某布料",
"customer": {
"KhID": 88,
"KhName": "某某客户"
}
}
]
}
字段说明:
mode: 固定为incrementalcursor_before: 本次请求开始前的游标cursor_after: 本次请求完成后的游标。如果update_cursor=false,这里会返回本次结果最后一条记录的 ID,但不会实际写入状态文件origin_cursor: 最近一次手工修改游标前的值;自动增量同步不会改它last_record_id: 本次返回结果中的最后一条记录 ID;无数据时不存在updated: 本次是否实际更新了游标状态count: 返回记录数records[].KhID: 主表记录中的客户 IDrecords[].customer: 客户表补充信息,当前默认包含KhID和KhNamerecords[].area: 客户地区,来自dbo.B_Khlb的MingChengrecords[].HpName: 布料表dbo.B_Hpzl中关联到的布料名称
2.2 范围查询模式
传了 id_start 和 id_end 时,接口进入范围查询模式:
- 条件:
ID >= id_start AND ID <= id_end - 排序:
ORDER BY ID ASC - 不会更新游标
- 必须显式传
update_cursor=false
参数:
id_start: 必填,非负整数id_end: 必填,非负整数update_cursor: 必填,且必须为falselimit: 可选,正整数,默认100
示例:
GET /api/v1/records?id_start=2000&id_end=2100&update_cursor=false
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "range",
"cursor_before": 1250,
"cursor_after": 1250,
"origin_cursor": 1000,
"last_record_id": 2050,
"updated": false,
"count": 51,
"id_start": 2000,
"id_end": 2100,
"records": [
{
"ID": 2000,
"BianHaoID": "..."
}
]
}
2.3 参数校验规则
limit必须大于 0id_start和id_end必须同时出现id_start和id_end必须是非负整数id_start不能大于id_end- 范围查询模式必须显式传
update_cursor=false
3. Query Current Order Snapshot
GET /api/v1/records/by-order
用途:
- 按
external_order_id精确查询当前整单快照 - 返回该
BianHaoID下当前全部 records - 不读取 cursor,也不推进 cursor
- 返回字段会继续附带现有的
customer、area、HpName
参数:
external_order_id: 必填,字符串,精确对应主表D_KhDhd.BianHaoID
示例:
GET /api/v1/records/by-order?external_order_id=KD20432358
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "snapshot",
"external_order_id": "KD20432358",
"status": "active",
"snapshot_at": "2026-04-16T10:12:34Z",
"total_count": 3,
"records": [
{
"ID": 1000001,
"BianHaoID": "KD20432358",
"KhID": "KH00999",
"HpName": "120克本白四面弹单定",
"area": "周边",
"customer": {
"KhID": "KH00999",
"KhName": "鸿烨服饰"
}
}
]
}
字段说明:
mode: 固定为snapshotexternal_order_id: 当前查询的订单号status: 当前快照状态;查询命中时固定为activesnapshot_at: 服务端生成本次快照响应的时间total_count: 当前订单快照下 records 数量records: 当前订单下的全部 records,字段宽度与现有GET /api/v1/records保持一致
未命中响应示例:
{
"external_order_id": "KD20432358",
"status": "not_found",
"message": "未找到该 external_order_id 对应的订单"
}
4. Query Customer Orders
GET /api/v1/records/by-customer
用途:
- 按客户名精确查询该客户名下全部订单
- 底层先查询
D_KhDhd明细,再按BianHaoID聚合为订单主体 - 每个订单主体下的明细结构与现有
GET /api/v1/records/by-order完全一致 - 不读取 cursor,也不推进 cursor
- 结果按配置项
customer_orders.sort_column倒序排列,默认KdRiQi
参数:
customer_name_b64: 必填,客户名的 Base64URL 编码;精确匹配客户表dbo.B_Khzl.KhName
示例:
GET /api/v1/records/by-customer?customer_name_b64=6bi_54OI5pyN6aWw
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "customer_orders",
"customer_name": "鸿烨服饰",
"status": "active",
"snapshot_at": "2026-05-09T10:12:34Z",
"total_orders": 2,
"orders": [
{
"mode": "snapshot",
"external_order_id": "KD20432358",
"status": "active",
"snapshot_at": "2026-05-09T10:12:34Z",
"total_count": 2,
"records": [
{
"ID": 1000001,
"BianHaoID": "KD20432358",
"KhID": "KH00999",
"HpName": "120克本白四面弹单定",
"area": "周边",
"customer": {
"KhID": "KH00999",
"KhName": "鸿烨服饰"
}
}
]
}
]
}
字段说明:
mode: 固定为customer_orderscustomer_name: 当前查询的客户名status: 查询命中时固定为activesnapshot_at: 服务端生成本次响应的时间total_orders: 当前客户名下聚合后的订单数orders: 订单列表;每个订单元素的结构与GET /api/v1/records/by-order返回结构保持一致
未命中响应示例:
{
"customer_name": "鸿烨服饰",
"status": "not_found",
"message": "未找到该 customer_name 对应的订单"
}
配置:
customer_orders:
sort_column: "KdRiQi"
sort_column用于控制订单聚合结果的排序字段,默认值为KdRiQi
5. Query Customer List
GET /api/v1/customers
用途:
- 按客户主档表
dbo.B_Khzl拉取客户列表 - 每次固定返回
10个客户 - 只返回
KhID和KhName - 用于外部系统按客户维度做同步
- 使用游标参数
cursor_id续拉,不使用page/pageSize
排序与游标规则:
- 底层按
ZZRQ ASC, KhID ASC排序 ZZRQ越早的客户越先返回,避免从新到旧翻页时遗漏新数据cursor_id传上一页最后一条客户的KhID- 服务端会先查出该
KhID对应的ZZRQ,再从(ZZRQ, KhID)这个排序位置之后继续取下一批10条 - 如果多个客户
ZZRQ相同,会继续用KhID做稳定次序
参数:
cursor_id: 可选,上一页最后一条客户的KhID
示例:
GET /api/v1/customers
Authorization: your-fixed-authorization-secret
GET /api/v1/customers?cursor_id=KH01485
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "customers",
"cursor_id": "KH01485",
"next_cursor_id": "KH01495",
"snapshot_at": "2026-05-14T15:00:00Z",
"count": 10,
"customers": [
{
"customer_id": "KH01486",
"customer_name": "A亿佳服饰"
}
]
}
字段说明:
mode: 固定为customerscursor_id: 本次请求使用的游标;首批为空next_cursor_id: 本页最后一条客户的KhID;下一页继续传它即可snapshot_at: 服务端生成本次响应的时间count: 当前返回客户数,最大固定为10customers[].customer_id: 客户 ID,对应dbo.B_Khzl.KhIDcustomers[].customer_name: 客户名称,对应dbo.B_Khzl.KhName
未命中游标响应示例:
{
"cursor_id": "KH99999",
"status": "not_found",
"message": "未找到该 cursor_id 对应的客户"
}
6. Query I_Sale Records By BianHaoID
GET /api/v1/i-sale/by-bianhao
用途:
- 按
BianHaoID查询dbo.I_Sale中的业务明细记录 - 为收款和退款记录补充对应业务单据的信息
- 返回
I_Sale的全量字段 - 不读取 cursor,也不推进 cursor
注意:
- 同一个
BianHaoID在I_Sale中可能命中多条明细行 - 因此该接口返回的是
records[],不是单对象详情
参数:
bianhao_id: 必填,精确匹配dbo.I_Sale.BianHaoID
示例:
GET /api/v1/i-sale/by-bianhao?bianhao_id=XT20203911
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "i_sale_records",
"bianhao_id": "XT20203911",
"status": "active",
"snapshot_at": "2026-05-14T15:10:00Z",
"total_count": 1,
"records": [
{
"BianHaoID": "XT20203911",
"DanType": "客户退货单",
"KhID": "KH01204"
}
]
}
字段说明:
mode: 固定为i_sale_recordsbianhao_id: 当前查询的业务单号status: 查询命中时固定为activesnapshot_at: 服务端生成本次响应的时间total_count: 当前BianHaoID命中的I_Sale明细行数records:I_Sale全量字段明细列表
未命中响应示例:
{
"bianhao_id": "XT404",
"status": "not_found",
"message": "未找到该 bianhao_id 对应的 I_Sale 记录"
}
7. Query I_Sale Records By Customer
GET /api/v1/i-sale/by-customer
用途:
- 按客户 ID 查询
dbo.I_Sale中的业务明细记录 - 用于补足按客户统计时对
I_Sale的直接拉取能力 - 不分页、不使用游标;一次返回当前客户在指定分类下的全部命中记录
- 返回字段不是
I_Sale全 119 列,而是“核心标识 + 全部金额字段 + 全部数量字段 + 备注字段”
当前分类策略:
sale:只取DanType = '成品销售单' AND BianHaoID LIKE 'XS%'sale_return:只取DanType = '客户退货单' AND BianHaoID LIKE 'XT%'
当前实现决策:
- 用户明确接受低频调用,因此本接口不做分页和游标
- 但为了避免把
I_Sale全 119 列直接暴露给同步方,当前只返回与统计/业务核对最相关的字段组 - 该字段组已结合项目内
I_SALE_FIELD_ANALYSIS.md与线上db/columns验证
参数:
customer_id: 必填,精确匹配dbo.I_Sale.KhIDcategory: 必填,当前只支持sale、sale_return
示例:
GET /api/v1/i-sale/by-customer?customer_id=KH00308&category=sale
Authorization: your-fixed-authorization-secret
GET /api/v1/i-sale/by-customer?customer_id=KH00308&category=sale_return
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "i_sale_customer_records",
"customer_id": "KH00308",
"category": "sale",
"status": "active",
"snapshot_at": "2026-05-14T20:00:00Z",
"total_count": 2,
"records": [
{
"SubID": 1,
"BianHaoID": "XS20367431",
"DanType": "成品销售单",
"KhID": "KH00308",
"RiQi": "2026-05-14T00:00:00Z",
"KdRiQi": "2026-05-14T19:08:23Z",
"JieSunFS": "欠款",
"HpID": "HP00001",
"JianShu": "1.00",
"ShuLiang": "20.00",
"ShuLiangT": "20.00",
"DanJia": "32.000000",
"JinE": "640.000000",
"DingJin": "0.00",
"YfJinE": "0.00",
"SfJinE": "0.00",
"ZkJinE": "0.00",
"LjQK": "0.00",
"BeiZhu": "",
"BeiZhuC": "",
"BeiZhuD": "",
"MeoD": "普通单据"
}
]
}
字段说明:
mode: 固定为i_sale_customer_recordscustomer_id: 当前查询的客户 IDcategory: 当前查询的分类status: 查询命中时固定为activesnapshot_at: 服务端生成本次响应的时间total_count: 当前客户在该分类下命中的I_Sale明细行数records: 当前字段集包含以下几类字段
核心标识字段:
SubIDBianHaoIDDanTypeKhIDRiQiKdRiQiJieSunFSHpIDJiJiaDW
数量相关字段:
JianShuShuLiangShuLiangZShuLiangKShuLiangSJianShuTShuLiangTShuLiangZTShuLiangKTShuLiangSTSl1~Sl10
金额相关字段:
DingJinYfJinESfJinEZkJinELjQKDanJiaJinEDanJiaTJinETDanJiaCBJinECBJinE_SL
备注相关字段:
BeiZhuBeiZhuCBeiZhuDMeoAMeoBMeoCMeoD
未命中响应示例:
{
"customer_id": "KH00308",
"category": "sale",
"status": "not_found",
"message": "未找到该 customer_id 和 category 对应的 I_Sale 记录"
}
参数错误:
customer_id为空时返回400category非sale/sale_return时返回400
7. Query Customer Finance Records
GET /api/v1/finance/by-customer
用途:
- 按客户名精确查询该客户名下的财务记录
- 当前销售/销退只认
dbo.I_Sale - 当前收款/退款只认
dbo.F_Skd - 支持按记录类型筛选,并支持控制是否包含真实资金变动、是否包含挂账/欠款调整
- 不读取 cursor,也不推进 cursor
参数:
customer_name_b64: 必填,客户名的 Base64URL 编码;精确匹配客户表dbo.B_Khzl.KhNamerecord_types: 可选,逗号分隔;支持sale、sale_return、receipt、refund;不传默认全部include_cash_movement: 可选,布尔值;默认trueinclude_adjustments: 可选,布尔值;默认falsefilter_sk: 可选,布尔值;默认false;当前receipt口径下保留该参数但不生效
当前口径:
sale:I_Sale.DanType = '成品销售单'sale_return:I_Sale.DanType = '客户退货单'receipt: 仅纳入F_Skd.BianHaoID LIKE 'SK%'的收款流水,不纳入XS%记录refund:F_Skd.sType = 1 AND F_Skd.BianHaoID LIKE 'XT%'receipt的返回金额字段仍保留F_Skd.FkJinE、F_Skd.ZkJinE等原始值,但当前口径不再使用FkJinE作为筛选条件refund的统计和筛选金额字段为F_Skd.YfJinEinclude_cash_movement与include_adjustments当前仅影响refund;receipt统一按SK%全量统计include_cash_movement=true时,refund取YfJinE < 0include_adjustments=true时,refund额外包含YfJinE = 0的挂账/欠款调整记录
当前已验证的 sType 规则:
sType = 1:当前财务 API 使用的业务单据关联流水,样本中覆盖XS...与XT...sType = 0:当前样本对应SK...型独立收款流水;当前receipt查询会纳入它,refund不会
示例:
GET /api/v1/finance/by-customer?customer_name_b64=6bi_54OI5pyN6aWw&record_types=sale,receipt,refund&include_cash_movement=true&include_adjustments=false
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"mode": "customer_finance",
"customer_name": "鸿烨服饰",
"customer_ids": [
"KH00999"
],
"record_types": [
"sale",
"receipt",
"refund"
],
"include_cash_movement": true,
"include_adjustments": false,
"status": "active",
"snapshot_at": "2026-05-10T21:30:00Z",
"total_count": 3,
"sales_count": 1,
"sale_returns_count": 0,
"receipts_count": 1,
"refunds_count": 1,
"sales": [
{
"BianHaoID": "XS20366749",
"DanType": "成品销售单",
"KhID": "KH00999"
}
],
"sale_returns": [],
"receipts": [
{
"BianHaoID": "XS20215718",
"FkJinE": "378.00",
"JieSunFS": "微信"
}
],
"refunds": [
{
"BianHaoID": "XT20200743",
"YfJinE": "-2201.00",
"FkJinE": "0.00",
"JieSunFS": "微信"
}
]
}
未命中响应示例:
{
"customer_name": "鸿烨服饰",
"status": "not_found",
"message": "未找到该 customer_name 对应的财务记录"
}
参数错误示例:
{
"error": "record_types contains unsupported value"
}
6. Database Inspect Permission Check
GET /api/v1/db/permissions
用途:
- 检查当前数据库账号是否具备数据库分析所需的基础能力
- 尝试验证数据库连接、获取当前库名、列出可见表、读取字段元数据、读取
sys元数据、检查VIEW DEFINITION、以及对所有可见表执行SELECT TOP (1) - 返回总体是否可用,以及各项检查结果
配置开关:
db_inspect:
enabled: true
成功响应示例:
{
"can_inspect": true,
"database_name": "RCYH2020",
"visible_table_count": 32,
"checks": [
{
"name": "database_connection",
"ok": true
},
{
"name": "list_base_tables",
"ok": true
},
{
"name": "read_all_visible_table_columns",
"ok": true
},
{
"name": "select_top_1_from_all_visible_tables",
"ok": true
}
]
}
字段说明:
can_inspect: 是否满足数据库分析所需的整体权限和可读能力database_name: 当前连接到的数据库名visible_table_count: 当前账号可见的基础表数量checks: 分项检查结果failed_column_tables: 无法读取字段元数据的表清单,仅检查失败时出现failed_select_tables: 无法执行SELECT TOP (1)的表清单,仅检查失败时出现
7. List Database Tables
GET /api/v1/db/tables
用途:
- 返回当前数据库中当前账号可见的所有基础表
成功响应示例:
{
"count": 2,
"tables": [
{
"schema": "dbo",
"name": "D_KhDhd",
"full_name": "dbo.D_KhDhd"
},
{
"schema": "dbo",
"name": "B_Khzl",
"full_name": "dbo.B_Khzl"
}
]
}
8. List Table Columns
GET /api/v1/db/columns
参数:
table: 必填,表名,格式为schema.table或db.schema.table
用途:
- 返回指定表的字段列表
- 返回字段名、类型、是否可空、长度、精度、顺序等信息
示例:
GET /api/v1/db/columns?table=dbo.D_KhDhd
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"table": "dbo.D_KhDhd",
"count": 2,
"columns": [
{
"name": "ID",
"data_type": "int",
"nullable": false,
"ordinal_position": 1
},
{
"name": "BianHaoID",
"data_type": "nvarchar",
"nullable": true,
"max_length": 50,
"ordinal_position": 2
}
]
}
9. Generic Table Select
GET /api/v1/db/select
参数:
table: 必填,表名,格式为schema.table或db.schema.tablecolumns: 必填,逗号分隔的字段名列表,例如ID,BianHaoIDwhere: 可选,受限条件表达式;支持=!=<>>>=<<=LIKEIS NULLIS NOT NULL,多个条件只支持用AND连接;字符串字面量必须写成单引号,例如DanType = '客户退货单'limit: 可选,正整数;不传时使用默认值,并受服务端上限限制
用途:
- 对指定表执行通用只读查询
- 仅返回请求中指定的字段集合
- 当前实现固定为
SELECT TOP (limit),支持受限WHERE,不支持自定义 SQL
示例:
GET /api/v1/db/select?table=dbo.D_KhDhd&columns=ID,BianHaoID&limit=10
Authorization: your-fixed-authorization-secret
GET /api/v1/db/select?table=dbo.I_Sale&columns=BianHaoID,DanType,ShuLiang&where=DanType%20%3D%20%27%E5%AE%A2%E6%88%B7%E9%80%80%E8%B4%A7%E5%8D%95%27%20AND%20ShuLiang%20%3C%200&limit=10
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"table": "dbo.D_KhDhd",
"columns": [
"ID",
"BianHaoID"
],
"count": 2,
"rows": [
{
"ID": 1001,
"BianHaoID": "KD20432358"
},
{
"ID": 1002,
"BianHaoID": "KD20432359"
}
]
}
参数错误示例:
{
"error": "where is invalid"
}
当配置关闭时:
{
"error": "db inspect api is disabled"
}
10. Get Current Cursor
GET /api/v1/cursor
用途:
- 查看当前游标值
- 查看最近一次人工修改游标前的
origin_cursor
示例:
GET /api/v1/cursor
Authorization: your-fixed-authorization-secret
成功响应示例:
{
"cursor": 1500,
"origin_cursor": 1250
}
11. Set Cursor
POST /api/v1/cursor/set
用途:
- 人工把游标改到指定值
- 每次手工修改时,都会把修改前的游标保存到
origin_cursor
请求体:
{
"value": 1500
}
规则:
value必须是非负整数- 调用成功后:
cursor更新为valueorigin_cursor保存修改前的cursor
请求示例:
POST /api/v1/cursor/set
Authorization: your-fixed-authorization-secret
Content-Type: application/json
{
"value": 1500
}
成功响应示例:
{
"cursor_before": 1250,
"cursor_after": 1500,
"origin_cursor": 1250,
"updated": true
}
12. Get Image
GET /api/v1/image
用途:
- 根据给定文件名,从配置的图片目录读取对应文件
- 找到后返回图片内容的 Base64 字符串
- 找不到时返回
404
配置文件:
image:
directory: "./images"
参数:
name: 可选,原始文件名,必须是纯文件名,不能包含路径name_b64: 可选,文件名的 Base64URL 编码;当文件名包含#等 URL 特殊字符时推荐使用- 如果
name不带后缀,服务端会按.jpg、.png的顺序自动尝试
说明:
name和name_b64二选一即可- 如果文件名里包含
#,很多客户端会在真正发请求前把它后面的内容当作 URL fragment 截掉,因此推荐使用name_b64
示例:
GET /api/v1/image?name=test
Authorization: your-fixed-authorization-secret
GET /api/v1/image?name_b64=YWJjIzEyMw
Authorization: your-fixed-authorization-secret
上面的 name_b64=YWJjIzEyMw 对应原始文件名:
abc#123
成功响应示例:
{
"name": "test.jpg",
"content_type": "image/jpeg",
"data": "/9j/4AAQSkZJRgABAQ..."
}
错误响应示例:
{
"error": "image not found"
}
说明:
data是图片二进制内容的 Base64 编码content_type会优先根据文件扩展名推断,推断不出来时会回退到内容检测- 如果
name没有后缀,会优先查找name.jpg,找不到再查name.png - 如果文件名中含有
#、空格等特殊字符,优先使用name_b64 - 为了安全,
name不能带目录分隔符,也不能包含..
13. Cursor State File
默认文件:
./data/cursor-state.json
文件内容示例:
{
"cursor": 1500,
"origin_cursor": 1250
}
说明:
cursor: 当前增量同步游标origin_cursor: 最近一次人工修改游标前的值