diff --git a/.gitignore b/.gitignore
index 4f59b90..a92d330 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/back-end/node_modules
/.tmp-upload-probe
+/.tmp-openapi-validate/node_modules
diff --git a/.tmp-openapi-validate/package.json b/.tmp-openapi-validate/package.json
new file mode 100644
index 0000000..3bac18b
--- /dev/null
+++ b/.tmp-openapi-validate/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "tmp-openapi-validate",
+ "private": true,
+ "version": "1.0.0",
+ "dependencies": {
+ "@redocly/cli": "^2.25.4",
+ "yaml": "^2.8.3"
+ }
+}
diff --git a/docs/pb_tbl_attachments.md b/docs/pb_tbl_attachments.md
index 4abe82f..1da3782 100644
--- a/docs/pb_tbl_attachments.md
+++ b/docs/pb_tbl_attachments.md
@@ -23,6 +23,7 @@
| `attachments_ocr` | `text` | 否 | OCR 识别结果 |
| `attachments_status` | `text` | 否 | 附件状态 |
| `attachments_remark` | `text` | 否 | 备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -35,5 +36,7 @@
## 补充约定
- 图片、视频、普通文件都统一走本表。
+- `is_delete` 用于软删除控制,附件删除时建议先标记为 `1`,并由后续归档/清理任务处理物理文件。
+- 集合默认查询规则已内置 `is_delete = 0`,附件列表/详情默认不返回已软删除数据。
- 业务访问控制不放在本表,而由引用它的业务表与 hooks 接口控制。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/docs/pb_tbl_auth_resources.md b/docs/pb_tbl_auth_resources.md
index 51a7334..956737f 100644
--- a/docs/pb_tbl_auth_resources.md
+++ b/docs/pb_tbl_auth_resources.md
@@ -17,6 +17,12 @@
| `table_name` | `text` | 是 | 对应数据表名 |
| `column_name` | `text` | 否 | 对应字段名;表级权限时可为空 |
| `res_type` | `text` | 是 | 资源类型 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
+
+## 补充约定
+
+- `is_delete` 用于软删除控制,资源定义如需停用应优先置为 `1`,避免直接物理删除影响权限审计。
+- 集合默认查询规则已内置 `is_delete = 0`,资源列表/详情默认不返回已软删除数据。
## 索引
diff --git a/docs/pb_tbl_auth_role_perms.md b/docs/pb_tbl_auth_role_perms.md
index 6d3176c..c8b4509 100644
--- a/docs/pb_tbl_auth_role_perms.md
+++ b/docs/pb_tbl_auth_role_perms.md
@@ -18,6 +18,7 @@
| `res_id` | `text` | 是 | 资源业务 ID |
| `access_level` | `number` | 是 | 权限级别 |
| `priority` | `number` | 否 | 优先级 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -27,3 +28,8 @@
| `idx_tbl_auth_role_perms_role_id` | `INDEX` | 加速按角色查询 |
| `idx_tbl_auth_role_perms_res_id` | `INDEX` | 加速按资源查询 |
| `idx_tbl_auth_role_perms_unique_map` | `UNIQUE INDEX` | 保证 `role_id + res_id` 唯一 |
+
+## 补充约定
+
+- `is_delete` 用于软删除控制,历史角色授权建议通过置 `1` 失效,以保留审计痕迹。
+- 集合默认查询规则已内置 `is_delete = 0`,角色权限映射默认不返回已软删除记录。
diff --git a/docs/pb_tbl_auth_roles.md b/docs/pb_tbl_auth_roles.md
index f29f12c..1e53fbe 100644
--- a/docs/pb_tbl_auth_roles.md
+++ b/docs/pb_tbl_auth_roles.md
@@ -18,6 +18,7 @@
| `role_code` | `text` | 否 | 角色编码 |
| `role_status` | `number` | 否 | 角色状态 |
| `role_remark` | `text` | 否 | 备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -26,3 +27,8 @@
| `idx_tbl_auth_roles_role_id` | `UNIQUE INDEX` | 保证 `role_id` 唯一 |
| `idx_tbl_auth_roles_role_name` | `UNIQUE INDEX` | 保证 `role_name` 唯一 |
| `idx_tbl_auth_roles_role_code` | `UNIQUE INDEX` | 保证 `role_code` 唯一 |
+
+## 补充约定
+
+- `is_delete` 用于软删除控制,角色下线时应优先置为 `1`,避免直接物理删除导致历史授权链断裂。
+- 集合默认查询规则已内置 `is_delete = 0`,角色列表/详情默认不返回已软删除数据。
diff --git a/docs/pb_tbl_auth_row_scopes.md b/docs/pb_tbl_auth_row_scopes.md
index 6b0147b..b72e0ff 100644
--- a/docs/pb_tbl_auth_row_scopes.md
+++ b/docs/pb_tbl_auth_row_scopes.md
@@ -18,6 +18,7 @@
| `target_id` | `text` | 是 | 目标 ID |
| `table_name` | `text` | 是 | 作用表名 |
| `filter_sql` | `editor` | 是 | 行级过滤表达式 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -27,3 +28,8 @@
| `idx_tbl_auth_row_scopes_target_type` | `INDEX` | 加速按目标类型查询 |
| `idx_tbl_auth_row_scopes_target_id` | `INDEX` | 加速按目标 ID 查询 |
| `idx_tbl_auth_row_scopes_table_name` | `INDEX` | 加速按作用表查询 |
+
+## 补充约定
+
+- `is_delete` 用于软删除控制,废弃的行级范围规则建议软删除保留,以便问题追溯。
+- 集合默认查询规则已内置 `is_delete = 0`,行级范围列表/详情默认隐藏已软删除规则。
diff --git a/docs/pb_tbl_auth_user_overrides.md b/docs/pb_tbl_auth_user_overrides.md
index e2492c5..ec29d92 100644
--- a/docs/pb_tbl_auth_user_overrides.md
+++ b/docs/pb_tbl_auth_user_overrides.md
@@ -18,6 +18,7 @@
| `res_id` | `text` | 是 | 资源业务 ID |
| `access_level` | `number` | 是 | 权限级别 |
| `priority` | `number` | 否 | 优先级 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -27,3 +28,8 @@
| `idx_tbl_auth_user_overrides_users_convers_id` | `INDEX` | 加速按用户查询 |
| `idx_tbl_auth_user_overrides_res_id` | `INDEX` | 加速按资源查询 |
| `idx_tbl_auth_user_overrides_unique_map` | `UNIQUE INDEX` | 保证 `users_convers_id + res_id` 唯一 |
+
+## 补充约定
+
+- `is_delete` 用于软删除控制,用户覆盖权限取消时建议软删除保留记录。
+- 集合默认查询规则已内置 `is_delete = 0`,用户覆盖权限列表/详情默认隐藏已软删除记录。
diff --git a/docs/pb_tbl_auth_users.md b/docs/pb_tbl_auth_users.md
index 3a36321..20efcf2 100644
--- a/docs/pb_tbl_auth_users.md
+++ b/docs/pb_tbl_auth_users.md
@@ -40,6 +40,7 @@
| `users_id_pic_a` | `text` | 否 | 证件照正面附件 ID |
| `users_id_pic_b` | `text` | 否 | 证件照反面附件 ID |
| `users_tag` | `text` | 否 | 用户标签 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -62,6 +63,8 @@
- 本表为 `auth` collection,除上述字段外还受 PocketBase 原生鉴权机制约束。
- 图片类字段统一只保存 `tbl_attachments.attachments_id`。
+- `is_delete` 用于软删除控制,业务侧删除时应优先将其置为 `1`,而不是直接物理删除记录。
+- 集合默认查询规则已内置 `is_delete = 0`,管理侧常规列表/详情默认隐藏已软删除用户。
- 登录接口返回的 token 来源于本表 auth record 的原生签发能力,可直接给 PocketBase SDK 使用。
- 新用户注册时,`users_level` 默认保持为空;已有用户后续登录 / 更新流程也不会自动改写该字段。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/docs/pb_tbl_cart.md b/docs/pb_tbl_cart.md
index 5324b2a..97202e2 100644
--- a/docs/pb_tbl_cart.md
+++ b/docs/pb_tbl_cart.md
@@ -28,6 +28,7 @@
| `cart_status` | `text` | 是 | 购物车状态,建议值:`有效` / `无效` |
| `cart_at_price` | `number` | 是 | 加入购物车时的价格,用于后续降价提醒或对比 |
| `cart_remark` | `text` | 否 | 备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -46,6 +47,8 @@
- `cart_owner`、`cart_product_id` 当前按文本字段保存业务 ID,不直接建立 relation,便于兼容现有 hooks 业务模型。
- `cart_owner` 统一保存 `tbl_auth_users.openid`,便于直接使用微信登录返回 token 做原生访问控制。
+- `is_delete` 用于软删除控制,购物车项删除时建议优先标记为 `1`。
+- 集合默认查询规则已内置 `is_delete = 0`,常规列表/详情不会返回已软删除数据。
- `cart_product_quantity`、`cart_at_price` 使用 `number`,数量正整数与价格精度建议在 hooks / API 层统一校验。
- 当购物车被清空时,建议业务侧将历史记录 `cart_status` 置为 `无效`,而不是直接覆盖有效记录。
- `cart_create` 由数据库自动写入,接口层不需要也不应允许客户端自行填值。
diff --git a/docs/pb_tbl_company.md b/docs/pb_tbl_company.md
index 05bee3f..e3f5292 100644
--- a/docs/pb_tbl_company.md
+++ b/docs/pb_tbl_company.md
@@ -32,6 +32,7 @@
| `company_level` | `text` | 否 | 公司等级 |
| `company_owner_openid` | `text` | 否 | 公司所有者 openid |
| `company_remark` | `text` | 否 | 备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -44,5 +45,7 @@
## 补充约定
- 微信端原生 PocketBase 接口支持公开创建公司记录。
+- `is_delete` 用于软删除控制,公司资料停用时应优先置为 `1`。
+- 集合默认查询规则已内置 `is_delete = 0`,公司列表/详情默认不返回已软删除数据。
- `company_id` 已切换为数据库自动生成,客户端不再需要提交。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/docs/pb_tbl_document.md b/docs/pb_tbl_document.md
index 013e190..c46bcaa 100644
--- a/docs/pb_tbl_document.md
+++ b/docs/pb_tbl_document.md
@@ -40,6 +40,7 @@
| `document_remark` | `text` | 否 | 备注 |
| `document_file` | `text` | 否 | 普通文件附件 ID 集合,底层以 `|` 分隔 |
| `document_create` | `autodate` | 否 | 文档创建时间,由数据库自动生成 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -57,6 +58,8 @@
## 补充约定
- 三类附件字段都只保存 `attachments_id`,真实文件统一在 `tbl_attachments`。
+- `is_delete` 用于软删除控制,文档删除时建议优先标记为 `1`,避免直接物理删除。
+- 集合默认查询规则已内置 `is_delete = 0`,公开列表与详情默认隐藏已软删除文档。
- `document_create` 已作为原生 PocketBase 列表排序字段,推荐使用 `sort=-document_create`。
- 面向用户填写的字段里,仅 `document_title`、`document_type` 必填,其余允许为空。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/docs/pb_tbl_document_operation_history.md b/docs/pb_tbl_document_operation_history.md
index 3c06188..36694e9 100644
--- a/docs/pb_tbl_document_operation_history.md
+++ b/docs/pb_tbl_document_operation_history.md
@@ -19,6 +19,7 @@
| `doh_user_id` | `text` | 否 | 操作人业务 ID |
| `doh_current_count` | `number` | 否 | 本次操作对应次数 |
| `doh_remark` | `text` | 否 | 备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -32,4 +33,5 @@
## 补充约定
- 本表主要用于管理端审计与追溯,不对匿名用户开放。
+- `is_delete` 用于软删除控制,历史记录如需屏蔽显示可标记为 `1`,不建议直接物理删除。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/docs/pb_tbl_order.md b/docs/pb_tbl_order.md
index 66706c8..1646a79 100644
--- a/docs/pb_tbl_order.md
+++ b/docs/pb_tbl_order.md
@@ -29,6 +29,7 @@
| `order_snap` | `json` | 是 | 订单快照,完整保存订单明细信息 |
| `order_amount` | `number` | 是 | 订单总金额 |
| `order_remark` | `text` | 否 | 订单备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -49,6 +50,8 @@
- 当订单进入 `订单已确定` 及之后状态时,建议业务侧锁定关键字段,不再允许修改订单核心数据。
- `order_owner`、`order_source_id` 当前按文本字段保存业务 ID,不直接建立 relation,便于兼容现有 hooks 业务模型。
- `order_owner` 统一保存 `tbl_auth_users.openid`,便于直接使用微信登录返回 token 做原生访问控制。
+- `is_delete` 用于软删除控制,订单删除/归档时建议优先标记为 `1`。
+- 集合默认查询规则已内置 `is_delete = 0`,常规列表/详情不会返回已软删除数据。
- `order_amount` 使用 `number`,货币精度策略建议后续统一为“分”或固定小数位。
- `order_create` 由数据库自动写入,接口层不需要也不应允许客户端自行填值。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/docs/pb_tbl_product_list.md b/docs/pb_tbl_product_list.md
index 1d4ad8e..165bc6a 100644
--- a/docs/pb_tbl_product_list.md
+++ b/docs/pb_tbl_product_list.md
@@ -31,6 +31,7 @@
| `prod_list_basic_price` | `number` | 否 | 基础价格 |
| `prod_list_vip_price` | `json` | 否 | 会员价数组,格式为 `[{"viplevel":"会员等级枚举值","price":1999}]` |
| `prod_list_remark` | `text` | 否 | 备注 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -49,6 +50,8 @@
## 补充约定
- `prod_list_icon` 仅保存附件业务 ID,真实文件统一在 `tbl_attachments`;多图时按上传顺序使用 `|` 聚合。
+- `is_delete` 用于软删除控制,产品停用/删除时建议优先标记为 `1`。
+- 集合默认查询规则已内置 `is_delete = 0`,产品列表默认不返回已软删除记录。
- 当前预构建脚本中已将 `listRule` 与 `viewRule` 设置为空字符串(`""`),对应 PocketBase 的“任何人可查看”。
- `prod_list_parameters` 使用 PocketBase `json` 字段,写入时应直接提交数组结构:`[{"sort":1,"name":"属性名","value":"属性值"}]`。
- `prod_list_parameters.sort` 用于稳定参数展示顺序,约定为正整数;前端未填写时可按当前录入/导入顺序自动补齐。
diff --git a/docs/pb_tbl_system_dict.md b/docs/pb_tbl_system_dict.md
index 129ecc7..609c9c0 100644
--- a/docs/pb_tbl_system_dict.md
+++ b/docs/pb_tbl_system_dict.md
@@ -22,6 +22,7 @@
| `dict_word_parent_id` | `text` | 否 | 父级字典业务 ID |
| `dict_word_remark` | `text` | 否 | 备注 |
| `dict_word_image` | `text` | 否 | 枚举图片附件 ID 集合,和枚举值一一对应 |
+| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
## 索引
@@ -34,5 +35,7 @@
## 补充约定
- 业务返回时,hooks 会把聚合字段转换成 `items[]` 结构,每个元素包含 `enum`、`description`、`image`、`imageUrl`、`sortOrder`。
+- `is_delete` 用于软删除控制,字典废弃时建议优先标记为 `1`。
+- 集合默认查询规则已内置 `is_delete = 0`,字典列表/详情默认隐藏已软删除项。
- 字典项图片本体统一存放在 `tbl_attachments`,本表只保存 `attachments_id`。
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
diff --git a/pocket-base/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js b/pocket-base/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js
index 437666d..5e6cc1c 100644
--- a/pocket-base/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js
+++ b/pocket-base/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js
@@ -521,6 +521,7 @@ function validateProductMutationBody(e, isUpdate) {
}
return {
+ id: payload.id || '',
prod_list_id: payload.prod_list_id || '',
prod_list_name: payload.prod_list_name || '',
prod_list_modelnumber: payload.prod_list_modelnumber || '',
diff --git a/pocket-base/bai_api_pb_hooks/bai_api_shared/services/cartOrderService.js b/pocket-base/bai_api_pb_hooks/bai_api_shared/services/cartOrderService.js
index e67e7d9..ad64c12 100644
--- a/pocket-base/bai_api_pb_hooks/bai_api_shared/services/cartOrderService.js
+++ b/pocket-base/bai_api_pb_hooks/bai_api_shared/services/cartOrderService.js
@@ -304,47 +304,52 @@ function exportOrderRecord(record) {
}
function exportAdminCartRecord(record) {
- const productInfo = buildProductInfo(findProductRecordByBusinessId(record.cart_product_id))
+ const productId = record && typeof record.getString === 'function'
+ ? record.getString('cart_product_id')
+ : String(record && record.cart_product_id || '')
+ const productInfo = buildProductInfo(findProductRecordByBusinessId(productId))
return {
- pb_id: String(record.id || ''),
- cart_id: String(record.cart_id || ''),
- cart_number: String(record.cart_number || ''),
- cart_create: String(record.cart_create || ''),
- cart_owner: String(record.cart_owner || ''),
- cart_product_id: String(record.cart_product_id || ''),
- cart_product_quantity: Number(record.cart_product_quantity || 0),
- cart_status: String(record.cart_status || ''),
- cart_at_price: Number(record.cart_at_price || 0),
- cart_remark: String(record.cart_remark || ''),
+ pb_id: String(record && record.id || ''),
+ cart_id: record && typeof record.getString === 'function' ? record.getString('cart_id') : String(record && record.cart_id || ''),
+ cart_number: record && typeof record.getString === 'function' ? record.getString('cart_number') : String(record && record.cart_number || ''),
+ cart_create: record && typeof record.get === 'function' ? String(record.get('cart_create') || '') : String(record && record.cart_create || ''),
+ cart_owner: record && typeof record.getString === 'function' ? record.getString('cart_owner') : String(record && record.cart_owner || ''),
+ cart_product_id: productId,
+ cart_product_quantity: record && typeof record.get === 'function' ? Number(record.get('cart_product_quantity') || 0) : Number(record && record.cart_product_quantity || 0),
+ cart_status: record && typeof record.getString === 'function' ? record.getString('cart_status') : String(record && record.cart_status || ''),
+ cart_at_price: record && typeof record.get === 'function' ? Number(record.get('cart_at_price') || 0) : Number(record && record.cart_at_price || 0),
+ cart_remark: record && typeof record.getString === 'function' ? record.getString('cart_remark') : String(record && record.cart_remark || ''),
product_name: productInfo.prod_list_name,
product_modelnumber: productInfo.prod_list_modelnumber,
product_basic_price: productInfo.prod_list_basic_price,
- created: String(record.created || ''),
- updated: String(record.updated || ''),
+ created: String(record && record.created || ''),
+ updated: String(record && record.updated || ''),
}
}
function exportAdminOrderRecord(record) {
return {
- pb_id: String(record.id || ''),
- order_id: String(record.order_id || ''),
- order_number: String(record.order_number || ''),
- order_create: String(record.order_create || ''),
- order_owner: String(record.order_owner || ''),
- order_source: String(record.order_source || ''),
- order_status: String(record.order_status || ''),
- order_source_id: String(record.order_source_id || ''),
- order_snap: parseJsonFieldForOutput(record.order_snap),
- order_amount: Number(record.order_amount || 0),
- order_remark: String(record.order_remark || ''),
- created: String(record.created || ''),
- updated: String(record.updated || ''),
+ pb_id: String(record && record.id || ''),
+ order_id: record && typeof record.getString === 'function' ? record.getString('order_id') : String(record && record.order_id || ''),
+ order_number: record && typeof record.getString === 'function' ? record.getString('order_number') : String(record && record.order_number || ''),
+ order_create: record && typeof record.get === 'function' ? String(record.get('order_create') || '') : String(record && record.order_create || ''),
+ order_owner: record && typeof record.getString === 'function' ? record.getString('order_owner') : String(record && record.order_owner || ''),
+ order_source: record && typeof record.getString === 'function' ? record.getString('order_source') : String(record && record.order_source || ''),
+ order_status: record && typeof record.getString === 'function' ? record.getString('order_status') : String(record && record.order_status || ''),
+ order_source_id: record && typeof record.getString === 'function' ? record.getString('order_source_id') : String(record && record.order_source_id || ''),
+ order_snap: record && typeof record.get === 'function' ? parseJsonFieldForOutput(record.get('order_snap')) : parseJsonFieldForOutput(record && record.order_snap),
+ order_amount: record && typeof record.get === 'function' ? Number(record.get('order_amount') || 0) : Number(record && record.order_amount || 0),
+ order_remark: record && typeof record.getString === 'function' ? record.getString('order_remark') : String(record && record.order_remark || ''),
+ created: String(record && record.created || ''),
+ updated: String(record && record.updated || ''),
}
}
function exportAdminManageUser(userRecord, groupedCarts, groupedOrders) {
- const openid = String(userRecord.openid || '')
+ const openid = userRecord && typeof userRecord.getString === 'function'
+ ? userRecord.getString('openid')
+ : String(userRecord && userRecord.openid || '')
const carts = groupedCarts[openid] || []
const orders = groupedOrders[openid] || []
let cartTotalQuantity = 0
@@ -358,40 +363,44 @@ function exportAdminManageUser(userRecord, groupedCarts, groupedOrders) {
}
return {
- pb_id: String(userRecord.id || ''),
+ pb_id: String(userRecord && userRecord.id || ''),
openid: openid,
- users_id: String(userRecord.users_id || ''),
- users_name: String(userRecord.users_name || ''),
- users_phone: String(userRecord.users_phone || ''),
- users_level: String(userRecord.users_level || ''),
- users_level_name: userService.resolveUserLevelName(String(userRecord.users_level || '')),
- users_type: String(userRecord.users_type || ''),
- users_idtype: String(userRecord.users_idtype || ''),
- users_id_number: String(userRecord.users_id_number || ''),
- users_status: String(userRecord.users_status || ''),
- users_rank_level: userRecord.users_rank_level === null || typeof userRecord.users_rank_level === 'undefined'
+ users_id: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_id') : String(userRecord && userRecord.users_id || ''),
+ users_name: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_name') : String(userRecord && userRecord.users_name || ''),
+ users_phone: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_phone') : String(userRecord && userRecord.users_phone || ''),
+ users_level: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_level') : String(userRecord && userRecord.users_level || ''),
+ users_level_name: userService.resolveUserLevelName(userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_level') : String(userRecord && userRecord.users_level || '')),
+ users_type: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_type') : String(userRecord && userRecord.users_type || ''),
+ users_idtype: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_idtype') : String(userRecord && userRecord.users_idtype || ''),
+ users_id_number: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_id_number') : String(userRecord && userRecord.users_id_number || ''),
+ users_status: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_status') : String(userRecord && userRecord.users_status || ''),
+ users_rank_level: !userRecord || typeof userRecord.get !== 'function'
+ ? (userRecord && (userRecord.users_rank_level === null || typeof userRecord.users_rank_level === 'undefined') ? null : Number(userRecord && userRecord.users_rank_level))
+ : (userRecord.get('users_rank_level') === null || typeof userRecord.get('users_rank_level') === 'undefined')
? null
- : Number(userRecord.users_rank_level),
- users_auth_type: userRecord.users_auth_type === null || typeof userRecord.users_auth_type === 'undefined'
+ : Number(userRecord.get('users_rank_level')),
+ users_auth_type: !userRecord || typeof userRecord.get !== 'function'
+ ? (userRecord && (userRecord.users_auth_type === null || typeof userRecord.users_auth_type === 'undefined') ? null : Number(userRecord && userRecord.users_auth_type))
+ : (userRecord.get('users_auth_type') === null || typeof userRecord.get('users_auth_type') === 'undefined')
? null
- : Number(userRecord.users_auth_type),
- users_tag: String(userRecord.users_tag || ''),
- company_id: String(userRecord.company_id || ''),
- users_parent_id: String(userRecord.users_parent_id || ''),
- users_promo_code: String(userRecord.users_promo_code || ''),
- usergroups_id: String(userRecord.usergroups_id || ''),
- users_picture: String(userRecord.users_picture || ''),
- users_id_pic_a: String(userRecord.users_id_pic_a || ''),
- users_id_pic_b: String(userRecord.users_id_pic_b || ''),
- users_title_picture: String(userRecord.users_title_picture || ''),
+ : Number(userRecord.get('users_auth_type')),
+ users_tag: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_tag') : String(userRecord && userRecord.users_tag || ''),
+ company_id: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('company_id') : String(userRecord && userRecord.company_id || ''),
+ users_parent_id: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_parent_id') : String(userRecord && userRecord.users_parent_id || ''),
+ users_promo_code: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_promo_code') : String(userRecord && userRecord.users_promo_code || ''),
+ usergroups_id: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('usergroups_id') : String(userRecord && userRecord.usergroups_id || ''),
+ users_picture: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_picture') : String(userRecord && userRecord.users_picture || ''),
+ users_id_pic_a: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_id_pic_a') : String(userRecord && userRecord.users_id_pic_a || ''),
+ users_id_pic_b: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_id_pic_b') : String(userRecord && userRecord.users_id_pic_b || ''),
+ users_title_picture: userRecord && typeof userRecord.getString === 'function' ? userRecord.getString('users_title_picture') : String(userRecord && userRecord.users_title_picture || ''),
cart_count: carts.length,
cart_total_quantity: cartTotalQuantity,
order_count: orders.length,
order_total_amount: orderTotalAmount,
carts: carts,
orders: orders,
- created: String(userRecord.created || ''),
- updated: String(userRecord.updated || ''),
+ created: String(userRecord && userRecord.created || ''),
+ updated: String(userRecord && userRecord.updated || ''),
}
}
@@ -728,7 +737,9 @@ function listManageUsersCartOrders(payload) {
const groupedOrders = {}
for (let i = 0; i < cartRecords.length; i += 1) {
- const owner = String(cartRecords[i].cart_owner || '')
+ const owner = cartRecords[i] && typeof cartRecords[i].getString === 'function'
+ ? cartRecords[i].getString('cart_owner')
+ : String(cartRecords[i] && cartRecords[i].cart_owner || '')
if (!groupedCarts[owner]) {
groupedCarts[owner] = []
}
@@ -736,7 +747,9 @@ function listManageUsersCartOrders(payload) {
}
for (let i = 0; i < orderRecords.length; i += 1) {
- const owner = String(orderRecords[i].order_owner || '')
+ const owner = orderRecords[i] && typeof orderRecords[i].getString === 'function'
+ ? orderRecords[i].getString('order_owner')
+ : String(orderRecords[i] && orderRecords[i].order_owner || '')
if (!groupedOrders[owner]) {
groupedOrders[owner] = []
}
diff --git a/pocket-base/bai_api_pb_hooks/bai_api_shared/services/documentService.js b/pocket-base/bai_api_pb_hooks/bai_api_shared/services/documentService.js
index bbb8796..32703ab 100644
--- a/pocket-base/bai_api_pb_hooks/bai_api_shared/services/documentService.js
+++ b/pocket-base/bai_api_pb_hooks/bai_api_shared/services/documentService.js
@@ -171,6 +171,39 @@ function findAttachmentRecordByAttachmentId(attachmentId) {
return records.length ? records[0] : null
}
+function fetchAllRecordsByFilter(collectionName, filter, sort, params) {
+ const batchSize = 200
+ const result = []
+ let offset = 0
+
+ while (true) {
+ const batch = $app.findRecordsByFilter(
+ collectionName,
+ filter || '',
+ sort || '',
+ batchSize,
+ offset,
+ params || {}
+ )
+
+ if (!batch.length) {
+ break
+ }
+
+ for (let i = 0; i < batch.length; i += 1) {
+ result.push(batch[i])
+ }
+
+ if (batch.length < batchSize) {
+ break
+ }
+
+ offset += batch.length
+ }
+
+ return result
+}
+
function resolveAttachmentList(value) {
const ids = parseAttachmentIdList(value)
const attachments = []
@@ -402,7 +435,7 @@ function deleteAttachment(attachmentId) {
throw createAppError(404, '未找到待删除的附件')
}
- const documentRecords = $app.findRecordsByFilter('tbl_document', '', '', 500, 0)
+ const documentRecords = fetchAllRecordsByFilter('tbl_document', '', '')
for (let i = 0; i < documentRecords.length; i += 1) {
const current = documentRecords[i]
const imageIds = parseAttachmentIdList(current.getString('document_image'))
@@ -434,7 +467,7 @@ function deleteAttachment(attachmentId) {
}
function listDocuments(payload) {
- const allRecords = $app.findRecordsByFilter('tbl_document', '', '', 500, 0)
+ const allRecords = fetchAllRecordsByFilter('tbl_document', '', '')
const titleKeyword = String(payload.title_keyword || '').toLowerCase().trim()
const status = String(payload.status || '')
const type = String(payload.document_type || '')
diff --git a/pocket-base/bai_api_pb_hooks/bai_api_shared/services/productService.js b/pocket-base/bai_api_pb_hooks/bai_api_shared/services/productService.js
index d046510..c6bd8ad 100644
--- a/pocket-base/bai_api_pb_hooks/bai_api_shared/services/productService.js
+++ b/pocket-base/bai_api_pb_hooks/bai_api_shared/services/productService.js
@@ -9,10 +9,57 @@ function buildBusinessId(prefix) {
return prefix + '-' + new Date().getTime() + '-' + $security.randomString(6)
}
+function buildPocketBaseRecordId(raw) {
+ const normalized = normalizeText(raw).toLowerCase()
+ if (/^[a-z0-9]{15}$/.test(normalized)) {
+ return normalized
+ }
+
+ const fallback = String($security.randomString(15) || '').toLowerCase().replace(/[^a-z0-9]/g, 'a')
+ if (fallback.length >= 15) {
+ return fallback.slice(0, 15)
+ }
+
+ return (fallback + 'aaaaaaaaaaaaaaa').slice(0, 15)
+}
+
function normalizeText(value) {
return String(value || '').replace(/^\s+|\s+$/g, '')
}
+function fetchAllRecordsByFilter(collectionName, filter, sort, params) {
+ const batchSize = 200
+ const result = []
+ let offset = 0
+
+ while (true) {
+ const batch = $app.findRecordsByFilter(
+ collectionName,
+ filter || '',
+ sort || '',
+ batchSize,
+ offset,
+ params || {}
+ )
+
+ if (!batch.length) {
+ break
+ }
+
+ for (let i = 0; i < batch.length; i += 1) {
+ result.push(batch[i])
+ }
+
+ if (batch.length < batchSize) {
+ break
+ }
+
+ offset += batch.length
+ }
+
+ return result
+}
+
function normalizeOptionalNumberValue(value, fieldName) {
if (value === '' || value === null || typeof value === 'undefined') {
return null
@@ -562,7 +609,7 @@ function exportProductRecord(record, extra) {
function listProducts(payload) {
try {
- const allRecords = $app.findRecordsByFilter('tbl_product_list', '', '', 500, 0)
+ const allRecords = fetchAllRecordsByFilter('tbl_product_list', '', '')
const keyword = normalizeText(payload.keyword).toLowerCase()
const status = normalizeText(payload.status)
const category = normalizeText(payload.prod_list_category)
@@ -620,7 +667,7 @@ function getProductDetail(productId) {
throw createAppError(404, '未找到对应产品')
}
- const allRecords = $app.findRecordsByFilter('tbl_product_list', '', '', 500, 0)
+ const allRecords = fetchAllRecordsByFilter('tbl_product_list', '', '')
const allItems = []
for (let i = 0; i < allRecords.length; i += 1) {
allItems.push(exportProductRecord(allRecords[i]))
@@ -644,6 +691,7 @@ function createProduct(_userOpenid, payload) {
const collection = $app.findCollectionByNameOrId('tbl_product_list')
const record = new Record(collection)
+ record.set('id', buildPocketBaseRecordId(payload && payload.id))
record.set('prod_list_id', targetProductId)
record.set('prod_list_name', normalizeText(payload.prod_list_name))
record.set('prod_list_modelnumber', normalizeText(payload.prod_list_modelnumber))
diff --git a/pocket-base/bai_web_pb_hooks/views/cart-order-manage.html b/pocket-base/bai_web_pb_hooks/views/cart-order-manage.html
index b2ee0d7..14364cb 100644
--- a/pocket-base/bai_web_pb_hooks/views/cart-order-manage.html
+++ b/pocket-base/bai_web_pb_hooks/views/cart-order-manage.html
@@ -48,6 +48,14 @@
.profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
.field-block { display: grid; gap: 6px; }
.field-label { font-size: 13px; color: #64748b; }
+ .attachment-panel { border: 1px solid #dbe3f0; border-radius: 14px; padding: 12px; background: #f8fbff; }
+ .attachment-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
+ .attachment-preview { margin-top: 10px; display: flex; align-items: center; gap: 12px; min-height: 72px; }
+ .attachment-thumb { width: 72px; height: 72px; border-radius: 12px; border: 1px solid #dbe3f0; background: #fff; object-fit: cover; }
+ .attachment-empty-thumb { width: 72px; height: 72px; border-radius: 12px; border: 1px solid #dbe3f0; background: #fff; color: #94a3b8; display: flex; align-items: center; justify-content: center; font-size: 12px; text-align: center; padding: 8px; }
+ .attachment-meta { min-width: 0; display: grid; gap: 4px; }
+ .attachment-link { color: #2563eb; text-decoration: none; font-size: 13px; font-weight: 600; word-break: break-all; }
+ .attachment-hidden-input { display: none; }
.detail-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
.section + .section { margin-top: 14px; }
.section-title { margin: 0 0 10px; font-size: 18px; color: #0f172a; }
@@ -113,6 +121,12 @@
users: [],
selectedOpenid: '',
userLevelOptions: [],
+ attachmentDetails: {
+ userUsersPicture: null,
+ userUsersIdPicA: null,
+ userUsersIdPicB: null,
+ userUsersTitlePicture: null,
+ },
}
const statusEl = document.getElementById('status')
@@ -172,6 +186,144 @@
}) || null
}
+ async function parseJsonSafe(res) {
+ const contentType = String(res.headers.get('content-type') || '').toLowerCase()
+ const rawText = await res.text()
+ const isJson = contentType.indexOf('application/json') !== -1
+
+ if (!rawText) {
+ return { json: null, text: '', isJson: false }
+ }
+
+ if (isJson) {
+ try {
+ return { json: JSON.parse(rawText), text: rawText, isJson: true }
+ } catch (_error) {}
+ }
+
+ return { json: null, text: rawText, isJson: false }
+ }
+
+ async function uploadAttachment(file, fieldKey) {
+ const token = getToken()
+ if (!token) {
+ window.location.replace('/pb/manage/login')
+ throw new Error('登录状态已失效,请重新登录')
+ }
+
+ const form = new FormData()
+ form.append('attachments_link', file)
+ form.append('attachments_filename', file.name || '')
+ form.append('attachments_filetype', file.type || '')
+ form.append('attachments_size', String(file.size || 0))
+ form.append('attachments_status', 'active')
+ form.append('attachments_remark', 'cart-order-manage:' + fieldKey)
+
+ const res = await fetch(API_BASE + '/attachment/upload', {
+ method: 'POST',
+ headers: {
+ Authorization: 'Bearer ' + token,
+ },
+ body: form,
+ })
+
+ const parsed = await parseJsonSafe(res)
+ const data = parsed.json || {}
+ if (!res.ok) {
+ if (res.status === 413) {
+ throw new Error('上传图片失败:文件过大或服务端 bodyLimit 未生效')
+ }
+ if (!parsed.isJson && parsed.text) {
+ throw new Error('上传图片失败:服务端返回了非 JSON 响应')
+ }
+ throw new Error((data && (data.errMsg || data.message)) || '上传图片失败')
+ }
+
+ return data
+ }
+
+ async function loadAttachmentDetail(attachmentId) {
+ const id = normalizeText(attachmentId)
+ if (!id) {
+ return null
+ }
+
+ try {
+ return await requestJson('/attachment/detail', { attachments_id: id })
+ } catch (_error) {
+ return {
+ attachments_id: id,
+ attachments_filename: id,
+ attachments_url: '',
+ }
+ }
+ }
+
+ async function refreshSelectedAttachmentDetails() {
+ const user = getSelectedUser()
+ if (!user) {
+ state.attachmentDetails = {
+ userUsersPicture: null,
+ userUsersIdPicA: null,
+ userUsersIdPicB: null,
+ userUsersTitlePicture: null,
+ }
+ renderDetail()
+ return
+ }
+
+ const currentOpenid = normalizeText(user.openid)
+ const results = await Promise.all([
+ loadAttachmentDetail(user.users_picture),
+ loadAttachmentDetail(user.users_id_pic_a),
+ loadAttachmentDetail(user.users_id_pic_b),
+ loadAttachmentDetail(user.users_title_picture),
+ ])
+
+ if (normalizeText(state.selectedOpenid) !== currentOpenid) {
+ return
+ }
+
+ state.attachmentDetails = {
+ userUsersPicture: results[0],
+ userUsersIdPicA: results[1],
+ userUsersIdPicB: results[2],
+ userUsersTitlePicture: results[3],
+ }
+ renderDetail()
+ }
+
+ function renderAttachmentUploader(fieldId, label, value) {
+ const detail = state.attachmentDetails[fieldId] || null
+ const url = detail && detail.attachments_url ? detail.attachments_url : ''
+ const filename = detail && (detail.attachments_filename || detail.attachments_id)
+ ? (detail.attachments_filename || detail.attachments_id)
+ : normalizeText(value)
+
+ return '
'
+ + '
'
+ + '
'
+ + '
'
+ + '
'
+ + '
'
+ + ''
+ + ''
+ + '
'
+ + '
'
+ + (url
+ ? '
 + ')
'
+ : '
暂无预览
')
+ + '
'
+ + '
'
+ + '
'
+ + '
'
+ }
+
function renderUserList() {
if (!state.users.length) {
userListEl.innerHTML = '暂无匹配用户。
'
@@ -248,11 +400,11 @@
+ ''
+ ''
+ ''
- + ''
- + ''
- + ''
- + ''
+ ''
+ + renderAttachmentUploader('userUsersPicture', '头像附件ID', user.users_picture || '')
+ + renderAttachmentUploader('userUsersIdPicA', '证件正面附件ID', user.users_id_pic_a || '')
+ + renderAttachmentUploader('userUsersIdPicB', '证件反面附件ID', user.users_id_pic_b || '')
+ + renderAttachmentUploader('userUsersTitlePicture', '资质附件ID', user.users_title_picture || '')
+ ''
+ ''
}
@@ -321,6 +473,7 @@
}
renderUserList()
renderDetail()
+ refreshSelectedAttachmentDetails()
setStatus('加载完成,共 ' + state.users.length + ' 位用户。', 'success')
} catch (err) {
renderUserList()
@@ -375,6 +528,35 @@
}
}
+ async function handleAttachmentUpload(fieldId, file) {
+ if (!file) {
+ return
+ }
+
+ const input = document.getElementById(fieldId)
+ if (!input) {
+ return
+ }
+
+ const labelMap = {
+ userUsersPicture: '头像',
+ userUsersIdPicA: '证件正面',
+ userUsersIdPicB: '证件反面',
+ userUsersTitlePicture: '资质附件',
+ }
+
+ try {
+ setStatus('正在上传' + (labelMap[fieldId] || '附件') + '...', '')
+ const uploaded = await uploadAttachment(file, fieldId)
+ input.value = uploaded.attachments_id || ''
+ state.attachmentDetails[fieldId] = uploaded
+ renderDetail()
+ setStatus((labelMap[fieldId] || '附件') + '上传成功,请点击“保存用户信息”完成写入。', 'success')
+ } catch (err) {
+ setStatus(err.message || '上传附件失败', 'error')
+ }
+ }
+
userListEl.addEventListener('click', function (event) {
const target = event.target && event.target.closest ? event.target.closest('[data-openid]') : null
if (!target) {
@@ -383,13 +565,55 @@
state.selectedOpenid = normalizeText(target.getAttribute('data-openid'))
renderUserList()
renderDetail()
+ refreshSelectedAttachmentDetails()
})
detailWrapEl.addEventListener('click', function (event) {
const target = event.target
if (target && target.id === 'saveUserBtn') {
saveSelectedUser()
+ return
}
+
+ if (target && target.getAttribute) {
+ const uploadTrigger = target.getAttribute('data-upload-trigger')
+ if (uploadTrigger) {
+ const fileInput = document.getElementById(uploadTrigger + 'File')
+ if (fileInput) {
+ fileInput.click()
+ }
+ return
+ }
+
+ const clearAttachment = target.getAttribute('data-clear-attachment')
+ if (clearAttachment) {
+ const input = document.getElementById(clearAttachment)
+ const fileInput = document.getElementById(clearAttachment + 'File')
+ if (input) {
+ input.value = ''
+ }
+ if (fileInput) {
+ fileInput.value = ''
+ }
+ state.attachmentDetails[clearAttachment] = null
+ renderDetail()
+ }
+ }
+ })
+
+ detailWrapEl.addEventListener('change', function (event) {
+ const target = event.target
+ if (!target || !target.getAttribute) {
+ return
+ }
+
+ const fieldId = target.getAttribute('data-upload-field')
+ if (!fieldId) {
+ return
+ }
+
+ const file = target.files && target.files[0] ? target.files[0] : null
+ handleAttachmentUpload(fieldId, file)
})
document.getElementById('searchBtn').addEventListener('click', loadUsers)
diff --git a/pocket-base/bai_web_pb_hooks/views/dictionary-manage.html b/pocket-base/bai_web_pb_hooks/views/dictionary-manage.html
index 6f9db2b..296a415 100644
--- a/pocket-base/bai_web_pb_hooks/views/dictionary-manage.html
+++ b/pocket-base/bai_web_pb_hooks/views/dictionary-manage.html
@@ -28,7 +28,7 @@
.btn-danger { background: #dc2626; color: #fff; }
.btn-light { background: #f8fafc; color: #334155; border: 1px solid #dbe3f0; }
.panel { border-radius: 20px; padding: 18px; }
- .toolbar { display: grid; grid-template-columns: 1.3fr 1fr 1fr auto auto; gap: 12px; margin-bottom: 18px; }
+ .toolbar { display: grid; grid-template-columns: 1.3fr auto 1fr 1fr auto auto; gap: 12px; margin-bottom: 18px; }
input, textarea, select { width: 100%; border: 1px solid #cbd5e1; border-radius: 12px; padding: 10px 12px; font-size: 14px; background: #fff; }
textarea { min-height: 88px; resize: vertical; }
table { width: 100%; border-collapse: collapse; overflow: hidden; border-radius: 16px; }
@@ -113,6 +113,7 @@
+
@@ -217,6 +218,7 @@
const statusEl = document.getElementById('status')
const keywordInput = document.getElementById('keywordInput')
+ const categorySelect = document.getElementById('categorySelect')
const detailInput = document.getElementById('detailInput')
const tableBody = document.getElementById('tableBody')
const editorModal = document.getElementById('editorModal')
@@ -405,6 +407,44 @@
.replace(/'/g, ''')
}
+ function extractCategory(dictName) {
+ var name = String(dictName || '')
+ if (name.length >= 2) {
+ return name.substring(0, 2)
+ }
+ return ''
+ }
+
+ function populateCategorySelect(list) {
+ var categories = {}
+ for (var i = 0; i < list.length; i++) {
+ var cat = extractCategory(list[i].dict_name)
+ if (cat) {
+ categories[cat] = true
+ }
+ }
+ var sorted = Object.keys(categories).sort()
+ var current = categorySelect.value
+ categorySelect.innerHTML = ''
+ for (var j = 0; j < sorted.length; j++) {
+ var opt = document.createElement('option')
+ opt.value = sorted[j]
+ opt.textContent = sorted[j]
+ categorySelect.appendChild(opt)
+ }
+ if (current && categories[current]) {
+ categorySelect.value = current
+ }
+ }
+
+ function filterByCategory(list) {
+ var selected = categorySelect.value
+ if (!selected) return list
+ return list.filter(function (item) {
+ return extractCategory(item.dict_name) === selected
+ })
+ }
+
function randomEnumSeed() {
const first = enumChars[Math.floor(Math.random() * enumChars.length)]
const second = enumChars[Math.floor(Math.random() * enumChars.length)]
@@ -775,7 +815,8 @@
const data = await request(API_BASE + '/dictionary/list', { keyword: keywordInput.value.trim() })
state.list = data.items || []
state.expandedPreviewKey = ''
- renderTable(state.list)
+ populateCategorySelect(state.list)
+ renderTable(filterByCategory(state.list))
setStatus('查询成功,共 ' + state.list.length + ' 条。', 'success')
} catch (err) {
setStatus(err.message || '查询失败', 'error')
@@ -797,7 +838,8 @@
const data = await request(API_BASE + '/dictionary/detail', { dict_name: dictName })
state.list = [data]
state.expandedPreviewKey = ''
- renderTable(state.list)
+ populateCategorySelect(state.list)
+ renderTable(filterByCategory(state.list))
setStatus('查询详情成功。', 'success')
} catch (err) {
setStatus(err.message || '查询失败', 'error')
@@ -1108,9 +1150,13 @@
})
document.getElementById('listBtn').addEventListener('click', loadList)
+ categorySelect.addEventListener('change', function () {
+ renderTable(filterByCategory(state.list))
+ })
document.getElementById('detailBtn').addEventListener('click', loadDetail)
document.getElementById('resetBtn').addEventListener('click', function () {
keywordInput.value = ''
+ categorySelect.value = ''
detailInput.value = ''
state.list = []
renderTable([])
diff --git a/pocket-base/bai_web_pb_hooks/views/document-manage.html b/pocket-base/bai_web_pb_hooks/views/document-manage.html
index 0fff527..471d9a3 100644
--- a/pocket-base/bai_web_pb_hooks/views/document-manage.html
+++ b/pocket-base/bai_web_pb_hooks/views/document-manage.html
@@ -84,6 +84,16 @@
.loading-card { min-width: min(92vw, 360px); padding: 24px 22px; border-radius: 20px; background: rgba(255,255,255,0.98); box-shadow: 0 28px 70px rgba(15, 23, 42, 0.22); border: 1px solid #dbe3f0; text-align: center; }
.loading-spinner { width: 44px; height: 44px; margin: 0 auto 14px; border-radius: 999px; border: 4px solid #dbeafe; border-top-color: #2563eb; animation: docSpin 0.9s linear infinite; }
.loading-text { color: #0f172a; font-size: 15px; font-weight: 700; }
+ .pagination { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
+ .pagination-info { color: #475569; font-size: 14px; }
+ .pagination-actions { display: flex; flex-wrap: wrap; gap: 8px; }
+ .pagination-size { display: inline-flex; align-items: center; gap: 8px; color: #475569; font-size: 14px; }
+ .pagination-btn { min-width: 42px; padding: 8px 12px; border-radius: 10px; border: 1px solid #dbe3f0; background: #fff; color: #334155; cursor: pointer; font-weight: 600; }
+ .pagination-select { min-width: 88px; width: auto; padding: 8px 12px; border-radius: 10px; border: 1px solid #dbe3f0; background: #fff; color: #334155; font-size: 14px; }
+ .pagination-btn.active { background: #2563eb; border-color: #2563eb; color: #fff; }
+ .pagination-btn:disabled { cursor: not-allowed; color: #94a3b8; background: #f8fafc; }
+ .pagination-btn:not(:disabled):hover { border-color: #94a3b8; background: #f8fafc; }
+ .pagination-btn.active:hover { background: #2563eb; border-color: #2563eb; }
html[data-theme="dark"] .editor-banner { background: rgba(30, 41, 59, 0.86); color: #bfdbfe; }
html[data-theme="dark"] .file-box { background: rgba(15, 23, 42, 0.92); border-color: rgba(148, 163, 184, 0.22); box-shadow: inset 0 0 0 1px rgba(148, 163, 184, 0.08); }
html[data-theme="dark"] .choice-switch { background: rgba(15, 23, 42, 0.86); border-color: rgba(148, 163, 184, 0.26); }
@@ -97,6 +107,15 @@
html[data-theme="dark"] .file-preview,
html[data-theme="dark"] .file-card-icon { background: rgba(2, 6, 23, 0.9); border-color: rgba(148, 163, 184, 0.24); color: #e2e8f0; }
html[data-theme="dark"] .file-card-icon:hover { background: rgba(30, 41, 59, 0.82); border-color: rgba(148, 163, 184, 0.4); }
+ html[data-theme="dark"] .pagination-info,
+ html[data-theme="dark"] .pagination-size { color: #cbd5e1; }
+ html[data-theme="dark"] .pagination-btn { background: rgba(15, 23, 42, 0.92); border-color: rgba(148, 163, 184, 0.24); color: #e2e8f0; box-shadow: inset 0 1px 0 rgba(255,255,255,0.04); }
+ html[data-theme="dark"] .pagination-btn:not(:disabled):hover { background: rgba(30, 41, 59, 0.96); border-color: rgba(96, 165, 250, 0.5); color: #f8fafc; }
+ html[data-theme="dark"] .pagination-btn.active,
+ html[data-theme="dark"] .pagination-btn.active:hover { background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%); border-color: #3b82f6; color: #eff6ff; box-shadow: 0 10px 24px rgba(37, 99, 235, 0.28); }
+ html[data-theme="dark"] .pagination-btn:disabled { background: rgba(15, 23, 42, 0.58); border-color: rgba(71, 85, 105, 0.45); color: #64748b; box-shadow: none; }
+ html[data-theme="dark"] .pagination-select { background: rgba(15, 23, 42, 0.92); border-color: rgba(148, 163, 184, 0.24); color: #f8fafc; box-shadow: inset 0 1px 0 rgba(255,255,255,0.04); }
+ html[data-theme="dark"] .pagination-select:focus { outline: none; border-color: #60a5fa; box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.18); }
@keyframes docSpin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@media (max-width: 960px) {
.grid, .file-group { grid-template-columns: 1fr; }
@@ -295,6 +314,7 @@
+
@@ -342,6 +362,7 @@
const hotelTypeTagsEl = document.getElementById('hotelTypeTags')
const listTitleKeywordEl = document.getElementById('listTitleKeyword')
const listTypeFilterEl = document.getElementById('listTypeFilter')
+ const listPaginationEl = document.getElementById('listPagination')
const imageViewerEl = document.getElementById('imageViewer')
const imageViewerImgEl = document.getElementById('imageViewerImg')
const loadingMaskEl = document.getElementById('loadingMask')
@@ -388,6 +409,7 @@
const state = {
list: [],
fullList: [],
+ filteredList: [],
mode: 'idle',
editingId: '',
editingSource: null,
@@ -413,6 +435,12 @@
titleKeyword: '',
type: '',
},
+ pagination: {
+ page: 1,
+ pageSize: 10,
+ total: 0,
+ totalPages: 1,
+ },
}
function setStatus(message, type) {
@@ -1005,6 +1033,102 @@
})
}
+ function clampPage(page, totalPages) {
+ const normalizedPage = Number(page) || 1
+ const normalizedTotalPages = Math.max(1, Number(totalPages) || 1)
+ return Math.min(Math.max(1, normalizedPage), normalizedTotalPages)
+ }
+
+ function getVisiblePageNumbers(page, totalPages) {
+ const result = []
+ const start = Math.max(1, page - 2)
+ const end = Math.min(totalPages, start + 4)
+ const adjustedStart = Math.max(1, end - 4)
+
+ for (let current = adjustedStart; current <= end; current += 1) {
+ result.push(current)
+ }
+
+ return result
+ }
+
+ function renderPageSizeSelector() {
+ const options = [10, 20, 50, 100, 500]
+ return ''
+ }
+
+ function renderPagination() {
+ if (!listPaginationEl) {
+ return
+ }
+
+ const total = Number(state.pagination.total || 0)
+ const page = Number(state.pagination.page || 1)
+ const totalPages = Math.max(1, Number(state.pagination.totalPages || 1))
+ const sizeSelector = renderPageSizeSelector()
+
+ if (!total) {
+ listPaginationEl.innerHTML = ''
+ + ''
+ return
+ }
+
+ const visiblePageNumbers = getVisiblePageNumbers(page, totalPages)
+ const startIndex = (page - 1) * state.pagination.pageSize + 1
+ const endIndex = Math.min(total, page * state.pagination.pageSize)
+ const buttons = []
+
+ buttons.push('')
+
+ if (visiblePageNumbers[0] > 1) {
+ buttons.push('')
+ if (visiblePageNumbers[0] > 2) {
+ buttons.push('')
+ }
+ }
+
+ for (let i = 0; i < visiblePageNumbers.length; i += 1) {
+ const current = visiblePageNumbers[i]
+ buttons.push('')
+ }
+
+ if (visiblePageNumbers.length && visiblePageNumbers[visiblePageNumbers.length - 1] < totalPages) {
+ if (visiblePageNumbers[visiblePageNumbers.length - 1] < totalPages - 1) {
+ buttons.push('')
+ }
+ buttons.push('')
+ }
+
+ buttons.push('')
+
+ listPaginationEl.innerHTML = ''
+ + ''
+ }
+
+ function updateDocumentListView() {
+ const filteredList = applyListFiltersToList(state.fullList)
+ const pageSize = Math.max(1, Number(state.pagination.pageSize || 10))
+ const total = filteredList.length
+ const totalPages = Math.max(1, Math.ceil(total / pageSize))
+ const currentPage = clampPage(state.pagination.page, totalPages)
+ const startIndex = (currentPage - 1) * pageSize
+
+ state.filteredList = filteredList
+ state.pagination.page = currentPage
+ state.pagination.total = total
+ state.pagination.totalPages = totalPages
+ state.list = filteredList.slice(startIndex, startIndex + pageSize)
+
+ renderTable()
+ renderPagination()
+ }
+
function renderTable() {
if (!state.list.length) {
tableBody.innerHTML = '| 暂无文档数据。 |
'
@@ -1132,6 +1256,27 @@
}
}
+ function goToDocumentPage(page) {
+ const nextPage = clampPage(page, state.pagination.totalPages)
+ if (nextPage === state.pagination.page) {
+ return
+ }
+
+ state.pagination.page = nextPage
+ updateDocumentListView()
+ }
+
+ function setDocumentPageSize(pageSize) {
+ const nextPageSize = Number(pageSize) || 10
+ if (nextPageSize === state.pagination.pageSize) {
+ return
+ }
+
+ state.pagination.pageSize = nextPageSize
+ state.pagination.page = 1
+ updateDocumentListView()
+ }
+
async function loadDocuments() {
setStatus('正在加载文档列表...', '')
showLoading('正在加载文档列表...')
@@ -1140,9 +1285,8 @@
document_type: state.listFilters.type,
})
state.fullList = Array.isArray(data.items) ? data.items : []
- state.list = applyListFiltersToList(state.fullList)
- renderTable()
- setStatus('文档列表已刷新,共 ' + state.list.length + ' 条。', 'success')
+ updateDocumentListView()
+ setStatus('文档列表已刷新,共 ' + state.pagination.total + ' 条。', 'success')
} catch (err) {
setStatus(err.message || '加载列表失败', 'error')
} finally {
@@ -1177,8 +1321,11 @@
}
}
- async function queryDocumentsWithAutoSave() {
+ async function queryDocumentsWithAutoSave(resetPage) {
syncListFiltersFromInputs()
+ if (resetPage) {
+ state.pagination.page = 1
+ }
await saveAndHideEditorBeforeListQuery()
applyListFiltersToInputs()
await loadDocuments()
@@ -1490,23 +1637,33 @@
updateSelection(fieldName, target.value, !!target.checked)
})
- document.getElementById('reloadBtn').addEventListener('click', queryDocumentsWithAutoSave)
- document.getElementById('searchBtn').addEventListener('click', queryDocumentsWithAutoSave)
+ window.__gotoDocumentPage = goToDocumentPage
+ window.__setDocumentPageSize = setDocumentPageSize
+
+ document.getElementById('reloadBtn').addEventListener('click', function () {
+ queryDocumentsWithAutoSave(false)
+ })
+ document.getElementById('searchBtn').addEventListener('click', function () {
+ queryDocumentsWithAutoSave(true)
+ })
document.getElementById('clearSearchBtn').addEventListener('click', function () {
state.listFilters.titleKeyword = ''
state.listFilters.type = ''
+ state.pagination.page = 1
applyListFiltersToInputs()
- queryDocumentsWithAutoSave()
+ queryDocumentsWithAutoSave(false)
})
if (listTitleKeywordEl) {
listTitleKeywordEl.addEventListener('keydown', function (event) {
if (event.key === 'Enter') {
- queryDocumentsWithAutoSave()
+ queryDocumentsWithAutoSave(true)
}
})
}
if (listTypeFilterEl) {
- listTypeFilterEl.addEventListener('change', queryDocumentsWithAutoSave)
+ listTypeFilterEl.addEventListener('change', function () {
+ queryDocumentsWithAutoSave(true)
+ })
}
document.getElementById('createModeBtn').addEventListener('click', function () {
enterCreateMode()
diff --git a/pocket-base/bai_web_pb_hooks/views/product-manage.html b/pocket-base/bai_web_pb_hooks/views/product-manage.html
index ca49c01..e8248e2 100644
--- a/pocket-base/bai_web_pb_hooks/views/product-manage.html
+++ b/pocket-base/bai_web_pb_hooks/views/product-manage.html
@@ -76,6 +76,25 @@
.loading-card { min-width: min(92vw, 360px); padding: 24px 22px; border-radius: 20px; background: rgba(255,255,255,0.98); box-shadow: 0 28px 70px rgba(15, 23, 42, 0.22); border: 1px solid #dbe3f0; text-align: center; }
.loading-spinner { width: 44px; height: 44px; margin: 0 auto 14px; border-radius: 999px; border: 4px solid #dbeafe; border-top-color: #2563eb; animation: spin 0.9s linear infinite; }
.loading-text { color: #0f172a; font-size: 15px; font-weight: 700; }
+ .pagination { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; margin-top: 16px; }
+ .pagination-info { color: #475569; font-size: 14px; }
+ .pagination-actions { display: flex; flex-wrap: wrap; gap: 8px; }
+ .pagination-size { display: inline-flex; align-items: center; gap: 8px; color: #475569; font-size: 14px; }
+ .pagination-btn { min-width: 42px; padding: 8px 12px; border-radius: 10px; border: 1px solid #dbe3f0; background: #fff; color: #334155; cursor: pointer; font-weight: 600; }
+ .pagination-select { min-width: 88px; width: auto; padding: 8px 12px; border-radius: 10px; border: 1px solid #dbe3f0; background: #fff; color: #334155; font-size: 14px; }
+ .pagination-btn.active { background: #2563eb; border-color: #2563eb; color: #fff; }
+ .pagination-btn:disabled { cursor: not-allowed; color: #94a3b8; background: #f8fafc; }
+ .pagination-btn:not(:disabled):hover { border-color: #94a3b8; background: #f8fafc; }
+ .pagination-btn.active:hover { background: #2563eb; border-color: #2563eb; }
+ html[data-theme="dark"] .pagination-info,
+ html[data-theme="dark"] .pagination-size { color: #cbd5e1; }
+ html[data-theme="dark"] .pagination-btn { background: rgba(15, 23, 42, 0.92); border-color: rgba(148, 163, 184, 0.24); color: #e2e8f0; box-shadow: inset 0 1px 0 rgba(255,255,255,0.04); }
+ html[data-theme="dark"] .pagination-btn:not(:disabled):hover { background: rgba(30, 41, 59, 0.96); border-color: rgba(96, 165, 250, 0.5); color: #f8fafc; }
+ html[data-theme="dark"] .pagination-btn.active,
+ html[data-theme="dark"] .pagination-btn.active:hover { background: linear-gradient(180deg, #3b82f6 0%, #2563eb 100%); border-color: #3b82f6; color: #eff6ff; box-shadow: 0 10px 24px rgba(37, 99, 235, 0.28); }
+ html[data-theme="dark"] .pagination-btn:disabled { background: rgba(15, 23, 42, 0.58); border-color: rgba(71, 85, 105, 0.45); color: #64748b; box-shadow: none; }
+ html[data-theme="dark"] .pagination-select { background: rgba(15, 23, 42, 0.92); border-color: rgba(148, 163, 184, 0.24); color: #f8fafc; box-shadow: inset 0 1px 0 rgba(255,255,255,0.04); }
+ html[data-theme="dark"] .pagination-select:focus { outline: none; border-color: #60a5fa; box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.18); }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
@media (max-width: 960px) {
.grid, .toolbar, .option-list { grid-template-columns: 1fr; }
@@ -309,6 +328,7 @@
+
@@ -375,6 +395,7 @@
const statusFilterEl = document.getElementById('statusFilter')
const categoryFilterEl = document.getElementById('categoryFilter')
const prodListSortHintEl = document.getElementById('prodListSortHint')
+ const listPaginationEl = document.getElementById('listPagination')
const fields = {
keyword: document.getElementById('keywordInput'),
@@ -404,6 +425,7 @@
const state = {
list: [],
+ fullList: [],
mode: 'idle',
editingId: '',
editingSource: null,
@@ -423,6 +445,12 @@
currentIconAttachments: [],
pendingIconFiles: [],
copySourceProductId: '',
+ pagination: {
+ page: 1,
+ pageSize: 10,
+ total: 0,
+ totalPages: 1,
+ },
}
const multiFieldConfig = {
@@ -609,24 +637,42 @@
}
}
- async function requestProductListFallback(payload) {
+ async function fetchAllFallbackProductRecords() {
const token = getToken()
- const res = await fetch(getApiUrl('/collections/tbl_product_list/records?page=1&perPage=500&sort=-updated'), {
- method: 'GET',
- headers: {
- Authorization: token ? ('Bearer ' + token) : '',
- },
- })
+ const perPage = 200
+ const result = []
+ let page = 1
- const parsed = await parseJsonSafe(res)
- if (!res.ok || !parsed.isJson || !parsed.json) {
- throw new Error('产品列表回退查询失败')
+ while (true) {
+ const res = await fetch(getApiUrl('/collections/tbl_product_list/records?page=' + page + '&perPage=' + perPage + '&sort=-updated'), {
+ method: 'GET',
+ headers: {
+ Authorization: token ? ('Bearer ' + token) : '',
+ },
+ })
+
+ const parsed = await parseJsonSafe(res)
+ if (!res.ok || !parsed.isJson || !parsed.json) {
+ throw new Error('产品列表回退查询失败')
+ }
+
+ const rawItems = Array.isArray(parsed.json.items) ? parsed.json.items : []
+ for (let i = 0; i < rawItems.length; i += 1) {
+ result.push(normalizeFallbackProductRecord(rawItems[i]))
+ }
+
+ if (rawItems.length < perPage) {
+ break
+ }
+
+ page += 1
}
- const rawItems = Array.isArray(parsed.json.items) ? parsed.json.items : []
- const normalized = rawItems.map(function (item) {
- return normalizeFallbackProductRecord(item)
- })
+ return result
+ }
+
+ async function requestProductListFallback(payload) {
+ const normalized = await fetchAllFallbackProductRecords()
const keyword = normalizeText(payload && payload.keyword).toLowerCase()
const status = normalizeText(payload && payload.status)
@@ -656,6 +702,101 @@
return filtered
}
+ function clampPage(page, totalPages) {
+ const normalizedPage = Number(page) || 1
+ const normalizedTotalPages = Math.max(1, Number(totalPages) || 1)
+ return Math.min(Math.max(1, normalizedPage), normalizedTotalPages)
+ }
+
+ function getVisiblePageNumbers(page, totalPages) {
+ const result = []
+ const start = Math.max(1, page - 2)
+ const end = Math.min(totalPages, start + 4)
+ const adjustedStart = Math.max(1, end - 4)
+
+ for (let current = adjustedStart; current <= end; current += 1) {
+ result.push(current)
+ }
+
+ return result
+ }
+
+ function renderPageSizeSelector() {
+ const options = [10, 20, 50, 100, 500]
+ return ''
+ }
+
+ function renderPagination() {
+ if (!listPaginationEl) {
+ return
+ }
+
+ const total = Number(state.pagination.total || 0)
+ const page = Number(state.pagination.page || 1)
+ const totalPages = Math.max(1, Number(state.pagination.totalPages || 1))
+ const sizeSelector = renderPageSizeSelector()
+
+ if (!total) {
+ listPaginationEl.innerHTML = ''
+ + ''
+ return
+ }
+
+ const visiblePageNumbers = getVisiblePageNumbers(page, totalPages)
+ const startIndex = (page - 1) * state.pagination.pageSize + 1
+ const endIndex = Math.min(total, page * state.pagination.pageSize)
+ const buttons = []
+
+ buttons.push('')
+
+ if (visiblePageNumbers[0] > 1) {
+ buttons.push('')
+ if (visiblePageNumbers[0] > 2) {
+ buttons.push('')
+ }
+ }
+
+ for (let i = 0; i < visiblePageNumbers.length; i += 1) {
+ const current = visiblePageNumbers[i]
+ buttons.push('')
+ }
+
+ if (visiblePageNumbers.length && visiblePageNumbers[visiblePageNumbers.length - 1] < totalPages) {
+ if (visiblePageNumbers[visiblePageNumbers.length - 1] < totalPages - 1) {
+ buttons.push('')
+ }
+ buttons.push('')
+ }
+
+ buttons.push('')
+
+ listPaginationEl.innerHTML = ''
+ + ''
+ }
+
+ function updateProductListView() {
+ const pageSize = Math.max(1, Number(state.pagination.pageSize || 10))
+ const total = state.fullList.length
+ const totalPages = Math.max(1, Math.ceil(total / pageSize))
+ const currentPage = clampPage(state.pagination.page, totalPages)
+ const startIndex = (currentPage - 1) * pageSize
+
+ state.pagination.page = currentPage
+ state.pagination.total = total
+ state.pagination.totalPages = totalPages
+ state.list = state.fullList.slice(startIndex, startIndex + pageSize)
+
+ renderTable()
+ renderPagination()
+ renderSortRankHint()
+ }
+
async function parseJsonSafe(res) {
const contentType = String(res.headers.get('content-type') || '').toLowerCase()
const rawText = await res.text()
@@ -713,6 +854,16 @@
return String(value || '').trim()
}
+ function generatePocketBaseRecordId() {
+ const alphabet = 'abcdefghijklmnopqrstuvwxyz0123456789'
+ let output = ''
+ for (let i = 0; i < 15; i += 1) {
+ const idx = Math.floor(Math.random() * alphabet.length)
+ output += alphabet[idx]
+ }
+ return output
+ }
+
function splitPipe(value) {
return String(value || '')
.split('|')
@@ -858,7 +1009,7 @@
const targetSort = Math.floor(sortValue)
const targetId = state.mode === 'edit' ? normalizeText(state.editingId) : '__draft__'
- const sameCategoryItems = state.list
+ const sameCategoryItems = state.fullList
.filter(function (item) {
return normalizeText(item.prod_list_category) === category && normalizeText(item.prod_list_id) !== targetId
})
@@ -1714,6 +1865,7 @@
function buildCopyPayload(source, nextName, nextModel) {
return {
+ id: generatePocketBaseRecordId(),
prod_list_id: '',
prod_list_name: normalizeText(nextName),
prod_list_modelnumber: normalizeText(nextModel),
@@ -1761,7 +1913,7 @@
}
async function copyProduct(productId) {
- const source = state.list.find(function (item) {
+ const source = state.fullList.find(function (item) {
return normalizeText(item.prod_list_id) === normalizeText(productId)
})
@@ -1781,7 +1933,7 @@
return
}
- const source = state.list.find(function (item) {
+ const source = state.fullList.find(function (item) {
return normalizeText(item.prod_list_id) === sourceId
})
if (!source) {
@@ -1830,20 +1982,18 @@
status: payload.status,
prod_list_category: payload.prod_list_category,
})
- state.list = Array.isArray(data.items) ? data.items : []
- renderTable()
- renderSortRankHint()
- setStatus('产品列表已刷新,共 ' + state.list.length + ' 条。', 'success')
+ state.fullList = Array.isArray(data.items) ? data.items : []
+ updateProductListView()
+ setStatus('产品列表已刷新,共 ' + state.pagination.total + ' 条。', 'success')
} catch (err) {
try {
- state.list = await requestProductListFallback({
+ state.fullList = await requestProductListFallback({
keyword: normalizeText(fields.keyword.value),
status: normalizeText(statusFilterEl.value),
prod_list_category: normalizeText(categoryFilterEl.value),
})
- renderTable()
- renderSortRankHint()
- setStatus('产品列表已通过回退链路刷新,共 ' + state.list.length + ' 条。', 'success')
+ updateProductListView()
+ setStatus('产品列表已通过回退链路刷新,共 ' + state.pagination.total + ' 条。', 'success')
} catch (_fallbackErr) {
setStatus(err.message || '加载产品列表失败', 'error')
}
@@ -1878,11 +2028,35 @@
}
}
- async function queryProductsWithAutoSave() {
+ async function queryProductsWithAutoSave(resetPage) {
+ if (resetPage) {
+ state.pagination.page = 1
+ }
await saveAndHideEditorBeforeProductQuery()
await loadProducts()
}
+ function goToProductPage(page) {
+ const nextPage = clampPage(page, state.pagination.totalPages)
+ if (nextPage === state.pagination.page) {
+ return
+ }
+
+ state.pagination.page = nextPage
+ updateProductListView()
+ }
+
+ function setProductPageSize(pageSize) {
+ const nextPageSize = Number(pageSize) || 10
+ if (nextPageSize === state.pagination.pageSize) {
+ return
+ }
+
+ state.pagination.pageSize = nextPageSize
+ state.pagination.page = 1
+ updateProductListView()
+ }
+
async function enterEditMode(productId) {
showLoading('正在加载产品详情...')
try {
@@ -1948,6 +2122,7 @@
}
const payload = {
+ id: state.mode === 'edit' ? '' : generatePocketBaseRecordId(),
prod_list_id: state.mode === 'edit' ? state.editingId : '',
prod_list_name: normalizeText(fields.name.value),
prod_list_modelnumber: normalizeText(fields.model.value),
@@ -2218,14 +2393,37 @@
}
})
+ window.__gotoProductPage = goToProductPage
+ window.__setProductPageSize = setProductPageSize
+
document.getElementById('reloadBtn').addEventListener('click', function () {
- queryProductsWithAutoSave()
+ queryProductsWithAutoSave(false)
})
document.getElementById('searchBtn').addEventListener('click', function () {
- queryProductsWithAutoSave()
+ queryProductsWithAutoSave(true)
})
+ if (fields.keyword) {
+ fields.keyword.addEventListener('keydown', function (event) {
+ if (event.key === 'Enter') {
+ queryProductsWithAutoSave(true)
+ }
+ })
+ }
+
+ if (statusFilterEl) {
+ statusFilterEl.addEventListener('change', function () {
+ queryProductsWithAutoSave(true)
+ })
+ }
+
+ if (categoryFilterEl) {
+ categoryFilterEl.addEventListener('change', function () {
+ queryProductsWithAutoSave(true)
+ })
+ }
+
document.getElementById('createModeBtn').addEventListener('click', function () {
enterCreateMode()
})
diff --git a/pocket-base/spec/openapi-wx.yaml b/pocket-base/spec/openapi-wx.yaml
index 0f609db..7773d3a 100644
--- a/pocket-base/spec/openapi-wx.yaml
+++ b/pocket-base/spec/openapi-wx.yaml
@@ -10,15 +10,17 @@ info:
- 除 `/pb/api/wechat/login` 外,调用购物车 / 订单的 PocketBase 原生 records API 时都需要在请求头中携带 `Authorization: Bearer `
- `token` 取自 `/pb/api/wechat/login` 成功返回的认证 token
- 小程序端应统一使用 HTTPS,不依赖 Cookie / Session
- - 购物车与订单当前文档展示的是 PocketBase 原生 `/pb/api/collections/.../records` 接口,不是自定义 hooks API
- - 按当前 collection 规则,创建 `tbl_cart` / `tbl_order` 记录时,客户端必须显式提交 owner 字段,且值必须等于当前 token 对应的 `openid`
+ - 购物车与订单同时提供两套调用说明:`/pb/api/cart/*`、`/pb/api/order/*` 为自定义 hooks API;`/pb/api/collections/.../records` 为 PocketBase 原生 records API
+ - 按当前 collection 规则,创建 `tbl_cart` / `tbl_order` 原生 records 时,客户端必须显式提交 owner 字段,且值必须等于当前 token 对应的 `openid`
+ - 当前仓库中 `DELETE` 对应的 hooks 实现仍是物理删除;若要满足“仅将 `is_delete` 标记为 `1`”的软删除契约,需同步调整后端服务实现
+ - 当前仓库中购物车 / 订单 hooks 列表与详情接口尚未显式按 `is_delete = 0` 过滤;若要完全满足软删除检索约束,需同步调整后端服务实现
license:
name: Proprietary
identifier: LicenseRef-Proprietary
servers:
- url: https://bai-api.blv-oa.com
description: 生产环境
- - url: http://localhost:8090
+ - url: http://127.0.0.1:8090
description: PocketBase 本地环境
tags:
- name: 系统
@@ -40,1462 +42,51 @@ tags:
security: []
paths:
/pb/api/system/users-count:
- post:
- security: []
- operationId: postSystemUsersCount
- tags:
- - 系统
- summary: 查询用户总数
- description: 统计 `tbl_auth_users` 集合中的记录总数。
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/UsersCountResponse'
- '400':
- description: 请求参数错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: './openapi-wx/paths/system.yaml#/usersCount'
/pb/api/system/refresh-token:
- post:
- security:
- - BearerAuth: []
- - {}
- operationId: postSystemRefreshToken
- tags:
- - 系统
- summary: 刷新认证 token
- description: |
- 当当前 `Authorization` 仍有效时,直接基于当前 auth 用户续签。
- 当 token 失效时,可传入 `users_wx_code` 走微信 code 重新签发。
- requestBody:
- required: false
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/SystemRefreshTokenRequest'
- responses:
- '200':
- description: 刷新成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/RefreshTokenResponse'
- '400':
- description: 参数错误或微信 code 换取失败
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: token 无效,且未提供有效的 `users_wx_code`
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '404':
- description: 当前用户不存在
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '415':
- description: 请求体不是 JSON
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: 请求过于频繁
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: './openapi-wx/paths/system.yaml#/refreshToken'
/pb/api/wechat/login:
- post:
- security: []
- operationId: postWechatLogin
- tags:
- - 微信认证
- summary: 微信登录或首次注册
- description: |
- 使用微信 `users_wx_code` 换取微信 openid。
- 若 `tbl_auth_users` 中不存在对应用户,则自动创建新 auth 用户并返回 token。
- 首次注册时,`users_level` 默认保持为空,不自动写入会员等级。
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/WechatLoginRequest'
- responses:
- '200':
- description: 登录或注册成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/AuthSuccessResponse'
- '400':
- description: 参数错误或保存用户失败
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: 认证失败
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '415':
- description: 请求体不是 JSON
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: 请求过于频繁
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: './openapi-wx/paths/wechat-auth.yaml#/wechatLogin'
/pb/api/wechat/profile:
- post:
- security:
- - BearerAuth: []
- operationId: postWechatProfile
- tags:
- - 微信认证
- summary: 更新微信用户资料
- description: |
- 基于当前 `Authorization` 对应的 auth 用户按“非空字段增量更新”资料。
-
- 更新规则:
- - 所有字段都不是必填
- - 如果传了 `users_phone_code`,服务端优先调用微信接口换取真实手机号并写入 `users_phone`
- - 如果没传 `users_phone_code`,但传了 `users_phone`,则直接将该手机号写入数据库
- - 如果上传了 `users_picture`、`users_id_pic_a`、`users_id_pic_b`、`users_title_picture`,会按附件 ID 进行关联校验并更新
- - 若当前用户类型为 `游客`,且本次未显式传 `users_type`,服务端会自动升级为 `注册用户`
- - 如果某个字段未传或传空,则不会清空数据库中的已有值
- - 只有请求体里非空的字段才会更新到数据库
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/WechatProfileRequest'
- responses:
- '200':
- description: 更新成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/WechatProfileResponse'
- '400':
- description: 参数错误、手机号已被占用、附件 ID 无效、微信手机号换取失败或资料更新失败
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '401':
- description: token 无效或缺少 openid
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '415':
- description: 请求体不是 JSON
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '429':
- description: 请求过于频繁
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/ErrorResponse'
+ $ref: './openapi-wx/paths/wechat-auth.yaml#/wechatProfile'
/pb/api/collections/tbl_company/records:
- post:
- operationId: postPocketBaseCompanyRecord
- tags:
- - 企业信息
- summary: 创建公司
- description: |
- 使用 PocketBase 原生 records create 接口向 `tbl_company` 新增一行记录。
-
- 当前线上权限规则:
- - `createRule = ""`,因此任何客户端都可直接创建
- - 其他原生操作中,`update/delete/view` 仅管理员或管理后台用户允许
-
- 注意:
- - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- - `company_id` 由数据库自动生成,客户端创建时不需要传
- - `company_id` 仍带唯一索引,可用于后续按业务 id 查询
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCompanyCreateRequest'
- examples:
- default:
- value:
- company_name: 微信侧测试企业
- company_type: 渠道商
- company_entity: 张三
- company_usci: '91310000123456789A'
- company_nationality: 中国
- company_nationality_code: CN
- company_province: 上海
- company_province_code: '310000'
- company_city: 上海
- company_city_code: '310100'
- company_district: 浦东新区
- company_district_code: '310115'
- company_postalcode: '200000'
- company_add: 上海市浦东新区XX路1号
- company_status: 有效
- company_level: A
- company_owner_openid: wx-openid-owner-001
- company_remark: 小程序公开创建示例
- responses:
- '200':
- description: 创建成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCompanyRecord'
- '400':
- description: 参数错误或违反当前集合约束
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- get:
- operationId: getPocketBaseCompanyRecords
- tags:
- - 企业信息
- summary: 查询整个 tbl_company 列表 / 根据 company_id 查询对应公司信息
- description: |
- 使用 PocketBase 原生 records list 接口查询 `tbl_company`。
-
- 当前线上权限规则:
- - `listRule = ""`,因此当前整个列表查询与条件查询都公开可读
- - `createRule = ""`,因此创建也公开可调用
- - `view/update/delete` 仅管理员或管理后台用户允许
-
- 标准调用方式有两种:
- 1. 根据 `company_id` 查询对应公司信息:
- - `filter=company_id="WX-COMPANY-10001"`
- - `perPage=1`
- - `page=1`
- 2. 查询整个 `tbl_company` 列表:
- - 不传 `filter`
- - 按需传 `page`、`perPage`
-
- 注意:
- - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- - PocketBase 原生标准接口里,“按 `company_id` 查询单条”和“查询整个列表”共用同一个 `GET /records` 路径,因此文档以同一个 GET operation 展示两种调用模式
- parameters:
- - name: filter
- in: query
- required: false
- description: |
- PocketBase 标准过滤表达式。
-
- - 根据 `company_id` 查询单条时:`company_id="WX-COMPANY-10001"`
- - 查询整个列表时:不传该参数
- schema:
- type: string
- example: company_id="WX-COMPANY-10001"
- - name: page
- in: query
- required: false
- description: 页码
- schema:
- type: integer
- minimum: 1
- default: 1
- - name: perPage
- in: query
- required: false
- description: 每页条数;按 `company_id` 单查时建议固定为 `1`
- schema:
- type: integer
- minimum: 1
- default: 20
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCompanyListResponse'
- examples:
- byCompanyId:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- listAll:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- notFoundByCompanyId:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items: []
- '400':
- description: 查询参数错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/company.yaml#/companyRecords'
/pb/api/collections/tbl_company/records/{recordId}:
- patch:
- operationId: patchPocketBaseCompanyRecordByRecordId
- tags:
- - 企业信息
- summary: 通过 company_id 定位后修改公司信息
- description: |
- 这是 PocketBase 原生标准更新接口,实际写入路径参数仍然必须使用记录主键 `recordId`。
-
- 如果前端手里只有 `company_id`,标准调用流程是:
- 1. 先调用 `GET /pb/api/collections/tbl_company/records?filter=company_id="..."&perPage=1&page=1`
- 2. 从返回结果 `items[0].id` 中取出 PocketBase 原生记录主键
- 3. 再调用当前 `PATCH /pb/api/collections/tbl_company/records/{recordId}` 完成更新
-
- 当前线上权限规则:
- - `updateRule` 仅管理员或管理后台用户允许
- - 普通公开调用不能直接更新
- parameters:
- - name: recordId
- in: path
- required: true
- description: 通过 `company_id` 查询结果拿到的 PocketBase 记录主键 `id`
- schema:
- type: string
- example: l2r3nq7rqhuob0h
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCompanyUpdateRequest'
- examples:
- default:
- value:
- company_name: 微信侧测试企业(更新)
- company_status: 有效
- company_level: S
- company_owner_openid: wx-openid-owner-001
- company_district: 徐汇区
- company_district_code: '310104'
- company_remark: 通过 company_id 先定位再修改
- responses:
- '200':
- description: 更新成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCompanyRecord'
- '400':
- description: 参数错误或违反集合约束
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 当前调用方没有 update 权限
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '404':
- description: 记录不存在
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/company.yaml#/companyRecordById'
/pb/api/collections/tbl_attachments/records:
- get:
- operationId: getPocketBaseAttachmentRecords
- tags:
- - 附件信息
- summary: 根据 attachments_id 查询单条或多条附件信息
- description: |
- 使用 PocketBase 原生 records list 接口查询 `tbl_attachments`。
-
- 当前线上权限规则:
- - `listRule = ""`,因此任何客户端都可直接读取
- - 原生 `create/update/delete` 仍仅管理员或管理后台用户允许
-
- 标准调用方式有两种:
- 1. 按 `attachments_id` 查询单条:
- - `filter=attachments_id="ATT-1774599142438-8n1UcU"`
- - `perPage=1`
- - `page=1`
- 2. 按多个 `attachments_id` 批量查询:
- - 使用 `||` 组合多个等值条件
- - 例如:`filter=attachments_id="ATT-1774599142438-8n1UcU" || attachments_id="ATT-1774599143999-7pQkLm"`
- - 传 `perPage` 为预期返回条数,`page=1`
-
- 注意:
- - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- - `attachments_link` 返回的是 PocketBase 文件字段值,不是完整下载地址
- - 若需文件流地址,可按 PocketBase 标准文件路径自行拼接:`/pb/api/files/{collectionId}/{recordId}/{attachments_link}`
- parameters:
- - name: filter
- in: query
- required: false
- description: |
- PocketBase 标准过滤表达式。
-
- - 按 `attachments_id` 精确查询单条:`attachments_id="ATT-1774599142438-8n1UcU"`
- - 按多个 `attachments_id` 批量查询:`attachments_id="ATT-1774599142438-8n1UcU" || attachments_id="ATT-1774599143999-7pQkLm"`
- - 不传该参数时,返回分页列表
- schema:
- type: string
- example: attachments_id="ATT-1774599142438-8n1UcU"
- - name: page
- in: query
- required: false
- description: 页码
- schema:
- type: integer
- minimum: 1
- default: 1
- - name: perPage
- in: query
- required: false
- description: 每页条数;单查建议为 `1`,批量查询建议设置为预期条数
- schema:
- type: integer
- minimum: 1
- default: 20
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseAttachmentListResponse'
- examples:
- byAttachmentsId:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- attachments_id: 附件业务ID | string
- attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
- attachments_filename: 原始文件名 | string
- attachments_filetype: 文件类型或MIME | string
- attachments_size: 文件大小 | number
- attachments_owner: 上传者业务标识 | string
- attachments_md5: 文件MD5 | string
- attachments_ocr: OCR识别结果 | string
- attachments_status: 附件状态 | string
- attachments_remark: 备注 | string
- byAttachmentsIds:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- attachments_id: ATT-1774599142438-8n1UcU
- attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
- attachments_filename: 原始文件名 | string
- attachments_filetype: 文件类型或MIME | string
- attachments_size: 文件大小 | number
- attachments_owner: 上传者业务标识 | string
- attachments_md5: 文件MD5 | string
- attachments_ocr: OCR识别结果 | string
- attachments_status: 附件状态 | string
- attachments_remark: 备注 | string
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- attachments_id: ATT-1774599143999-7pQkLm
- attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
- attachments_filename: 原始文件名 | string
- attachments_filetype: 文件类型或MIME | string
- attachments_size: 文件大小 | number
- attachments_owner: 上传者业务标识 | string
- attachments_md5: 文件MD5 | string
- attachments_ocr: OCR识别结果 | string
- attachments_status: 附件状态 | string
- attachments_remark: 备注 | string
- '400':
- description: 查询参数错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/attachments.yaml#/attachmentRecords'
/pb/api/collections/tbl_product_list/records:
- get:
- operationId: getPocketBaseProductListRecords
- tags:
- - 产品信息
- summary: 根据产品分类精确筛选并按分类排序值升序返回产品列表
- description: |
- 使用 PocketBase 原生 records list 接口查询 `tbl_product_list`。
-
- 当前接口约定:
- - 条件:按 `prod_list_category` 精确匹配筛选
- - 排序:按 `prod_list_sort` 从小到大排序
-
- 标准调用参数建议:
- - `filter=prod_list_category="<产品分类>"`
- - `sort=prod_list_sort`
-
- 注意:
- - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- - 若不传 `sort`,将由 PocketBase 默认排序策略决定返回顺序
- parameters:
- - name: filter
- in: query
- required: true
- description: |
- PocketBase 标准过滤表达式,当前要求按产品分类精确值筛选。
-
- 推荐写法:`prod_list_category="<产品分类>"`
- schema:
- type: string
- example: prod_list_category="<产品分类>"
- - name: page
- in: query
- required: false
- description: 页码
- schema:
- type: integer
- minimum: 1
- default: 1
- - name: perPage
- in: query
- required: false
- description: 每页条数
- schema:
- type: integer
- minimum: 1
- default: 20
- - name: sort
- in: query
- required: false
- description: |
- PocketBase 原生排序表达式。
-
- 当前要求使用:
- - `prod_list_sort`:按分类排序值从小到大
- schema:
- type: string
- example: prod_list_sort
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseProductListListResponse'
- examples:
- byCategoryAscSort:
- value:
- page: <页码>|
- perPage: <每页条数>|
- totalItems: <总记录数>|
- totalPages: <总页数>|
- items:
- - id: |
- collectionId: <集合ID>|
- collectionName: <集合名称>|
- created: <记录创建时间>|
- updated: <记录更新时间>|
- prod_list_id: <产品列表业务ID>|
- prod_list_name: <产品名称>|
- prod_list_modelnumber: <产品型号>|
- prod_list_icon: <产品图标附件ID>|
- prod_list_description: <产品说明>|
- prod_list_feature: <产品特色>|
- prod_list_parameters:
- - name: <属性名>|
- value: <属性值>|
- prod_list_plantype: <产品方案>|
- prod_list_category: <产品分类>|
- prod_list_sort: 10
- prod_list_comm_type: <通讯类型>|
- prod_list_series: <产品系列>|
- prod_list_power_supply: <供电方式>|
- prod_list_tags: <产品标签>|
- prod_list_status: <产品状态>|
- prod_list_basic_price: 1999
- prod_list_vip_price:
- - viplevel: VIP1
- price: 1899
- prod_list_remark: <备注>|
- '400':
- description: 查询参数错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/products.yaml#/productRecords'
/pb/api/collections/tbl_document/records:
- get:
- operationId: getPocketBaseDocumentRecords
- tags:
- - 文档信息
- summary: 分页查询文档列表 / 按 system_dict_id 与 enum 双条件分页筛选文档
- description: |
- 使用 PocketBase 原生 records list 接口查询 `tbl_document`。
-
- 当前线上权限规则:
- - `listRule = ""`,因此任何客户端都可直接分页查询
- - `viewRule = ""`,因此单条详情也可公开读取
- - `create/update/delete` 仍仅管理员或管理后台用户允许
-
- `document_type` 的存储格式为:
- - `system_dict_id@dict_word_enum|system_dict_id@dict_word_enum`
-
- 业务上这里是两个独立条件,并且查询时两个条件都要满足:
- - 条件 1:包含某个 `system_dict_id`
- - 条件 2:包含某个 `enum`
-
- PocketBase 原生标准接口实际只有一个 `filter` 参数,因此应在同一个 `filter` 中写成两个 `contains` 条件,例如:
- - `system_dict_id = DICT-1774599144591-hAEFQj`
- - `enum = UT1`
- - 最终:`document_type ~ "DICT-1774599144591-hAEFQj" && document_type ~ "@UT1"`
-
- 这条写法已经按线上真实数据验证通过。
-
- 排序说明:
- - 当前线上统一按 `document_create` 排序
- - 若要“最新上传的排在最前面”,请传 `sort=-document_create`
-
- 标准调用方式有两种:
- 1. 查询整个文档列表:
- - 不传 `filter`
- - 按需传 `page`、`perPage`
- - 若要按最新上传倒序,传 `sort=-document_create`
- 2. 根据 `system_dict_id` 和 `enum` 两个业务条件分页筛选:
- - 直接传 `filter=document_type ~ "" && document_type ~ "@"`
- - 传 `page`、`perPage`
- - 若要按最新上传倒序,传 `sort=-document_create`
-
- 注意:
- - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- - 如果需要更复杂的条件组合,可继续使用 PocketBase 原生 `filter` 语法自行扩展
- parameters:
- - name: filter
- in: query
- required: false
- description: |
- PocketBase 标准过滤表达式。
-
- - 查全部列表时:不传
- - 按业务条件筛选时,同时写两个 `contains` 条件
- - 第二个条件建议带上 `@` 前缀,避免误命中
- - 例如:`document_type ~ "DICT-1774599144591-hAEFQj" && document_type ~ "@UT1"`
- schema:
- type: string
- example: document_type ~ "DICT-1774599144591-hAEFQj" && document_type ~ "@UT1"
- - name: page
- in: query
- required: false
- description: 页码
- schema:
- type: integer
- minimum: 1
- default: 1
- - name: perPage
- in: query
- required: false
- description: 每页条数
- schema:
- type: integer
- minimum: 1
- default: 20
- - name: sort
- in: query
- required: false
- description: |
- PocketBase 原生排序表达式。
-
- 当前线上建议使用:
- - `-document_create`:按最新上传倒序返回
- schema:
- type: string
- example: -document_create
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseDocumentListResponse'
- examples:
- listAll:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- document_id: 文档业务ID | string
- document_create: 文档创建时间,由数据库自动生成 | string
- document_effect_date: 文档生效日期 | string
- document_expiry_date: 文档到期日期 | string
- document_title: 文档标题 | string
- document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
- document_subtitle: 文档副标题 | string
- document_summary: 文档摘要 | string
- document_content: 正文内容 | string
- document_image: 图片附件ID串,底层按|分隔 | string
- document_video: 视频附件ID串,底层按|分隔 | string
- document_file: 文件附件ID串,底层按|分隔 | string
- document_status: 文档状态 | string
- document_owner: 上传者openid | string
- document_relation_model: 关联机型标识 | string
- document_keywords: 关键词,多选按|分隔 | string
- document_share_count: 分享次数 | number
- document_download_count: 下载次数 | number
- document_favorite_count: 收藏次数 | number
- document_embedding_status: 文档嵌入状态 | string
- document_embedding_error: 文档嵌入错误原因 | string
- document_embedding_lasttime: 最后一次嵌入更新时间 | string
- document_vector_version: 向量版本号或模型名称 | string
- document_product_categories: 产品关联文档,多选按|分隔 | string
- document_application_scenarios: 筛选依据,多选按|分隔 | string
- document_hotel_type: 适用场景,多选按|分隔 | string
- document_remark: 备注 | string
- filterByTypeToken:
- value:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- document_id: 文档业务ID | string
- document_create: 文档创建时间,由数据库自动生成 | string
- document_effect_date: 文档生效日期 | string
- document_expiry_date: 文档到期日期 | string
- document_title: 文档标题 | string
- document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
- document_subtitle: 文档副标题 | string
- document_summary: 文档摘要 | string
- document_content: 正文内容 | string
- document_image: 图片附件ID串,底层按|分隔 | string
- document_video: 视频附件ID串,底层按|分隔 | string
- document_file: 文件附件ID串,底层按|分隔 | string
- document_status: 文档状态 | string
- document_owner: 上传者openid | string
- document_relation_model: 关联机型标识 | string
- document_keywords: 关键词,多选按|分隔 | string
- document_share_count: 分享次数 | number
- document_download_count: 下载次数 | number
- document_favorite_count: 收藏次数 | number
- document_embedding_status: 文档嵌入状态 | string
- document_embedding_error: 文档嵌入错误原因 | string
- document_embedding_lasttime: 最后一次嵌入更新时间 | string
- document_vector_version: 向量版本号或模型名称 | string
- document_product_categories: 产品关联文档,多选按|分隔 | string
- document_application_scenarios: 筛选依据,多选按|分隔 | string
- document_hotel_type: 适用场景,多选按|分隔 | string
- document_remark: 备注 | string
- - id: ofy47wp9mmm0aub
- collectionId: pbc_3636602973
- collectionName: tbl_document
- created: '2026-03-28 07:20:00.000Z'
- updated: '2026-03-28 07:20:00.000Z'
- document_id: DOC-1774680568340-TeUSQn
- document_create: '2026-03-28 08:22:48.000Z'
- document_effect_date: ''
- document_expiry_date: ''
- document_title: 易从碳达人节能系统,为酒店每天每间房省二元,以智能推动酒店ESG双碳落地!上海酒店用品展我们在E7A01等您!!
- document_type: DICT-1774599144591-hAEFQj@UT1
- document_subtitle: ''
- document_summary: ''
- document_content: ''
- document_image: ATT-1774680568287-zuhJWN
- document_video: ''
- document_file: ''
- document_status: 有效
- document_owner: su13106859882
- document_relation_model: ''
- document_keywords: ''
- document_share_count: 0
- document_download_count: 0
- document_favorite_count: 0
- document_embedding_status: ''
- document_embedding_error: ''
- document_embedding_lasttime: ''
- document_vector_version: ''
- document_product_categories: ''
- document_application_scenarios: ''
- document_hotel_type: ''
- document_remark: ''
- '400':
- description: 查询参数错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/documents.yaml#/documentRecords'
+ /pb/api/cart/list:
+ $ref: './openapi-wx/paths/cart-hooks.yaml#/cartList'
+ /pb/api/cart/detail:
+ $ref: './openapi-wx/paths/cart-hooks.yaml#/cartDetail'
+ /pb/api/cart/create:
+ $ref: './openapi-wx/paths/cart-hooks.yaml#/cartCreate'
+ /pb/api/cart/update:
+ $ref: './openapi-wx/paths/cart-hooks.yaml#/cartUpdate'
+ /pb/api/cart/delete:
+ $ref: './openapi-wx/paths/cart-hooks.yaml#/cartDelete'
+ /pb/api/order/list:
+ $ref: './openapi-wx/paths/order-hooks.yaml#/orderList'
+ /pb/api/order/detail:
+ $ref: './openapi-wx/paths/order-hooks.yaml#/orderDetail'
+ /pb/api/order/create:
+ $ref: './openapi-wx/paths/order-hooks.yaml#/orderCreate'
+ /pb/api/order/update:
+ $ref: './openapi-wx/paths/order-hooks.yaml#/orderUpdate'
+ /pb/api/order/delete:
+ $ref: './openapi-wx/paths/order-hooks.yaml#/orderDelete'
/pb/api/collections/tbl_cart/records:
- get:
- operationId: getPocketBaseCartRecords
- tags:
- - 购物车
- summary: 查询当前用户购物车列表或按业务 ID 精确查询
- description: |
- 使用 PocketBase 原生 records list 接口查询 `tbl_cart`。
-
- 当前线上权限规则:
- - `listRule = @request.auth.id != "" && cart_owner = @request.auth.openid`
- - 因此调用方只能读到 `cart_owner` 等于自己 `openid` 的记录
-
- 标准调用方式:
- 1. 查询当前登录用户全部购物车:
- - 不传 `filter`
- - 可选传 `sort=-cart_create`
- 2. 按业务 ID 精确查单条:
- - `filter=cart_id="CART-..."`
- - `perPage=1`
- - `page=1`
-
- 注意:
- - 这是 PocketBase 原生返回结构,不是 hooks 统一包装
- - 即使不传 `filter`,返回结果也会继续受 `listRule` 限制
- security:
- - BearerAuth: []
- parameters:
- - name: filter
- in: query
- required: false
- description: |
- PocketBase 标准过滤表达式。
-
- - 查当前用户全部购物车时:不传
- - 按 `cart_id` 精确查单条时:`cart_id="CART-1770000000000-abc123"`
- schema:
- type: string
- example: cart_id="CART-1770000000000-abc123"
- - name: page
- in: query
- required: false
- schema:
- type: integer
- minimum: 1
- default: 1
- - name: perPage
- in: query
- required: false
- schema:
- type: integer
- minimum: 1
- default: 20
- - name: sort
- in: query
- required: false
- description: PocketBase 原生排序表达式,推荐 `-cart_create`
- schema:
- type: string
- example: -cart_create
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCartListResponse'
- '400':
- description: 查询参数错误或不满足 listRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- post:
- operationId: postPocketBaseCartRecord
- tags:
- - 购物车
- summary: 创建购物车记录
- description: |
- 使用 PocketBase 原生 records create 接口向 `tbl_cart` 新增记录。
-
- 当前线上权限规则:
- - `createRule = @request.auth.id != "" && @request.body.cart_owner = @request.auth.openid`
- - 因此客户端创建时必须显式提交 `cart_owner`,并且值必须等于当前 token 对应的 `openid`
-
- 这意味着:
- - 不能依赖服务端自动补 owner
- - 不能省略 `cart_owner`
- - 不满足规则时 PocketBase 会直接返回 `400`
- security:
- - BearerAuth: []
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCartCreateRequest'
- responses:
- '200':
- description: 创建成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCartRecord'
- '400':
- description: 参数错误、违反字段约束或不满足 createRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/cart-native.yaml#/cartRecords'
/pb/api/collections/tbl_cart/records/{recordId}:
- patch:
- operationId: patchPocketBaseCartRecordByRecordId
- tags:
- - 购物车
- summary: 更新购物车记录
- description: |
- 使用 PocketBase 原生 records update 接口更新 `tbl_cart`。
-
- 标准调用流程:
- 1. 先通过 `GET /pb/api/collections/tbl_cart/records?filter=cart_id="..."&perPage=1&page=1` 找到原生 `recordId`
- 2. 再调用当前 `PATCH /pb/api/collections/tbl_cart/records/{recordId}`
-
- 当前线上权限规则:
- - `updateRule = @request.auth.id != "" && cart_owner = @request.auth.openid`
- - 调用方只能修改自己的购物车记录
- security:
- - BearerAuth: []
- parameters:
- - name: recordId
- in: path
- required: true
- schema:
- type: string
- example: l2r3nq7rqhuob0h
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCartUpdateRequest'
- responses:
- '200':
- description: 更新成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseCartRecord'
- '400':
- description: 参数错误或违反字段约束
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '404':
- description: 记录不存在或不满足 updateRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- delete:
- operationId: deletePocketBaseCartRecordByRecordId
- tags:
- - 购物车
- summary: 删除购物车记录
- description: |
- 使用 PocketBase 原生 records delete 接口删除 `tbl_cart`。
-
- 当前线上权限规则:
- - `deleteRule = @request.auth.id != "" && cart_owner = @request.auth.openid`
- - 调用方只能删除自己的购物车记录
- security:
- - BearerAuth: []
- parameters:
- - name: recordId
- in: path
- required: true
- schema:
- type: string
- example: l2r3nq7rqhuob0h
- responses:
- '204':
- description: 删除成功
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '404':
- description: 记录不存在或不满足 deleteRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/cart-native.yaml#/cartRecordById'
/pb/api/collections/tbl_order/records:
- get:
- operationId: getPocketBaseOrderRecords
- tags:
- - 订单
- summary: 查询当前用户订单列表或按业务 ID 精确查询
- description: |
- 使用 PocketBase 原生 records list 接口查询 `tbl_order`。
-
- 当前线上权限规则:
- - `listRule = @request.auth.id != "" && order_owner = @request.auth.openid`
- - 因此调用方只能读到 `order_owner` 等于自己 `openid` 的记录
-
- 标准调用方式:
- 1. 查询当前登录用户全部订单:
- - 不传 `filter`
- - 可选传 `sort=-order_create`
- 2. 按业务 ID 精确查单条:
- - `filter=order_id="ORDER-..."`
- - `perPage=1`
- - `page=1`
- security:
- - BearerAuth: []
- parameters:
- - name: filter
- in: query
- required: false
- schema:
- type: string
- example: order_id="ORDER-1770000000000-abc123"
- - name: page
- in: query
- required: false
- schema:
- type: integer
- minimum: 1
- default: 1
- - name: perPage
- in: query
- required: false
- schema:
- type: integer
- minimum: 1
- default: 20
- - name: sort
- in: query
- required: false
- description: PocketBase 原生排序表达式,推荐 `-order_create`
- schema:
- type: string
- example: -order_create
- responses:
- '200':
- description: 查询成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseOrderListResponse'
- '400':
- description: 查询参数错误或不满足 listRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- post:
- operationId: postPocketBaseOrderRecord
- tags:
- - 订单
- summary: 创建订单记录
- description: |
- 使用 PocketBase 原生 records create 接口向 `tbl_order` 新增记录。
-
- 当前线上权限规则:
- - `createRule = @request.auth.id != "" && @request.body.order_owner = @request.auth.openid`
- - 因此客户端创建时必须显式提交 `order_owner`,并且值必须等于当前 token 对应的 `openid`
-
- 这意味着:
- - 不能依赖服务端自动补 owner
- - 不能省略 `order_owner`
- - 不满足规则时 PocketBase 会直接返回 `400`
- security:
- - BearerAuth: []
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseOrderCreateRequest'
- responses:
- '200':
- description: 创建成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseOrderRecord'
- '400':
- description: 参数错误、违反字段约束或不满足 createRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '403':
- description: 集合规则被锁定或服务端权限设置异常
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/order-native.yaml#/orderRecords'
/pb/api/collections/tbl_order/records/{recordId}:
- patch:
- operationId: patchPocketBaseOrderRecordByRecordId
- tags:
- - 订单
- summary: 更新订单记录
- description: |
- 使用 PocketBase 原生 records update 接口更新 `tbl_order`。
-
- 标准调用流程:
- 1. 先通过 `GET /pb/api/collections/tbl_order/records?filter=order_id="..."&perPage=1&page=1` 找到原生 `recordId`
- 2. 再调用当前 `PATCH /pb/api/collections/tbl_order/records/{recordId}`
-
- 当前线上权限规则:
- - `updateRule = @request.auth.id != "" && order_owner = @request.auth.openid`
- - 调用方只能修改自己的订单记录
- security:
- - BearerAuth: []
- parameters:
- - name: recordId
- in: path
- required: true
- schema:
- type: string
- example: l2r3nq7rqhuob0h
- requestBody:
- required: true
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseOrderUpdateRequest'
- responses:
- '200':
- description: 更新成功
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseOrderRecord'
- '400':
- description: 参数错误或违反字段约束
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '404':
- description: 记录不存在或不满足 updateRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- delete:
- operationId: deletePocketBaseOrderRecordByRecordId
- tags:
- - 订单
- summary: 删除订单记录
- description: |
- 使用 PocketBase 原生 records delete 接口删除 `tbl_order`。
-
- 当前线上权限规则:
- - `deleteRule = @request.auth.id != "" && order_owner = @request.auth.openid`
- - 调用方只能删除自己的订单记录
- security:
- - BearerAuth: []
- parameters:
- - name: recordId
- in: path
- required: true
- schema:
- type: string
- example: l2r3nq7rqhuob0h
- responses:
- '204':
- description: 删除成功
- '401':
- description: token 缺失、无效或已过期
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '404':
- description: 记录不存在或不满足 deleteRule
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
- '500':
- description: 服务端错误
- content:
- application/json:
- schema:
- $ref: '#/components/schemas/PocketBaseNativeError'
+ $ref: './openapi-wx/paths/order-native.yaml#/orderRecordById'
components:
securitySchemes:
BearerAuth:
@@ -1504,1774 +95,114 @@ components:
bearerFormat: JWT
schemas:
ApiResponseBase:
- type: object
- required:
- - statusCode
- - errMsg
- - data
- properties:
- statusCode:
- type:
- - integer
- - string
- description: "业务状态码"
- example: 业务状态码 | integer
- errMsg:
- type: string
- description: "业务提示信息"
- example: 业务提示信息 | string
- data:
- description: "业务响应数据"
- type: object
- additionalProperties: true
- example:
- statusCode: 业务状态码 | integer
- errMsg: 业务提示信息 | string
- data:
- 任意业务字段: 业务响应数据 | object
+ $ref: './openapi-wx/schemas/common.yaml#/ApiResponseBase'
ErrorResponse:
- type: object
- required:
- - statusCode
- - errMsg
- - data
- properties:
- statusCode:
- type:
- - integer
- - string
- description: "业务状态码"
- example: 业务状态码 | integer
- errMsg:
- type: string
- description: "业务提示信息"
- example: 失败原因提示 | string
- data:
- description: "业务响应数据"
- type: object
- additionalProperties: true
- example:
- statusCode: 业务状态码 | integer
- errMsg: 失败原因提示 | string
- data:
- 任意错误字段: 错误附加信息 | object
- PocketBaseCartFields:
- type: object
- properties:
- cart_id:
- type: string
- description: 购物车业务 ID
- example: CART-1770000000000-abc123
- cart_number:
- type: string
- description: 购物车名称或分组号
- example: wx-user-20260403153000
- cart_create:
- type: string
- description: 购物车项创建时间,由数据库自动生成
- example: 2026-04-03 15:30:00.000Z
- cart_owner:
- type: string
- description: 购物车所有者 openid
- example: wx-openid-user-001
- cart_product_id:
- type: string
- description: 产品业务 ID
- example: PROD-1770000000000-abcd12
- cart_product_quantity:
- type:
- - integer
- - number
- description: 产品数量
- example: 2
- cart_status:
- type: string
- description: 购物车状态
- example: 有效
- cart_at_price:
- type:
- - number
- - integer
- description: 加入购物车时价格
- example: 1999
- cart_remark:
- type: string
- description: 备注
- example: 小程序加入购物车示例
- PocketBaseCartRecord:
- allOf:
- - $ref: '#/components/schemas/PocketBaseRecordBase'
- - $ref: '#/components/schemas/PocketBaseCartFields'
- PocketBaseCartCreateRequest:
- type: object
- required:
- - cart_id
- - cart_number
- - cart_owner
- - cart_product_id
- - cart_product_quantity
- - cart_status
- - cart_at_price
- properties:
- cart_id:
- type: string
- cart_number:
- type: string
- cart_owner:
- type: string
- description: 必须显式提交,且值必须等于当前 token 对应 openid
- cart_product_id:
- type: string
- cart_product_quantity:
- type:
- - integer
- - number
- cart_status:
- type: string
- cart_at_price:
- type:
- - number
- - integer
- cart_remark:
- type: string
- PocketBaseCartUpdateRequest:
- type: object
- properties:
- cart_number:
- type: string
- cart_owner:
- type: string
- description: 若提交,必须仍等于当前 token 对应 openid
- cart_product_id:
- type: string
- cart_product_quantity:
- type:
- - integer
- - number
- cart_status:
- type: string
- cart_at_price:
- type:
- - number
- - integer
- cart_remark:
- type: string
- PocketBaseCartListResponse:
- type: object
- required:
- - page
- - perPage
- - totalItems
- - totalPages
- - items
- properties:
- page:
- type:
- - integer
- - string
- perPage:
- type:
- - integer
- - string
- totalItems:
- type:
- - integer
- - string
- totalPages:
- type:
- - integer
- - string
- items:
- type: array
- items:
- $ref: '#/components/schemas/PocketBaseCartRecord'
- PocketBaseOrderFields:
- type: object
- properties:
- order_id:
- type: string
- description: 订单业务 ID
- example: ORDER-1770000000000-abc123
- order_number:
- type: string
- description: 订单编号
- example: wx-user-20260403153500
- order_create:
- type: string
- description: 订单创建时间,由数据库自动生成
- example: 2026-04-03 15:35:00.000Z
- order_owner:
- type: string
- description: 订单所有者 openid
- example: wx-openid-user-001
- order_source:
- type: string
- description: 订单来源
- example: 购物车
- order_status:
- type: string
- description: 订单状态
- example: 订单已生成
- order_source_id:
- type: string
- description: 来源关联业务 ID
- example: CART-1770000000000-abc123
- order_snap:
- description: 订单快照 JSON
- oneOf:
- - type: object
- additionalProperties: true
- - type: array
- items:
- type: object
- additionalProperties: true
- order_amount:
- type:
- - number
- - integer
- description: 订单金额
- example: 3998
- order_remark:
- type: string
- description: 备注
- example: 小程序订单示例
- PocketBaseOrderRecord:
- allOf:
- - $ref: '#/components/schemas/PocketBaseRecordBase'
- - $ref: '#/components/schemas/PocketBaseOrderFields'
- PocketBaseOrderCreateRequest:
- type: object
- required:
- - order_id
- - order_number
- - order_owner
- - order_source
- - order_status
- - order_source_id
- - order_snap
- - order_amount
- properties:
- order_id:
- type: string
- order_number:
- type: string
- order_owner:
- type: string
- description: 必须显式提交,且值必须等于当前 token 对应 openid
- order_source:
- type: string
- order_status:
- type: string
- order_source_id:
- type: string
- order_snap:
- oneOf:
- - type: object
- additionalProperties: true
- - type: array
- items:
- type: object
- additionalProperties: true
- order_amount:
- type:
- - number
- - integer
- order_remark:
- type: string
- PocketBaseOrderUpdateRequest:
- type: object
- properties:
- order_number:
- type: string
- order_owner:
- type: string
- description: 若提交,必须仍等于当前 token 对应 openid
- order_source:
- type: string
- order_status:
- type: string
- order_source_id:
- type: string
- order_snap:
- oneOf:
- - type: object
- additionalProperties: true
- - type: array
- items:
- type: object
- additionalProperties: true
- order_amount:
- type:
- - number
- - integer
- order_remark:
- type: string
- PocketBaseOrderListResponse:
- type: object
- required:
- - page
- - perPage
- - totalItems
- - totalPages
- - items
- properties:
- page:
- type:
- - integer
- - string
- perPage:
- type:
- - integer
- - string
- totalItems:
- type:
- - integer
- - string
- totalPages:
- type:
- - integer
- - string
- items:
- type: array
- items:
- $ref: '#/components/schemas/PocketBaseOrderRecord'
- CompanyInfo:
- anyOf:
- - type: object
- description: 用户所属公司信息;当用户尚未绑定公司时返回 `null`
- properties:
- pb_id:
- type: string
- description: PocketBase 记录主键 id
- example: PocketBase记录主键id | string
- company_id:
- type: string
- description: 公司业务 id,由数据库自动生成
- example: 公司业务id,由数据库自动生成 | string
- company_name:
- type: string
- description: 公司名称
- example: 公司名称 | string
- company_type:
- type: string
- description: 公司类型
- example: 公司类型 | string
- company_entity:
- type: string
- description: 公司法人
- example: 公司法人 | string
- company_usci:
- type: string
- description: 统一社会信用代码
- example: 统一社会信用代码 | string
- company_nationality:
- type: string
- description: 国家名称
- example: 国家名称 | string
- company_nationality_code:
- type: string
- description: 国家编码
- example: 国家编码 | string
- company_province:
- type: string
- description: 省份名称
- example: 省份名称 | string
- company_province_code:
- type: string
- description: 省份编码
- example: 省份编码 | string
- company_city:
- type: string
- description: 城市名称
- example: 城市名称 | string
- company_city_code:
- type: string
- description: 城市编码
- example: 城市编码 | string
- company_district:
- type: string
- description: 区/县名称
- example: 区县名称 | string
- company_district_code:
- type: string
- description: 区/县编码
- example: 区县编码 | string
- company_postalcode:
- type: string
- description: 邮政编码
- example: 邮政编码 | string
- company_add:
- type: string
- description: 公司地址
- example: 公司地址 | string
- company_status:
- type: string
- description: 公司状态
- example: 公司状态 | string
- company_level:
- type: string
- description: 公司等级
- example: 公司等级 | string
- company_owner_openid:
- type: string
- description: 公司所有者 openid
- example: 公司所有者openid | string
- company_remark:
- type: string
- description: 备注
- example: 备注 | string
- created:
- type: string
- description: 记录创建时间
- example: 记录创建时间 | string
- updated:
- type: string
- description: 记录更新时间
- example: 记录更新时间 | string
- example:
- pb_id: PocketBase记录主键id | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- - type: 'null'
- PocketBaseRecordBase:
- type: object
- required:
- - id
- - collectionId
- - collectionName
- - created
- - updated
- properties:
- id:
- type: string
- description: "PocketBase 记录主键"
- example: PocketBase记录主键 | string
- collectionId:
- type: string
- example: 集合ID | string
- collectionName:
- type: string
- example: 集合名称 | string
- created:
- type: string
- description: "记录创建时间"
- example: 记录创建时间 | string
- updated:
- type: string
- description: "记录更新时间"
- example: 记录更新时间 | string
- example:
- id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- PocketBaseCompanyFields:
- type: object
- properties:
- company_id:
- type: string
- description: 公司业务 id,由数据库自动生成
- example: 公司业务id,由数据库自动生成 | string
- company_name:
- type: string
- description: 公司名称
- example: 公司名称 | string
- company_type:
- type: string
- description: 公司类型
- example: 公司类型 | string
- company_entity:
- type: string
- description: 公司法人
- example: 公司法人 | string
- company_usci:
- type: string
- description: 统一社会信用代码
- example: 统一社会信用代码 | string
- company_nationality:
- type: string
- description: 国家名称
- example: 国家名称 | string
- company_nationality_code:
- type: string
- description: 国家编码
- example: 国家编码 | string
- company_province:
- type: string
- description: 省份名称
- example: 省份名称 | string
- company_province_code:
- type: string
- description: 省份编码
- example: 省份编码 | string
- company_city:
- type: string
- description: 城市名称
- example: 城市名称 | string
- company_city_code:
- type: string
- description: 城市编码
- example: 城市编码 | string
- company_district:
- type: string
- description: 区/县名称
- example: 区县名称 | string
- company_district_code:
- type: string
- description: 区/县编码
- example: 区县编码 | string
- company_postalcode:
- type: string
- description: 邮政编码
- example: 邮政编码 | string
- company_add:
- type: string
- description: 公司地址
- example: 公司地址 | string
- company_status:
- type: string
- description: 公司状态
- example: 公司状态 | string
- company_level:
- type: string
- description: 公司等级
- example: 公司等级 | string
- company_owner_openid:
- type: string
- description: 公司所有者 openid
- example: 公司所有者openid | string
- company_remark:
- type: string
- description: 备注
- example: 备注 | string
- PocketBaseCompanyRecord:
- allOf:
- - $ref: '#/components/schemas/PocketBaseRecordBase'
- - $ref: '#/components/schemas/PocketBaseCompanyFields'
- example:
- id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- PocketBaseCompanyCreateRequest:
- type: object
- properties:
- company_name:
- description: "公司名称"
- type: string
- company_type:
- description: "公司类型"
- type: string
- company_entity:
- description: "公司法人"
- type: string
- company_usci:
- description: "统一社会信用代码"
- type: string
- company_nationality:
- description: "国家名称"
- type: string
- company_nationality_code:
- description: "国家编码"
- type: string
- company_province:
- description: "省份名称"
- type: string
- company_province_code:
- description: "省份编码"
- type: string
- company_city:
- description: "城市名称"
- type: string
- company_city_code:
- description: "城市编码"
- type: string
- company_district:
- description: "区 / 县名称"
- type: string
- company_district_code:
- description: "区 / 县编码"
- type: string
- company_postalcode:
- description: "邮编"
- type: string
- company_add:
- description: "地址"
- type: string
- company_status:
- description: "公司状态"
- type: string
- company_level:
- description: "公司等级"
- type: string
- company_owner_openid:
- description: "公司所有者 openid"
- type: string
- company_remark:
- description: "备注"
- type: string
- additionalProperties: false
- PocketBaseCompanyUpdateRequest:
- type: object
- properties:
- company_id:
- description: "所属公司业务 ID"
- type: string
- company_name:
- description: "公司名称"
- type: string
- company_type:
- description: "公司类型"
- type: string
- company_entity:
- description: "公司法人"
- type: string
- company_usci:
- description: "统一社会信用代码"
- type: string
- company_nationality:
- description: "国家名称"
- type: string
- company_nationality_code:
- description: "国家编码"
- type: string
- company_province:
- description: "省份名称"
- type: string
- company_province_code:
- description: "省份编码"
- type: string
- company_city:
- description: "城市名称"
- type: string
- company_city_code:
- description: "城市编码"
- type: string
- company_district:
- description: "区 / 县名称"
- type: string
- company_district_code:
- description: "区 / 县编码"
- type: string
- company_postalcode:
- description: "邮编"
- type: string
- company_add:
- description: "地址"
- type: string
- company_status:
- description: "公司状态"
- type: string
- company_level:
- description: "公司等级"
- type: string
- company_owner_openid:
- description: "公司所有者 openid"
- type: string
- company_remark:
- description: "备注"
- type: string
- additionalProperties: false
- PocketBaseCompanyListResponse:
- type: object
- required:
- - page
- - perPage
- - totalItems
- - totalPages
- - items
- properties:
- page:
- type:
- - integer
- - string
- example: 页码 | integer
- perPage:
- type:
- - integer
- - string
- example: 每页条数 | integer
- totalItems:
- type:
- - integer
- - string
- example: 总记录数 | integer
- totalPages:
- type:
- - integer
- - string
- example: 总页数 | integer
- items:
- type: array
- items:
- $ref: '#/components/schemas/PocketBaseCompanyRecord'
- example:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- PocketBaseAttachmentRecord:
- type: object
- properties:
- id:
- type: string
- description: PocketBase 记录主键
- example: PocketBase记录主键 | string
- collectionId:
- type: string
- description: 集合ID
- example: 集合ID | string
- collectionName:
- type: string
- description: 集合名称
- example: 集合名称 | string
- attachments_id:
- type: string
- description: 附件业务 ID
- example: 附件业务ID | string
- attachments_link:
- type: string
- description: PocketBase 文件字段值,可按标准文件路径拼接文件流地址
- example: PocketBase文件字段值,可拼接文件流地址 | string
- attachments_filename:
- type: string
- description: 原始文件名
- example: 原始文件名 | string
- attachments_filetype:
- type: string
- description: 文件类型 / MIME
- example: 文件类型或MIME | string
- attachments_size:
- type:
- - number
- - integer
- - string
- description: 文件大小
- example: 文件大小 | number
- attachments_owner:
- type: string
- description: 上传者业务标识
- example: 上传者业务标识 | string
- attachments_md5:
- type: string
- description: 文件 MD5
- example: 文件MD5 | string
- attachments_ocr:
- type: string
- description: OCR 识别结果
- example: OCR识别结果 | string
- attachments_status:
- type: string
- description: 附件状态
- example: 附件状态 | string
- attachments_remark:
- type: string
- description: 备注
- example: 备注 | string
- example:
- id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- attachments_id: 附件业务ID | string
- attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
- attachments_filename: 原始文件名 | string
- attachments_filetype: 文件类型或MIME | string
- attachments_size: 文件大小 | number
- attachments_owner: 上传者业务标识 | string
- attachments_md5: 文件MD5 | string
- attachments_ocr: OCR识别结果 | string
- attachments_status: 附件状态 | string
- attachments_remark: 备注 | string
- PocketBaseAttachmentListResponse:
- type: object
- required:
- - page
- - perPage
- - totalItems
- - totalPages
- - items
- properties:
- page:
- type:
- - integer
- - string
- example: 页码 | integer
- perPage:
- type:
- - integer
- - string
- example: 每页条数 | integer
- totalItems:
- type:
- - integer
- - string
- example: 总记录数 | integer
- totalPages:
- type:
- - integer
- - string
- example: 总页数 | integer
- items:
- type: array
- items:
- $ref: '#/components/schemas/PocketBaseAttachmentRecord'
- example:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- attachments_id: 附件业务ID | string
- attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
- attachments_filename: 原始文件名 | string
- attachments_filetype: 文件类型或MIME | string
- attachments_size: 文件大小 | number
- attachments_owner: 上传者业务标识 | string
- attachments_md5: 文件MD5 | string
- attachments_ocr: OCR识别结果 | string
- attachments_status: 附件状态 | string
- attachments_remark: 备注 | string
- PocketBaseProductListFields:
- type: object
- properties:
- prod_list_id:
- type: string
- description: 产品列表业务 ID,唯一标识
- example: <产品列表业务ID>|
- prod_list_name:
- type: string
- description: 产品名称
- example: <产品名称>|
- prod_list_modelnumber:
- type: string
- description: 产品型号
- example: <产品型号>|
- prod_list_icon:
- type: string
- description: 产品图标附件 ID,保存 `tbl_attachments.attachments_id`
- example: <产品图标附件ID>|
- prod_list_description:
- type: string
- description: 产品说明
- example: <产品说明>|
- prod_list_feature:
- type: string
- description: 产品特色
- example: <产品特色>|
- prod_list_parameters:
- type: array
- description: 产品参数数组,每项包含 name/value
- items:
- type: object
- properties:
- name:
- type: string
- example: <属性名>|
- value:
- type: string
- example: <属性值>|
- example:
- - name: <属性名>|
- value: <属性值>|
- prod_list_plantype:
- type: string
- description: 产品方案
- example: <产品方案>|
- prod_list_category:
- type: string
- description: 产品分类(必填,单选)
- example: <产品分类>|
- prod_list_sort:
- type:
- - number
- - integer
- description: 排序值(同分类内按升序)
- example: 10
- prod_list_comm_type:
- type: string
- description: 通讯类型
- example: <通讯类型>|
- prod_list_series:
- type: string
- description: 产品系列
- example: <产品系列>|
- prod_list_power_supply:
- type: string
- description: 供电方式
- example: <供电方式>|
- prod_list_tags:
- type: string
- description: 产品标签(辅助检索,以 `|` 聚合)
- example: <产品标签>|
- prod_list_status:
- type: string
- description: 产品状态(有效 / 过期 / 主推等)
- example: <产品状态>|
- prod_list_basic_price:
- type:
- - number
- - integer
- description: 基础价格
- example: 1999
- prod_list_vip_price:
- type: array
- description: 会员价数组,每项包含会员等级枚举值与价格
- items:
- type: object
- properties:
- viplevel:
- type: string
- example: <会员等级枚举值>|
- price:
- type:
- - number
- - integer
- example: 1899
- example:
- - viplevel: VIP1
- price: 1899
- prod_list_remark:
- type: string
- description: 备注
- example: <备注>|
- PocketBaseProductListRecord:
- allOf:
- - $ref: '#/components/schemas/PocketBaseRecordBase'
- - $ref: '#/components/schemas/PocketBaseProductListFields'
- example:
- id: |
- collectionId: <集合ID>|
- collectionName: <集合名称>|
- created: <记录创建时间>|
- updated: <记录更新时间>|
- prod_list_id: <产品列表业务ID>|
- prod_list_name: <产品名称>|
- prod_list_modelnumber: <产品型号>|
- prod_list_icon: <产品图标附件ID>|
- prod_list_description: <产品说明>|
- prod_list_feature: <产品特色>|
- prod_list_parameters:
- - name: <属性名>|
- value: <属性值>|
- prod_list_plantype: <产品方案>|
- prod_list_category: <产品分类>|
- prod_list_sort: 10
- prod_list_comm_type: <通讯类型>|
- prod_list_series: <产品系列>|
- prod_list_power_supply: <供电方式>|
- prod_list_tags: <产品标签>|
- prod_list_status: <产品状态>|
- prod_list_basic_price: 1999
- prod_list_vip_price:
- - viplevel: VIP1
- price: 1899
- prod_list_remark: <备注>|
- PocketBaseProductListListResponse:
- type: object
- required:
- - page
- - perPage
- - totalItems
- - totalPages
- - items
- properties:
- page:
- type:
- - integer
- - string
- example: <页码>|
- perPage:
- type:
- - integer
- - string
- example: <每页条数>|
- totalItems:
- type:
- - integer
- - string
- example: <总记录数>|
- totalPages:
- type:
- - integer
- - string
- example: <总页数>|
- items:
- type: array
- items:
- $ref: '#/components/schemas/PocketBaseProductListRecord'
- example:
- page: <页码>|
- perPage: <每页条数>|
- totalItems: <总记录数>|
- totalPages: <总页数>|
- items:
- - id: |
- collectionId: <集合ID>|
- collectionName: <集合名称>|
- created: <记录创建时间>|
- updated: <记录更新时间>|
- prod_list_id: <产品列表业务ID>|
- prod_list_name: <产品名称>|
- prod_list_modelnumber: <产品型号>|
- prod_list_icon: <产品图标附件ID>|
- prod_list_description: <产品说明>|
- prod_list_feature: <产品特色>|
- prod_list_parameters:
- - name: <属性名>|
- value: <属性值>|
- prod_list_plantype: <产品方案>|
- prod_list_category: <产品分类>|
- prod_list_sort: 10
- prod_list_comm_type: <通讯类型>|
- prod_list_series: <产品系列>|
- prod_list_power_supply: <供电方式>|
- prod_list_tags: <产品标签>|
- prod_list_status: <产品状态>|
- prod_list_basic_price: 1999
- prod_list_vip_price:
- - viplevel: VIP1
- price: 1899
- prod_list_remark: <备注>|
- PocketBaseDocumentFields:
- type: object
- properties:
- document_id:
- type: string
- description: "文档业务 ID"
- example: 文档业务ID | string
- document_create:
- type: string
- description: "文档创建时间,由数据库自动生成"
- example: 文档创建时间,由数据库自动生成 | string
- document_effect_date:
- type: string
- description: "文档生效日期"
- example: 文档生效日期 | string
- document_expiry_date:
- type: string
- description: "文档到期日期"
- example: 文档到期日期 | string
- document_title:
- type: string
- description: "文档标题"
- example: 文档标题 | string
- document_type:
- type: string
- description: "文档类型,多选时按 system_dict_id@dict_word_enum|... 保存"
- example: 文档类型,按system_dict_id@dict_word_enum保存 | string
- document_subtitle:
- type: string
- description: "文档副标题"
- example: 文档副标题 | string
- document_summary:
- type: string
- description: "文档摘要"
- example: 文档摘要 | string
- document_content:
- type: string
- description: "正文内容,保存 Markdown"
- example: 正文内容 | string
- document_image:
- type: string
- description: "图片附件 ID 集合,底层以 | 分隔"
- example: 图片附件ID串,底层按|分隔 | string
- document_video:
- type: string
- description: "视频附件 ID 集合,底层以 | 分隔"
- example: 视频附件ID串,底层按|分隔 | string
- document_file:
- type: string
- description: "文件附件 ID 集合,底层以 | 分隔"
- example: 文件附件ID串,底层按|分隔 | string
- document_status:
- type: string
- description: "文档状态,仅 `有效` / `过期`"
- example: 文档状态 | string
- document_owner:
- type: string
- description: "上传者 openid"
- example: 上传者openid | string
- document_relation_model:
- type: string
- description: "关联机型 / 模型标识"
- example: 关联机型标识 | string
- document_keywords:
- type: string
- description: "关键词,多选后以 | 分隔"
- example: 关键词,多选按|分隔 | string
- document_share_count:
- type: number
- description: "分享次数"
- example: 分享次数 | number
- document_download_count:
- type: number
- description: "下载次数"
- example: 下载次数 | number
- document_favorite_count:
- type: number
- description: "收藏次数"
- example: 收藏次数 | number
- document_embedding_status:
- type: string
- description: "文档嵌入状态"
- example: 文档嵌入状态 | string
- document_embedding_error:
- type: string
- description: "文档嵌入错误原因"
- example: 文档嵌入错误原因 | string
- document_embedding_lasttime:
- type: string
- description: "最后一次嵌入更新时间"
- example: 最后一次嵌入更新时间 | string
- document_vector_version:
- type: string
- description: "向量版本号 / 模型名称"
- example: 向量版本号或模型名称 | string
- document_product_categories:
- type: string
- description: "产品关联文档,多选后以 | 分隔"
- example: 产品关联文档,多选按|分隔 | string
- document_application_scenarios:
- type: string
- description: "筛选依据,多选后以 | 分隔"
- example: 筛选依据,多选按|分隔 | string
- document_hotel_type:
- type: string
- description: "适用场景,多选后以 | 分隔"
- example: 适用场景,多选按|分隔 | string
- document_remark:
- type: string
- description: "备注"
- example: 备注 | string
- PocketBaseDocumentRecord:
- allOf:
- - $ref: '#/components/schemas/PocketBaseRecordBase'
- - $ref: '#/components/schemas/PocketBaseDocumentFields'
- example:
- id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- document_id: 文档业务ID | string
- document_create: 文档创建时间,由数据库自动生成 | string
- document_effect_date: 文档生效日期 | string
- document_expiry_date: 文档到期日期 | string
- document_title: 文档标题 | string
- document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
- document_subtitle: 文档副标题 | string
- document_summary: 文档摘要 | string
- document_content: 正文内容 | string
- document_image: 图片附件ID串,底层按|分隔 | string
- document_video: 视频附件ID串,底层按|分隔 | string
- document_file: 文件附件ID串,底层按|分隔 | string
- document_status: 文档状态 | string
- document_owner: 上传者openid | string
- document_relation_model: 关联机型标识 | string
- document_keywords: 关键词,多选按|分隔 | string
- document_share_count: 分享次数 | number
- document_download_count: 下载次数 | number
- document_favorite_count: 收藏次数 | number
- document_embedding_status: 文档嵌入状态 | string
- document_embedding_error: 文档嵌入错误原因 | string
- document_embedding_lasttime: 最后一次嵌入更新时间 | string
- document_vector_version: 向量版本号或模型名称 | string
- document_product_categories: 产品关联文档,多选按|分隔 | string
- document_application_scenarios: 筛选依据,多选按|分隔 | string
- document_hotel_type: 适用场景,多选按|分隔 | string
- document_remark: 备注 | string
- PocketBaseDocumentListResponse:
- type: object
- required:
- - page
- - perPage
- - totalItems
- - totalPages
- - items
- properties:
- page:
- type:
- - integer
- - string
- example: 页码 | integer
- perPage:
- type:
- - integer
- - string
- example: 每页条数 | integer
- totalItems:
- type:
- - integer
- - string
- example: 总记录数 | integer
- totalPages:
- type:
- - integer
- - string
- example: 总页数 | integer
- items:
- type: array
- items:
- $ref: '#/components/schemas/PocketBaseDocumentRecord'
- example:
- page: 页码 | integer
- perPage: 每页条数 | integer
- totalItems: 总记录数 | integer
- totalPages: 总页数 | integer
- items:
- - id: PocketBase记录主键 | string
- collectionId: 集合ID | string
- collectionName: 集合名称 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- document_id: 文档业务ID | string
- document_create: 文档创建时间,由数据库自动生成 | string
- document_effect_date: 文档生效日期 | string
- document_expiry_date: 文档到期日期 | string
- document_title: 文档标题 | string
- document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
- document_subtitle: 文档副标题 | string
- document_summary: 文档摘要 | string
- document_content: 正文内容 | string
- document_image: 图片附件ID串,底层按|分隔 | string
- document_video: 视频附件ID串,底层按|分隔 | string
- document_file: 文件附件ID串,底层按|分隔 | string
- document_status: 文档状态 | string
- document_owner: 上传者openid | string
- document_relation_model: 关联机型标识 | string
- document_keywords: 关键词,多选按|分隔 | string
- document_share_count: 分享次数 | number
- document_download_count: 下载次数 | number
- document_favorite_count: 收藏次数 | number
- document_embedding_status: 文档嵌入状态 | string
- document_embedding_error: 文档嵌入错误原因 | string
- document_embedding_lasttime: 最后一次嵌入更新时间 | string
- document_vector_version: 向量版本号或模型名称 | string
- document_product_categories: 产品关联文档,多选按|分隔 | string
- document_application_scenarios: 筛选依据,多选按|分隔 | string
- document_hotel_type: 适用场景,多选按|分隔 | string
- document_remark: 备注 | string
+ $ref: './openapi-wx/schemas/common.yaml#/ErrorResponse'
+ HookRecordBase:
+ $ref: './openapi-wx/schemas/common.yaml#/HookRecordBase'
PocketBaseNativeError:
- type: object
- properties:
- code:
- type:
- - integer
- - string
- description: "业务状态码"
- example: 错误状态码 | integer
- message:
- type: string
- example: PocketBase原生错误信息 | string
- data:
- description: "业务响应数据"
- type: object
- additionalProperties: true
- example:
- code: 错误状态码 | integer
- message: PocketBase原生错误信息 | string
- data:
- 任意错误字段: 原生错误附加信息 | object
- UserInfo:
- type: object
- properties:
- pb_id:
- description: "PocketBase 记录主键 id"
- type: string
- users_convers_id:
- description: "会话侧用户 ID"
- type: string
- users_id:
- description: "用户业务 ID"
- type: string
- users_idtype:
- description: "身份来源类型或证件类型"
- anyOf:
- - type: string
- enum:
- - WeChat
- - ManagePlatform
- - type: string
- users_id_number:
- description: "证件号"
- type: string
- users_type:
- description: "用户类型"
- anyOf:
- - type: string
- enum:
- - 游客
- - 注册用户
- - type: string
- users_name:
- description: "用户姓名 / 昵称"
- type: string
- users_status:
- description: "用户状态"
- type:
- - string
- - number
- users_rank_level:
- description: "用户星级数值"
- type:
- - number
- - integer
- - string
- users_auth_type:
- description: "账户类型"
- type:
- - number
- - integer
- - string
- users_phone:
- description: "手机号"
- type: string
- users_phone_masked:
- type: string
- users_level:
- type: string
- description: 用户等级
- users_level_name:
- type: string
- description: 用户等级名称,按 `users_level -> 数据-会员等级` 字典描述实时解析
- users_tag:
- type: string
- description: 用户标签
- users_picture:
- type: string
- description: 用户头像附件的 `attachments_id`
- users_picture_url:
- type: string
- description: 根据 `users_picture -> tbl_attachments` 自动解析出的头像文件流链接
- users_id_pic_a:
- type: string
- description: 证件正面附件的 `attachments_id`
- users_id_pic_a_url:
- type: string
- description: 根据 `users_id_pic_a -> tbl_attachments` 自动解析出的文件流链接
- users_id_pic_b:
- type: string
- description: 证件反面附件的 `attachments_id`
- users_id_pic_b_url:
- type: string
- description: 根据 `users_id_pic_b -> tbl_attachments` 自动解析出的文件流链接
- users_title_picture:
- type: string
- description: 资质附件的 `attachments_id`
- users_title_picture_url:
- type: string
- description: 根据 `users_title_picture -> tbl_attachments` 自动解析出的文件流链接
- openid:
- type: string
- description: 全平台统一身份标识
- company_id:
- type: string
- description: 公司业务 id,存储 `tbl_company.company_id`
- users_parent_id:
- type: string
- description: 上级用户业务 id
- users_promo_code:
- type: string
- description: 推广码
- usergroups_id:
- type: string
- description: 用户组业务 id
- company:
- $ref: '#/components/schemas/CompanyInfo'
- created:
- type: string
- description: 用户创建时间
- updated:
- type: string
- description: 用户更新时间
- example:
- pb_id: PocketBase记录主键id | string
- users_convers_id: 会话侧用户ID | string
- users_id: 用户业务ID | string
- users_idtype: 用户身份来源类型 | string
- users_id_number: 证件号 | string
- users_type: 用户类型 | string
- users_name: 用户姓名或昵称 | string
- users_status: 用户状态 | string
- users_rank_level: 用户星级数值 | number
- users_auth_type: 账户类型 | number
- users_phone: 手机号 | string
- users_phone_masked: 手机号脱敏值 | string
- users_level: 用户等级 | string
- users_level_name: 用户等级名称 | string
- users_tag: 用户标签 | string
- users_picture: 用户头像附件ID | string
- users_picture_url: 用户头像文件流链接 | string
- users_id_pic_a: 证件正面附件ID | string
- users_id_pic_a_url: 证件正面文件流链接 | string
- users_id_pic_b: 证件反面附件ID | string
- users_id_pic_b_url: 证件反面文件流链接 | string
- users_title_picture: 资质附件ID | string
- users_title_picture_url: 资质附件文件流链接 | string
- openid: 全平台统一身份标识 | string
- company_id: 公司业务id | string
- users_parent_id: 上级用户业务id | string
- users_promo_code: 推广码 | string
- usergroups_id: 用户组业务id | string
- company:
- pb_id: PocketBase记录主键id | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- created: 用户创建时间 | string
- updated: 用户更新时间 | string
- WechatLoginRequest:
- type: object
- required:
- - users_wx_code
- properties:
- users_wx_code:
- type: string
- description: 微信小程序登录临时凭证 code
- example: 0a1b2c3d4e5f6g
- WechatProfileRequest:
- type: object
- properties:
- users_name:
- type: string
- description: "用户姓名 / 昵称"
- example: 张三
- users_phone_code:
- type: string
- description: 可选。若传入,服务端优先通过微信接口换取真实手机号并写入数据库
- example: 2b7d9f2e3c4a5b6d7e8f
- users_phone:
- type: string
- description: 可选。未传 `users_phone_code` 时,可直接写入手机号
- example: '13800138000'
- users_type:
- type: string
- description: 可选。用户类型;仅在传入非空值时更新
- example: 服务商
- company_id:
- type: string
- description: 可选。公司业务 id;仅在传入非空值时更新
- example: WX-COMPANY-10001
- users_tag:
- type: string
- description: 可选。用户标签;非空时才更新
- example: 核心客户
- users_picture:
- type: string
- description: 可选。用户头像附件的 `attachments_id`
- example: ATT-1743123456789-abc123
- users_id_pic_a:
- type: string
- description: 可选。证件正面附件的 `attachments_id`
- users_id_pic_b:
- type: string
- description: 可选。证件反面附件的 `attachments_id`
- users_title_picture:
- type: string
- description: 可选。资质附件的 `attachments_id`
+ $ref: './openapi-wx/schemas/common.yaml#/PocketBaseNativeError'
+ PocketBaseRecordBase:
+ $ref: './openapi-wx/schemas/common.yaml#/PocketBaseRecordBase'
SystemRefreshTokenRequest:
- type: object
- properties:
- users_wx_code:
- type:
- - string
- - 'null'
- description: |
- 可选。
- 当前 token 失效时,可通过该 code 重新签发 token。
- example: 0a1b2c3d4e5f6g
- AuthSuccessData:
- type: object
- properties:
- status:
- anyOf:
- - type: string
- enum:
- - register_success
- - login_success
- - type: string
- is_info_complete:
- type:
- - boolean
- - string
- user:
- $ref: '#/components/schemas/UserInfo'
- AuthSuccessResponse:
- allOf:
- - $ref: '#/components/schemas/ApiResponseBase'
- - type: object
- required:
- - token
- properties:
- data:
- description: "业务响应数据"
- $ref: '#/components/schemas/AuthSuccessData'
- token:
- type: string
- description: PocketBase 原生 auth token
- example:
- statusCode: 业务状态码 | integer
- errMsg: 业务提示信息 | string
- data:
- status: 登录或注册状态 | string
- is_info_complete: 资料是否完整 | boolean
- user:
- pb_id: PocketBase记录主键id | string
- users_convers_id: 会话侧用户ID | string
- users_id: 用户业务ID | string
- users_idtype: 用户身份来源类型 | string
- users_id_number: 证件号 | string
- users_type: 用户类型 | string
- users_name: 用户姓名或昵称 | string
- users_status: 用户状态 | string
- users_rank_level: 用户星级数值 | number
- users_auth_type: 账户类型 | number
- users_phone: 手机号 | string
- users_phone_masked: 手机号脱敏值 | string
- users_level: 用户等级 | string
- users_level_name: 用户等级名称 | string
- users_tag: 用户标签 | string
- users_picture: 用户头像附件ID | string
- users_picture_url: 用户头像文件流链接 | string
- users_id_pic_a: 证件正面附件ID | string
- users_id_pic_a_url: 证件正面文件流链接 | string
- users_id_pic_b: 证件反面附件ID | string
- users_id_pic_b_url: 证件反面文件流链接 | string
- users_title_picture: 资质附件ID | string
- users_title_picture_url: 资质附件文件流链接 | string
- openid: 全平台统一身份标识 | string
- company_id: 公司业务id | string
- users_parent_id: 上级用户业务id | string
- users_promo_code: 推广码 | string
- usergroups_id: 用户组业务id | string
- company:
- pb_id: PocketBase记录主键id | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- created: 用户创建时间 | string
- updated: 用户更新时间 | string
- token: PocketBase原生认证token | string
- WechatProfileResponseData:
- type: object
- properties:
- status:
- anyOf:
- - type: string
- enum:
- - update_success
- - type: string
- user:
- $ref: '#/components/schemas/UserInfo'
- WechatProfileResponse:
- allOf:
- - $ref: '#/components/schemas/ApiResponseBase'
- - type: object
- properties:
- data:
- description: "业务响应数据"
- $ref: '#/components/schemas/WechatProfileResponseData'
- example:
- statusCode: 业务状态码 | integer
- errMsg: 业务提示信息 | string
- data:
- status: 资料更新状态 | string
- user:
- pb_id: PocketBase记录主键id | string
- users_convers_id: 会话侧用户ID | string
- users_id: 用户业务ID | string
- users_idtype: 用户身份来源类型 | string
- users_id_number: 证件号 | string
- users_type: 用户类型 | string
- users_name: 用户姓名或昵称 | string
- users_status: 用户状态 | string
- users_rank_level: 用户星级数值 | number
- users_auth_type: 账户类型 | number
- users_phone: 手机号 | string
- users_phone_masked: 手机号脱敏值 | string
- users_level: 用户等级 | string
- users_level_name: 用户等级名称 | string
- users_tag: 用户标签 | string
- users_picture: 用户头像附件ID | string
- users_picture_url: 用户头像文件流链接 | string
- users_id_pic_a: 证件正面附件ID | string
- users_id_pic_a_url: 证件正面文件流链接 | string
- users_id_pic_b: 证件反面附件ID | string
- users_id_pic_b_url: 证件反面文件流链接 | string
- users_title_picture: 资质附件ID | string
- users_title_picture_url: 资质附件文件流链接 | string
- openid: 全平台统一身份标识 | string
- company_id: 公司业务id | string
- users_parent_id: 上级用户业务id | string
- users_promo_code: 推广码 | string
- usergroups_id: 用户组业务id | string
- company:
- pb_id: PocketBase记录主键id | string
- company_id: 公司业务id,由数据库自动生成 | string
- company_name: 公司名称 | string
- company_type: 公司类型 | string
- company_entity: 公司法人 | string
- company_usci: 统一社会信用代码 | string
- company_nationality: 国家名称 | string
- company_nationality_code: 国家编码 | string
- company_province: 省份名称 | string
- company_province_code: 省份编码 | string
- company_city: 城市名称 | string
- company_city_code: 城市编码 | string
- company_district: 区县名称 | string
- company_district_code: 区县编码 | string
- company_postalcode: 邮政编码 | string
- company_add: 公司地址 | string
- company_status: 公司状态 | string
- company_level: 公司等级 | string
- company_owner_openid: 公司所有者openid | string
- company_remark: 备注 | string
- created: 记录创建时间 | string
- updated: 记录更新时间 | string
- created: 用户创建时间 | string
- updated: 用户更新时间 | string
+ $ref: './openapi-wx/schemas/system.yaml#/SystemRefreshTokenRequest'
RefreshTokenResponse:
- allOf:
- - $ref: '#/components/schemas/ApiResponseBase'
- - type: object
- required:
- - token
- properties:
- data:
- type: object
- additionalProperties: true
- description: "业务响应数据"
- example: {}
- token:
- type: string
- description: 新签发的 PocketBase 原生 auth token
- example:
- statusCode: 业务状态码 | integer
- errMsg: 刷新成功 | string
- data: {}
- token: 新签发的PocketBase原生auth token | string
+ $ref: './openapi-wx/schemas/system.yaml#/RefreshTokenResponse'
UsersCountData:
- type: object
- properties:
- total_users:
- type:
- - integer
- - string
- example: 用户总数 | integer
+ $ref: './openapi-wx/schemas/system.yaml#/UsersCountData'
UsersCountResponse:
- allOf:
- - $ref: '#/components/schemas/ApiResponseBase'
- - type: object
- properties:
- data:
- description: "业务响应数据"
- $ref: '#/components/schemas/UsersCountData'
- example:
- statusCode: 业务状态码 | integer
- errMsg: 业务提示信息 | string
- data:
- total_users: 用户总数 | integer
+ $ref: './openapi-wx/schemas/system.yaml#/UsersCountResponse'
+ UserInfo:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/UserInfo'
+ WechatLoginRequest:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/WechatLoginRequest'
+ WechatProfileRequest:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/WechatProfileRequest'
+ AuthSuccessData:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/AuthSuccessData'
+ AuthSuccessResponse:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/AuthSuccessResponse'
+ WechatProfileResponseData:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/WechatProfileResponseData'
+ WechatProfileResponse:
+ $ref: './openapi-wx/schemas/wechat-auth.yaml#/WechatProfileResponse'
+ CompanyInfo:
+ $ref: './openapi-wx/schemas/company.yaml#/CompanyInfo'
+ PocketBaseCompanyFields:
+ $ref: './openapi-wx/schemas/company.yaml#/PocketBaseCompanyFields'
+ PocketBaseCompanyRecord:
+ $ref: './openapi-wx/schemas/company.yaml#/PocketBaseCompanyRecord'
+ PocketBaseCompanyCreateRequest:
+ $ref: './openapi-wx/schemas/company.yaml#/PocketBaseCompanyCreateRequest'
+ PocketBaseCompanyUpdateRequest:
+ $ref: './openapi-wx/schemas/company.yaml#/PocketBaseCompanyUpdateRequest'
+ PocketBaseCompanyListResponse:
+ $ref: './openapi-wx/schemas/company.yaml#/PocketBaseCompanyListResponse'
+ PocketBaseAttachmentRecord:
+ $ref: './openapi-wx/schemas/attachments.yaml#/PocketBaseAttachmentRecord'
+ PocketBaseAttachmentListResponse:
+ $ref: './openapi-wx/schemas/attachments.yaml#/PocketBaseAttachmentListResponse'
+ PocketBaseProductListFields:
+ $ref: './openapi-wx/schemas/products.yaml#/PocketBaseProductListFields'
+ PocketBaseProductListRecord:
+ $ref: './openapi-wx/schemas/products.yaml#/PocketBaseProductListRecord'
+ PocketBaseProductListListResponse:
+ $ref: './openapi-wx/schemas/products.yaml#/PocketBaseProductListListResponse'
+ PocketBaseDocumentFields:
+ $ref: './openapi-wx/schemas/documents.yaml#/PocketBaseDocumentFields'
+ PocketBaseDocumentRecord:
+ $ref: './openapi-wx/schemas/documents.yaml#/PocketBaseDocumentRecord'
+ PocketBaseDocumentListResponse:
+ $ref: './openapi-wx/schemas/documents.yaml#/PocketBaseDocumentListResponse'
+ CartRecord:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartRecord'
+ CartListRequest:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartListRequest'
+ CartDetailRequest:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartDetailRequest'
+ CartCreateRequest:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartCreateRequest'
+ CartUpdateRequest:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartUpdateRequest'
+ CartDeleteRequest:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartDeleteRequest'
+ CartListResponse:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartListResponse'
+ CartDeleteResponse:
+ $ref: './openapi-wx/schemas/cart-hooks.yaml#/CartDeleteResponse'
+ OrderRecord:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderRecord'
+ OrderListRequest:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderListRequest'
+ OrderDetailRequest:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderDetailRequest'
+ OrderCreateRequest:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderCreateRequest'
+ OrderUpdateRequest:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderUpdateRequest'
+ OrderDeleteRequest:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderDeleteRequest'
+ OrderListResponse:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderListResponse'
+ OrderDeleteResponse:
+ $ref: './openapi-wx/schemas/order-hooks.yaml#/OrderDeleteResponse'
+ PocketBaseCartFields:
+ $ref: './openapi-wx/schemas/cart-native.yaml#/PocketBaseCartFields'
+ PocketBaseCartRecord:
+ $ref: './openapi-wx/schemas/cart-native.yaml#/PocketBaseCartRecord'
+ PocketBaseCartCreateRequest:
+ $ref: './openapi-wx/schemas/cart-native.yaml#/PocketBaseCartCreateRequest'
+ PocketBaseCartUpdateRequest:
+ $ref: './openapi-wx/schemas/cart-native.yaml#/PocketBaseCartUpdateRequest'
+ PocketBaseCartListResponse:
+ $ref: './openapi-wx/schemas/cart-native.yaml#/PocketBaseCartListResponse'
+ PocketBaseOrderFields:
+ $ref: './openapi-wx/schemas/order-native.yaml#/PocketBaseOrderFields'
+ PocketBaseOrderRecord:
+ $ref: './openapi-wx/schemas/order-native.yaml#/PocketBaseOrderRecord'
+ PocketBaseOrderCreateRequest:
+ $ref: './openapi-wx/schemas/order-native.yaml#/PocketBaseOrderCreateRequest'
+ PocketBaseOrderUpdateRequest:
+ $ref: './openapi-wx/schemas/order-native.yaml#/PocketBaseOrderUpdateRequest'
+ PocketBaseOrderListResponse:
+ $ref: './openapi-wx/schemas/order-native.yaml#/PocketBaseOrderListResponse'
diff --git a/pocket-base/spec/openapi-wx/paths/attachments.yaml b/pocket-base/spec/openapi-wx/paths/attachments.yaml
new file mode 100644
index 0000000..1fc4776
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/attachments.yaml
@@ -0,0 +1,135 @@
+attachmentRecords:
+ get:
+ operationId: getPocketBaseAttachmentRecords
+ tags:
+ - 附件信息
+ summary: 根据 attachments_id 查询单条或多条附件信息
+ description: |
+ 使用 PocketBase 原生 records list 接口查询 `tbl_attachments`。
+
+ 当前线上权限规则:
+ - `listRule = is_delete = 0`,因此任何客户端都可直接读取未软删除附件
+ - 原生 `create/update/delete` 仍仅管理员或管理后台用户允许
+
+ 标准调用方式有两种:
+ 1. 按 `attachments_id` 查询单条:
+ - `filter=attachments_id="ATT-1774599142438-8n1UcU"`
+ - `perPage=1`
+ - `page=1`
+ 2. 按多个 `attachments_id` 批量查询:
+ - 使用 `||` 组合多个等值条件
+ - 例如:`filter=attachments_id="ATT-1774599142438-8n1UcU" || attachments_id="ATT-1774599143999-7pQkLm"`
+ - 传 `perPage` 为预期返回条数,`page=1`
+
+ 注意:
+ - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
+ - `attachments_link` 返回的是 PocketBase 文件字段值,不是完整下载地址
+ - 若需文件流地址,可按 PocketBase 标准文件路径自行拼接:`/pb/api/files/{collectionId}/{recordId}/{attachments_link}`
+ parameters:
+ - name: filter
+ in: query
+ required: false
+ description: |
+ PocketBase 标准过滤表达式。
+
+ - 按 `attachments_id` 精确查询单条:`attachments_id="ATT-1774599142438-8n1UcU"`
+ - 按多个 `attachments_id` 批量查询:`attachments_id="ATT-1774599142438-8n1UcU" || attachments_id="ATT-1774599143999-7pQkLm"`
+ - 不传该参数时,返回分页列表
+ schema:
+ type: string
+ example: attachments_id="ATT-1774599142438-8n1UcU"
+ - name: page
+ in: query
+ required: false
+ description: 页码
+ schema:
+ type: integer
+ minimum: 1
+ default: 1
+ - name: perPage
+ in: query
+ required: false
+ description: 每页条数;单查建议为 `1`,批量查询建议设置为预期条数
+ schema:
+ type: integer
+ minimum: 1
+ default: 20
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseAttachmentListResponse'
+ examples:
+ byAttachmentsId:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ attachments_id: 附件业务ID | string
+ attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
+ attachments_filename: 原始文件名 | string
+ attachments_filetype: 文件类型或MIME | string
+ attachments_size: 文件大小 | number
+ attachments_owner: 上传者业务标识 | string
+ attachments_md5: 文件MD5 | string
+ attachments_ocr: OCR识别结果 | string
+ attachments_status: 附件状态 | string
+ attachments_remark: 备注 | string
+ byAttachmentsIds:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ attachments_id: ATT-1774599142438-8n1UcU
+ attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
+ attachments_filename: 原始文件名 | string
+ attachments_filetype: 文件类型或MIME | string
+ attachments_size: 文件大小 | number
+ attachments_owner: 上传者业务标识 | string
+ attachments_md5: 文件MD5 | string
+ attachments_ocr: OCR识别结果 | string
+ attachments_status: 附件状态 | string
+ attachments_remark: 备注 | string
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ attachments_id: ATT-1774599143999-7pQkLm
+ attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
+ attachments_filename: 原始文件名 | string
+ attachments_filetype: 文件类型或MIME | string
+ attachments_size: 文件大小 | number
+ attachments_owner: 上传者业务标识 | string
+ attachments_md5: 文件MD5 | string
+ attachments_ocr: OCR识别结果 | string
+ attachments_status: 附件状态 | string
+ attachments_remark: 备注 | string
+ '400':
+ description: 查询参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
diff --git a/pocket-base/spec/openapi-wx/paths/cart-hooks.yaml b/pocket-base/spec/openapi-wx/paths/cart-hooks.yaml
new file mode 100644
index 0000000..715804e
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/cart-hooks.yaml
@@ -0,0 +1,335 @@
+cartList:
+ post:
+ operationId: postCartList
+ tags:
+ - 购物车
+ summary: 按索引字段模糊查询购物车列表
+ description: |
+ 调用自定义 hooks API 查询当前登录用户的购物车列表。
+
+ 查询规则:
+ - 仅允许查询当前 `Authorization` 对应用户自己的购物车记录
+ - 支持通过 `keyword` 对多个索引相关字段做统一模糊搜索,当前实现覆盖:
+ - `cart_id`
+ - `cart_number`
+ - `cart_product_id`
+ - `product_name`
+ - 支持按 `cart_status` 精确过滤
+ - 支持按 `cart_number` 精确过滤
+
+ 目标软删除契约:
+ - 目标行为应仅返回 `is_delete = 0` 的记录
+ - 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: false
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartListRequest'
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartListResponse'
+ '400':
+ description: 参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+cartDetail:
+ post:
+ operationId: postCartDetail
+ tags:
+ - 购物车
+ summary: 按 cart_id 精确查询购物车详情
+ description: |
+ 调用自定义 hooks API 按 `cart_id` 查询单条购物车记录。
+
+ 查询规则:
+ - 仅允许访问当前 `Authorization` 对应用户自己的购物车记录
+ - 查询键为业务 ID `cart_id`,不是 PocketBase 原生 `recordId`
+
+ 目标软删除契约:
+ - 目标行为应仅允许查询 `is_delete = 0` 的记录
+ - 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartDetailRequest'
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartRecord'
+ '400':
+ description: 参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '403':
+ description: 无权访问该购物车记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 未找到对应购物车记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+cartCreate:
+ post:
+ operationId: postCartCreate
+ tags:
+ - 购物车
+ summary: 新增购物车记录
+ description: |
+ 调用自定义 hooks API 新增一条购物车记录。
+
+ 创建规则:
+ - 服务端自动根据当前 token 写入 `cart_owner`
+ - 服务端自动生成 `cart_id`
+ - 若未传 `cart_number`,服务端会自动生成展示编号
+ - `cart_product_id`、`cart_product_quantity`、`cart_at_price` 为必填
+
+ 目标软删除契约:
+ - 新建记录应默认为 `is_delete = 0`
+ - 当前仓库导出响应中尚未显式返回 `is_delete` 字段
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartCreateRequest'
+ responses:
+ '200':
+ description: 创建成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartRecord'
+ '400':
+ description: 参数错误、产品不存在或创建失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 重复请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+cartUpdate:
+ post:
+ operationId: postCartUpdate
+ tags:
+ - 购物车
+ summary: 修改购物车记录
+ description: |
+ 调用自定义 hooks API 按 `cart_id` 更新购物车记录。
+
+ 更新规则:
+ - 仅允许修改当前 `Authorization` 对应用户自己的购物车记录
+ - `cart_id` 为必填,用于精确定位目标记录
+ - 其余字段均为可选增量更新
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartUpdateRequest'
+ responses:
+ '200':
+ description: 更新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartRecord'
+ '400':
+ description: 参数错误、产品不存在或更新失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '403':
+ description: 无权访问该购物车记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 未找到待修改的购物车记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 重复请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+cartDelete:
+ post:
+ operationId: postCartDelete
+ tags:
+ - 购物车
+ summary: 软删除购物车记录
+ description: |
+ 目标契约:按 `cart_id` 软删除购物车记录,将 `is_delete` 标记为 `1`,而不是物理删除。
+
+ 当前仓库实现差异:
+ - 当前 `cartOrderService.deleteCart()` 仍直接调用 `$app.delete(record)` 做物理删除
+ - 因此当前后端实现与本软删除契约不一致
+ - 若要严格按本文档执行,需先同步调整后端服务实现,并在列表/详情接口中补充 `is_delete = 0` 过滤
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartDeleteRequest'
+ responses:
+ '200':
+ description: 删除成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartDeleteResponse'
+ '400':
+ description: 参数错误或删除失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '403':
+ description: 无权访问该购物车记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 未找到待删除的购物车记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 重复请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
diff --git a/pocket-base/spec/openapi-wx/paths/cart-native.yaml b/pocket-base/spec/openapi-wx/paths/cart-native.yaml
new file mode 100644
index 0000000..2b4f1a2
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/cart-native.yaml
@@ -0,0 +1,250 @@
+cartRecords:
+ get:
+ operationId: getPocketBaseCartRecords
+ tags:
+ - 购物车
+ summary: 查询当前用户购物车列表或按业务 ID 精确查询
+ description: |
+ 使用 PocketBase 原生 records list 接口查询 `tbl_cart`。
+
+ 当前线上权限规则:
+ - `listRule = @request.auth.id != "" && cart_owner = @request.auth.openid && is_delete = 0`
+ - 因此调用方只能读到 `cart_owner` 等于自己 `openid` 且未软删除的记录
+
+ 标准调用方式:
+ 1. 查询当前登录用户全部购物车:
+ - 不传 `filter`
+ - 可选传 `sort=-cart_create`
+ 2. 按业务 ID 精确查单条:
+ - `filter=cart_id="CART-..."`
+ - `perPage=1`
+ - `page=1`
+
+ 注意:
+ - 这是 PocketBase 原生返回结构,不是 hooks 统一包装
+ - 即使不传 `filter`,返回结果也会继续受 `listRule` 限制
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: filter
+ in: query
+ required: false
+ description: |
+ PocketBase 标准过滤表达式。
+
+ - 查当前用户全部购物车时:不传
+ - 按 `cart_id` 精确查单条时:`cart_id="CART-1770000000000-abc123"`
+ schema:
+ type: string
+ example: cart_id="CART-1770000000000-abc123"
+ - name: page
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ default: 1
+ - name: perPage
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ default: 20
+ - name: sort
+ in: query
+ required: false
+ description: PocketBase 原生排序表达式,推荐 `-cart_create`
+ schema:
+ type: string
+ example: -cart_create
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartListResponse'
+ '400':
+ description: 查询参数错误或不满足 listRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ post:
+ operationId: postPocketBaseCartRecord
+ tags:
+ - 购物车
+ summary: 创建购物车记录
+ description: |
+ 使用 PocketBase 原生 records create 接口向 `tbl_cart` 新增记录。
+
+ 当前线上权限规则:
+ - `createRule = @request.auth.id != "" && @request.body.cart_owner = @request.auth.openid`
+ - 因此客户端创建时必须显式提交 `cart_owner`,并且值必须等于当前 token 对应的 `openid`
+
+ 这意味着:
+ - 不能依赖服务端自动补 owner
+ - 不能省略 `cart_owner`
+ - 不满足规则时 PocketBase 会直接返回 `400`
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartCreateRequest'
+ responses:
+ '200':
+ description: 创建成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartRecord'
+ '400':
+ description: 参数错误、违反字段约束或不满足 createRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+
+cartRecordById:
+ patch:
+ operationId: patchPocketBaseCartRecordByRecordId
+ tags:
+ - 购物车
+ summary: 更新购物车记录
+ description: |
+ 使用 PocketBase 原生 records update 接口更新 `tbl_cart`。
+
+ 标准调用流程:
+ 1. 先通过 `GET /pb/api/collections/tbl_cart/records?filter=cart_id="..."&perPage=1&page=1` 找到原生 `recordId`
+ 2. 再调用当前 `PATCH /pb/api/collections/tbl_cart/records/{recordId}`
+
+ 当前线上权限规则:
+ - `updateRule = @request.auth.id != "" && cart_owner = @request.auth.openid`
+ - 调用方只能修改自己的购物车记录
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: recordId
+ in: path
+ required: true
+ schema:
+ type: string
+ example: l2r3nq7rqhuob0h
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartUpdateRequest'
+ responses:
+ '200':
+ description: 更新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartRecord'
+ '400':
+ description: 参数错误或违反字段约束
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '404':
+ description: 记录不存在或不满足 updateRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ delete:
+ operationId: deletePocketBaseCartRecordByRecordId
+ tags:
+ - 购物车
+ summary: 删除购物车记录
+ description: |
+ 使用 PocketBase 原生 records delete 接口删除 `tbl_cart`。
+
+ 当前线上权限规则:
+ - `deleteRule = @request.auth.id != "" && cart_owner = @request.auth.openid`
+ - 调用方只能删除自己的购物车记录
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: recordId
+ in: path
+ required: true
+ schema:
+ type: string
+ example: l2r3nq7rqhuob0h
+ responses:
+ '204':
+ description: 删除成功
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '404':
+ description: 记录不存在或不满足 deleteRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
diff --git a/pocket-base/spec/openapi-wx/paths/company.yaml b/pocket-base/spec/openapi-wx/paths/company.yaml
new file mode 100644
index 0000000..df27177
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/company.yaml
@@ -0,0 +1,290 @@
+companyRecords:
+ post:
+ operationId: postPocketBaseCompanyRecord
+ tags:
+ - 企业信息
+ summary: 创建公司
+ description: |
+ 使用 PocketBase 原生 records create 接口向 `tbl_company` 新增一行记录。
+
+ 当前线上权限规则:
+ - `createRule = ""`,因此任何客户端都可直接创建
+ - 其他原生操作中,`update/delete/view` 仅管理员或管理后台用户允许
+
+ 注意:
+ - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
+ - `company_id` 由数据库自动生成,客户端创建时不需要传
+ - `company_id` 仍带唯一索引,可用于后续按业务 id 查询
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyCreateRequest'
+ examples:
+ default:
+ value:
+ company_name: 微信侧测试企业
+ company_type: 渠道商
+ company_entity: 张三
+ company_usci: '91310000123456789A'
+ company_nationality: 中国
+ company_nationality_code: CN
+ company_province: 上海
+ company_province_code: '310000'
+ company_city: 上海
+ company_city_code: '310100'
+ company_district: 浦东新区
+ company_district_code: '310115'
+ company_postalcode: '200000'
+ company_add: 上海市浦东新区XX路1号
+ company_status: 有效
+ company_level: A
+ company_owner_openid: wx-openid-owner-001
+ company_remark: 小程序公开创建示例
+ responses:
+ '200':
+ description: 创建成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyRecord'
+ '400':
+ description: 参数错误或违反当前集合约束
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ get:
+ operationId: getPocketBaseCompanyRecords
+ tags:
+ - 企业信息
+ summary: 查询整个 tbl_company 列表 / 根据 company_id 查询对应公司信息
+ description: |
+ 使用 PocketBase 原生 records list 接口查询 `tbl_company`。
+
+ 当前线上权限规则:
+ - `listRule = is_delete = 0`,因此默认只返回未软删除数据,且整个列表查询与条件查询都公开可读
+ - `createRule = ""`,因此创建也公开可调用
+ - `view/update/delete` 仅管理员或管理后台用户允许
+
+ 标准调用方式有两种:
+ 1. 根据 `company_id` 查询对应公司信息:
+ - `filter=company_id="WX-COMPANY-10001"`
+ - `perPage=1`
+ - `page=1`
+ 2. 查询整个 `tbl_company` 列表:
+ - 不传 `filter`
+ - 按需传 `page`、`perPage`
+
+ 注意:
+ - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
+ - PocketBase 原生标准接口里,“按 `company_id` 查询单条”和“查询整个列表”共用同一个 `GET /records` 路径,因此文档以同一个 GET operation 展示两种调用模式
+ parameters:
+ - name: filter
+ in: query
+ required: false
+ description: |
+ PocketBase 标准过滤表达式。
+
+ - 根据 `company_id` 查询单条时:`company_id="WX-COMPANY-10001"`
+ - 查询整个列表时:不传该参数
+ schema:
+ type: string
+ example: company_id="WX-COMPANY-10001"
+ - name: page
+ in: query
+ required: false
+ description: 页码
+ schema:
+ type: integer
+ minimum: 1
+ default: 1
+ - name: perPage
+ in: query
+ required: false
+ description: 每页条数;按 `company_id` 单查时建议固定为 `1`
+ schema:
+ type: integer
+ minimum: 1
+ default: 20
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyListResponse'
+ examples:
+ byCompanyId:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+ listAll:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+ notFoundByCompanyId:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items: []
+ '400':
+ description: 查询参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+
+companyRecordById:
+ patch:
+ operationId: patchPocketBaseCompanyRecordByRecordId
+ tags:
+ - 企业信息
+ summary: 通过 company_id 定位后修改公司信息
+ description: |
+ 这是 PocketBase 原生标准更新接口,实际写入路径参数仍然必须使用记录主键 `recordId`。
+
+ 如果前端手里只有 `company_id`,标准调用流程是:
+ 1. 先调用 `GET /pb/api/collections/tbl_company/records?filter=company_id="..."&perPage=1&page=1`
+ 2. 从返回结果 `items[0].id` 中取出 PocketBase 原生记录主键
+ 3. 再调用当前 `PATCH /pb/api/collections/tbl_company/records/{recordId}` 完成更新
+
+ 当前线上权限规则:
+ - `updateRule` 仅管理员或管理后台用户允许
+ - 普通公开调用不能直接更新
+ parameters:
+ - name: recordId
+ in: path
+ required: true
+ description: 通过 `company_id` 查询结果拿到的 PocketBase 记录主键 `id`
+ schema:
+ type: string
+ example: l2r3nq7rqhuob0h
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyUpdateRequest'
+ examples:
+ default:
+ value:
+ company_name: 微信侧测试企业(更新)
+ company_status: 有效
+ company_level: S
+ company_owner_openid: wx-openid-owner-001
+ company_district: 徐汇区
+ company_district_code: '310104'
+ company_remark: 通过 company_id 先定位再修改
+ responses:
+ '200':
+ description: 更新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyRecord'
+ '400':
+ description: 参数错误或违反集合约束
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 当前调用方没有 update 权限
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '404':
+ description: 记录不存在
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
diff --git a/pocket-base/spec/openapi-wx/paths/documents.yaml b/pocket-base/spec/openapi-wx/paths/documents.yaml
new file mode 100644
index 0000000..f1b8d5b
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/documents.yaml
@@ -0,0 +1,222 @@
+documentRecords:
+ get:
+ operationId: getPocketBaseDocumentRecords
+ tags:
+ - 文档信息
+ summary: 分页查询文档列表 / 按 system_dict_id 与 enum 双条件分页筛选文档
+ description: |
+ 使用 PocketBase 原生 records list 接口查询 `tbl_document`。
+
+ 当前线上权限规则:
+ - `listRule = is_delete = 0`,因此任何客户端都可直接分页查询未软删除文档
+ - `viewRule = is_delete = 0`,因此单条详情也只可读取未软删除文档
+ - `create/update/delete` 仍仅管理员或管理后台用户允许
+
+ `document_type` 的存储格式为:
+ - `system_dict_id@dict_word_enum|system_dict_id@dict_word_enum`
+
+ 业务上这里是两个独立条件,并且查询时两个条件都要满足:
+ - 条件 1:包含某个 `system_dict_id`
+ - 条件 2:包含某个 `enum`
+
+ PocketBase 原生标准接口实际只有一个 `filter` 参数,因此应在同一个 `filter` 中写成两个 `contains` 条件,例如:
+ - `system_dict_id = DICT-1774599144591-hAEFQj`
+ - `enum = UT1`
+ - 最终:`document_type ~ "DICT-1774599144591-hAEFQj" && document_type ~ "@UT1"`
+
+ 这条写法已经按线上真实数据验证通过。
+
+ 排序说明:
+ - 当前线上统一按 `document_create` 排序
+ - 若要“最新上传的排在最前面”,请传 `sort=-document_create`
+
+ 标准调用方式有两种:
+ 1. 查询整个文档列表:
+ - 不传 `filter`
+ - 按需传 `page`、`perPage`
+ - 若要按最新上传倒序,传 `sort=-document_create`
+ 2. 根据 `system_dict_id` 和 `enum` 两个业务条件分页筛选:
+ - 直接传 `filter=document_type ~ "" && document_type ~ "@"`
+ - 传 `page`、`perPage`
+ - 若要按最新上传倒序,传 `sort=-document_create`
+
+ 注意:
+ - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
+ - 如果需要更复杂的条件组合,可继续使用 PocketBase 原生 `filter` 语法自行扩展
+ parameters:
+ - name: filter
+ in: query
+ required: false
+ description: |
+ PocketBase 标准过滤表达式。
+
+ - 查全部列表时:不传
+ - 按业务条件筛选时,同时写两个 `contains` 条件
+ - 第二个条件建议带上 `@` 前缀,避免误命中
+ - 例如:`document_type ~ "DICT-1774599144591-hAEFQj" && document_type ~ "@UT1"`
+ schema:
+ type: string
+ example: document_type ~ "DICT-1774599144591-hAEFQj" && document_type ~ "@UT1"
+ - name: page
+ in: query
+ required: false
+ description: 页码
+ schema:
+ type: integer
+ minimum: 1
+ default: 1
+ - name: perPage
+ in: query
+ required: false
+ description: 每页条数
+ schema:
+ type: integer
+ minimum: 1
+ default: 20
+ - name: sort
+ in: query
+ required: false
+ description: |
+ PocketBase 原生排序表达式。
+
+ 当前线上建议使用:
+ - `-document_create`:按最新上传倒序返回
+ schema:
+ type: string
+ example: -document_create
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseDocumentListResponse'
+ examples:
+ listAll:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ document_id: 文档业务ID | string
+ document_create: 文档创建时间,由数据库自动生成 | string
+ document_effect_date: 文档生效日期 | string
+ document_expiry_date: 文档到期日期 | string
+ document_title: 文档标题 | string
+ document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
+ document_subtitle: 文档副标题 | string
+ document_summary: 文档摘要 | string
+ document_content: 正文内容 | string
+ document_image: 图片附件ID串,底层按|分隔 | string
+ document_video: 视频附件ID串,底层按|分隔 | string
+ document_file: 文件附件ID串,底层按|分隔 | string
+ document_status: 文档状态 | string
+ document_owner: 上传者openid | string
+ document_relation_model: 关联机型标识 | string
+ document_keywords: 关键词,多选按|分隔 | string
+ document_share_count: 0
+ document_download_count: 0
+ document_favorite_count: 0
+ document_embedding_status: 文档嵌入状态 | string
+ document_embedding_error: 文档嵌入错误原因 | string
+ document_embedding_lasttime: 最后一次嵌入更新时间 | string
+ document_vector_version: 向量版本号或模型名称 | string
+ document_product_categories: 产品关联文档,多选按|分隔 | string
+ document_application_scenarios: 筛选依据,多选按|分隔 | string
+ document_hotel_type: 适用场景,多选按|分隔 | string
+ document_remark: 备注 | string
+ filterByTypeToken:
+ value:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ document_id: 文档业务ID | string
+ document_create: 文档创建时间,由数据库自动生成 | string
+ document_effect_date: 文档生效日期 | string
+ document_expiry_date: 文档到期日期 | string
+ document_title: 文档标题 | string
+ document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
+ document_subtitle: 文档副标题 | string
+ document_summary: 文档摘要 | string
+ document_content: 正文内容 | string
+ document_image: 图片附件ID串,底层按|分隔 | string
+ document_video: 视频附件ID串,底层按|分隔 | string
+ document_file: 文件附件ID串,底层按|分隔 | string
+ document_status: 文档状态 | string
+ document_owner: 上传者openid | string
+ document_relation_model: 关联机型标识 | string
+ document_keywords: 关键词,多选按|分隔 | string
+ document_share_count: 0
+ document_download_count: 0
+ document_favorite_count: 0
+ document_embedding_status: 文档嵌入状态 | string
+ document_embedding_error: 文档嵌入错误原因 | string
+ document_embedding_lasttime: 最后一次嵌入更新时间 | string
+ document_vector_version: 向量版本号或模型名称 | string
+ document_product_categories: 产品关联文档,多选按|分隔 | string
+ document_application_scenarios: 筛选依据,多选按|分隔 | string
+ document_hotel_type: 适用场景,多选按|分隔 | string
+ document_remark: 备注 | string
+ - id: ofy47wp9mmm0aub
+ collectionId: pbc_3636602973
+ collectionName: tbl_document
+ created: '2026-03-28 07:20:00.000Z'
+ updated: '2026-03-28 07:20:00.000Z'
+ document_id: DOC-1774680568340-TeUSQn
+ document_create: '2026-03-28 08:22:48.000Z'
+ document_effect_date: ''
+ document_expiry_date: ''
+ document_title: 易从碳达人节能系统,为酒店每天每间房省二元,以智能推动酒店ESG双碳落地!上海酒店用品展我们在E7A01等您!!
+ document_type: DICT-1774599144591-hAEFQj@UT1
+ document_subtitle: ''
+ document_summary: ''
+ document_content: ''
+ document_image: ATT-1774680568287-zuhJWN
+ document_video: ''
+ document_file: ''
+ document_status: 有效
+ document_owner: su13106859882
+ document_relation_model: ''
+ document_keywords: ''
+ document_share_count: 0
+ document_download_count: 0
+ document_favorite_count: 0
+ document_embedding_status: ''
+ document_embedding_error: ''
+ document_embedding_lasttime: ''
+ document_vector_version: ''
+ document_product_categories: ''
+ document_application_scenarios: ''
+ document_hotel_type: ''
+ document_remark: ''
+ '400':
+ description: 查询参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
diff --git a/pocket-base/spec/openapi-wx/paths/order-hooks.yaml b/pocket-base/spec/openapi-wx/paths/order-hooks.yaml
new file mode 100644
index 0000000..a795de9
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/order-hooks.yaml
@@ -0,0 +1,334 @@
+orderList:
+ post:
+ operationId: postOrderList
+ tags:
+ - 订单
+ summary: 按索引字段模糊查询订单列表
+ description: |
+ 调用自定义 hooks API 查询当前登录用户的订单列表。
+
+ 查询规则:
+ - 仅允许查询当前 `Authorization` 对应用户自己的订单记录
+ - 支持通过 `keyword` 对多个索引相关字段做统一模糊搜索,当前实现覆盖:
+ - `order_id`
+ - `order_number`
+ - `order_source_id`
+ - 支持按 `order_status` 精确过滤
+ - 支持按 `order_source` 精确过滤
+
+ 目标软删除契约:
+ - 目标行为应仅返回 `is_delete = 0` 的记录
+ - 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: false
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderListRequest'
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderListResponse'
+ '400':
+ description: 参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+orderDetail:
+ post:
+ operationId: postOrderDetail
+ tags:
+ - 订单
+ summary: 按 order_id 精确查询订单详情
+ description: |
+ 调用自定义 hooks API 按 `order_id` 查询单条订单记录。
+
+ 查询规则:
+ - 仅允许访问当前 `Authorization` 对应用户自己的订单记录
+ - 查询键为业务 ID `order_id`,不是 PocketBase 原生 `recordId`
+
+ 目标软删除契约:
+ - 目标行为应仅允许查询 `is_delete = 0` 的记录
+ - 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderDetailRequest'
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderRecord'
+ '400':
+ description: 参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '403':
+ description: 无权访问该订单记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 未找到对应订单记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+orderCreate:
+ post:
+ operationId: postOrderCreate
+ tags:
+ - 订单
+ summary: 新增订单记录
+ description: |
+ 调用自定义 hooks API 新增一条订单记录。
+
+ 创建规则:
+ - 服务端自动根据当前 token 写入 `order_owner`
+ - 服务端自动生成 `order_id`
+ - 若未传 `order_number`,服务端会自动生成展示编号
+ - `order_source`、`order_source_id`、`order_snap`、`order_amount` 为必填
+
+ 目标软删除契约:
+ - 新建记录应默认为 `is_delete = 0`
+ - 当前仓库导出响应中尚未显式返回 `is_delete` 字段
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderCreateRequest'
+ responses:
+ '200':
+ description: 创建成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderRecord'
+ '400':
+ description: 参数错误或创建失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 重复请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+orderUpdate:
+ post:
+ operationId: postOrderUpdate
+ tags:
+ - 订单
+ summary: 修改订单记录
+ description: |
+ 调用自定义 hooks API 按 `order_id` 更新订单记录。
+
+ 更新规则:
+ - 仅允许修改当前 `Authorization` 对应用户自己的订单记录
+ - `order_id` 为必填,用于精确定位目标记录
+ - 其余字段均为可选增量更新
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderUpdateRequest'
+ responses:
+ '200':
+ description: 更新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderRecord'
+ '400':
+ description: 参数错误或更新失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '403':
+ description: 无权访问该订单记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 未找到待修改的订单记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 重复请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+orderDelete:
+ post:
+ operationId: postOrderDelete
+ tags:
+ - 订单
+ summary: 软删除订单记录
+ description: |
+ 目标契约:按 `order_id` 软删除订单记录,将 `is_delete` 标记为 `1`,而不是物理删除。
+
+ 当前仓库实现差异:
+ - 当前 `cartOrderService.deleteOrder()` 仍直接调用 `$app.delete(record)` 做物理删除
+ - 因此当前后端实现与本软删除契约不一致
+ - 若要严格按本文档执行,需先同步调整后端服务实现,并在列表/详情接口中补充 `is_delete = 0` 过滤
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderDeleteRequest'
+ responses:
+ '200':
+ description: 删除成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderDeleteResponse'
+ '400':
+ description: 参数错误或删除失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '403':
+ description: 无权访问该订单记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 未找到待删除的订单记录
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体必须为 application/json
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 重复请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
diff --git a/pocket-base/spec/openapi-wx/paths/order-native.yaml b/pocket-base/spec/openapi-wx/paths/order-native.yaml
new file mode 100644
index 0000000..a47fe9c
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/order-native.yaml
@@ -0,0 +1,241 @@
+orderRecords:
+ get:
+ operationId: getPocketBaseOrderRecords
+ tags:
+ - 订单
+ summary: 查询当前用户订单列表或按业务 ID 精确查询
+ description: |
+ 使用 PocketBase 原生 records list 接口查询 `tbl_order`。
+
+ 当前线上权限规则:
+ - `listRule = @request.auth.id != "" && order_owner = @request.auth.openid && is_delete = 0`
+ - 因此调用方只能读到 `order_owner` 等于自己 `openid` 且未软删除的记录
+
+ 标准调用方式:
+ 1. 查询当前登录用户全部订单:
+ - 不传 `filter`
+ - 可选传 `sort=-order_create`
+ 2. 按业务 ID 精确查单条:
+ - `filter=order_id="ORDER-..."`
+ - `perPage=1`
+ - `page=1`
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: filter
+ in: query
+ required: false
+ schema:
+ type: string
+ example: order_id="ORDER-1770000000000-abc123"
+ - name: page
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ default: 1
+ - name: perPage
+ in: query
+ required: false
+ schema:
+ type: integer
+ minimum: 1
+ default: 20
+ - name: sort
+ in: query
+ required: false
+ description: PocketBase 原生排序表达式,推荐 `-order_create`
+ schema:
+ type: string
+ example: -order_create
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderListResponse'
+ '400':
+ description: 查询参数错误或不满足 listRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ post:
+ operationId: postPocketBaseOrderRecord
+ tags:
+ - 订单
+ summary: 创建订单记录
+ description: |
+ 使用 PocketBase 原生 records create 接口向 `tbl_order` 新增记录。
+
+ 当前线上权限规则:
+ - `createRule = @request.auth.id != "" && @request.body.order_owner = @request.auth.openid`
+ - 因此客户端创建时必须显式提交 `order_owner`,并且值必须等于当前 token 对应的 `openid`
+
+ 这意味着:
+ - 不能依赖服务端自动补 owner
+ - 不能省略 `order_owner`
+ - 不满足规则时 PocketBase 会直接返回 `400`
+ security:
+ - BearerAuth: []
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderCreateRequest'
+ responses:
+ '200':
+ description: 创建成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderRecord'
+ '400':
+ description: 参数错误、违反字段约束或不满足 createRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+
+orderRecordById:
+ patch:
+ operationId: patchPocketBaseOrderRecordByRecordId
+ tags:
+ - 订单
+ summary: 更新订单记录
+ description: |
+ 使用 PocketBase 原生 records update 接口更新 `tbl_order`。
+
+ 标准调用流程:
+ 1. 先通过 `GET /pb/api/collections/tbl_order/records?filter=order_id="..."&perPage=1&page=1` 找到原生 `recordId`
+ 2. 再调用当前 `PATCH /pb/api/collections/tbl_order/records/{recordId}`
+
+ 当前线上权限规则:
+ - `updateRule = @request.auth.id != "" && order_owner = @request.auth.openid`
+ - 调用方只能修改自己的订单记录
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: recordId
+ in: path
+ required: true
+ schema:
+ type: string
+ example: l2r3nq7rqhuob0h
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderUpdateRequest'
+ responses:
+ '200':
+ description: 更新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderRecord'
+ '400':
+ description: 参数错误或违反字段约束
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '404':
+ description: 记录不存在或不满足 updateRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ delete:
+ operationId: deletePocketBaseOrderRecordByRecordId
+ tags:
+ - 订单
+ summary: 删除订单记录
+ description: |
+ 使用 PocketBase 原生 records delete 接口删除 `tbl_order`。
+
+ 当前线上权限规则:
+ - `deleteRule = @request.auth.id != "" && order_owner = @request.auth.openid`
+ - 调用方只能删除自己的订单记录
+ security:
+ - BearerAuth: []
+ parameters:
+ - name: recordId
+ in: path
+ required: true
+ schema:
+ type: string
+ example: l2r3nq7rqhuob0h
+ responses:
+ '204':
+ description: 删除成功
+ '401':
+ description: token 缺失、无效或已过期
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '404':
+ description: 记录不存在或不满足 deleteRule
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
diff --git a/pocket-base/spec/openapi-wx/paths/products.yaml b/pocket-base/spec/openapi-wx/paths/products.yaml
new file mode 100644
index 0000000..d6ea2cf
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/products.yaml
@@ -0,0 +1,119 @@
+productRecords:
+ get:
+ operationId: getPocketBaseProductListRecords
+ tags:
+ - 产品信息
+ summary: 根据产品分类精确筛选并按分类排序值升序返回产品列表
+ description: |
+ 使用 PocketBase 原生 records list 接口查询 `tbl_product_list`。
+
+ 当前接口约定:
+ - 默认仅返回 `is_delete = 0` 的未软删除产品
+ - 条件:按 `prod_list_category` 精确匹配筛选
+ - 排序:按 `prod_list_sort` 从小到大排序
+
+ 标准调用参数建议:
+ - `filter=prod_list_category="<产品分类>"`
+ - `sort=prod_list_sort`
+
+ 注意:
+ - 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
+ - 若不传 `sort`,将由 PocketBase 默认排序策略决定返回顺序
+ parameters:
+ - name: filter
+ in: query
+ required: true
+ description: |
+ PocketBase 标准过滤表达式,当前要求按产品分类精确值筛选。
+
+ 推荐写法:`prod_list_category="<产品分类>"`
+ schema:
+ type: string
+ example: prod_list_category="<产品分类>"
+ - name: page
+ in: query
+ required: false
+ description: 页码
+ schema:
+ type: integer
+ minimum: 1
+ default: 1
+ - name: perPage
+ in: query
+ required: false
+ description: 每页条数
+ schema:
+ type: integer
+ minimum: 1
+ default: 20
+ - name: sort
+ in: query
+ required: false
+ description: |
+ PocketBase 原生排序表达式。
+
+ 当前要求使用:
+ - `prod_list_sort`:按分类排序值从小到大
+ schema:
+ type: string
+ example: prod_list_sort
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseProductListListResponse'
+ examples:
+ byCategoryAscSort:
+ value:
+ page: <页码>|
+ perPage: <每页条数>|
+ totalItems: <总记录数>|
+ totalPages: <总页数>|
+ items:
+ - id: |
+ collectionId: <集合ID>|
+ collectionName: <集合名称>|
+ created: <记录创建时间>|
+ updated: <记录更新时间>|
+ prod_list_id: <产品列表业务ID>|
+ prod_list_name: <产品名称>|
+ prod_list_modelnumber: <产品型号>|
+ prod_list_icon: <产品图标附件ID>|
+ prod_list_description: <产品说明>|
+ prod_list_feature: <产品特色>|
+ prod_list_parameters:
+ - name: <属性名>|
+ value: <属性值>|
+ prod_list_plantype: <产品方案>|
+ prod_list_category: <产品分类>|
+ prod_list_sort: 10
+ prod_list_comm_type: <通讯类型>|
+ prod_list_series: <产品系列>|
+ prod_list_power_supply: <供电方式>|
+ prod_list_tags: <产品标签>|
+ prod_list_status: <产品状态>|
+ prod_list_basic_price: 1999
+ prod_list_vip_price:
+ - viplevel: VIP1
+ price: 1899
+ prod_list_remark: <备注>|
+ '400':
+ description: 查询参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '403':
+ description: 集合规则被锁定或服务端权限设置异常
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseNativeError'
diff --git a/pocket-base/spec/openapi-wx/paths/system.yaml b/pocket-base/spec/openapi-wx/paths/system.yaml
new file mode 100644
index 0000000..e47b02a
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/system.yaml
@@ -0,0 +1,89 @@
+usersCount:
+ post:
+ security: []
+ operationId: postSystemUsersCount
+ tags:
+ - 系统
+ summary: 查询用户总数
+ description: 统计 `tbl_auth_users` 集合中的记录总数。
+ responses:
+ '200':
+ description: 查询成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/UsersCountResponse'
+ '400':
+ description: 请求参数错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+refreshToken:
+ post:
+ security:
+ - BearerAuth: []
+ - {}
+ operationId: postSystemRefreshToken
+ tags:
+ - 系统
+ summary: 刷新认证 token
+ description: |
+ 当当前 `Authorization` 仍有效时,直接基于当前 auth 用户续签。
+ 当 token 失效时,可传入 `users_wx_code` 走微信 code 重新签发。
+ requestBody:
+ required: false
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/SystemRefreshTokenRequest'
+ responses:
+ '200':
+ description: 刷新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/RefreshTokenResponse'
+ '400':
+ description: 参数错误或微信 code 换取失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 无效,且未提供有效的 `users_wx_code`
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '404':
+ description: 当前用户不存在
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体不是 JSON
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
diff --git a/pocket-base/spec/openapi-wx/paths/wechat-auth.yaml b/pocket-base/spec/openapi-wx/paths/wechat-auth.yaml
new file mode 100644
index 0000000..b8c0020
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/paths/wechat-auth.yaml
@@ -0,0 +1,117 @@
+wechatLogin:
+ post:
+ security: []
+ operationId: postWechatLogin
+ tags:
+ - 微信认证
+ summary: 微信登录或首次注册
+ description: |
+ 使用微信 `users_wx_code` 换取微信 openid。
+ 若 `tbl_auth_users` 中不存在对应用户,则自动创建新 auth 用户并返回 token。
+ 首次注册时,`users_level` 默认保持为空,不自动写入会员等级。
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/WechatLoginRequest'
+ responses:
+ '200':
+ description: 登录或注册成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/AuthSuccessResponse'
+ '400':
+ description: 参数错误或保存用户失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: 认证失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体不是 JSON
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+
+wechatProfile:
+ post:
+ security:
+ - BearerAuth: []
+ operationId: postWechatProfile
+ tags:
+ - 微信认证
+ summary: 更新微信用户资料
+ description: |
+ 基于当前 `Authorization` 对应的 auth 用户按“非空字段增量更新”资料。
+
+ 更新规则:
+ - 所有字段都不是必填
+ - 如果传了 `users_phone_code`,服务端优先调用微信接口换取真实手机号并写入 `users_phone`
+ - 如果没传 `users_phone_code`,但传了 `users_phone`,则直接将该手机号写入数据库
+ - 如果上传了 `users_picture`、`users_id_pic_a`、`users_id_pic_b`、`users_title_picture`,会按附件 ID 进行关联校验并更新
+ - 若当前用户类型为 `游客`,且本次未显式传 `users_type`,服务端会自动升级为 `注册用户`
+ - 如果某个字段未传或传空,则不会清空数据库中的已有值
+ - 只有请求体里非空的字段才会更新到数据库
+ requestBody:
+ required: true
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/WechatProfileRequest'
+ responses:
+ '200':
+ description: 更新成功
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/WechatProfileResponse'
+ '400':
+ description: 参数错误、手机号已被占用、附件 ID 无效、微信手机号换取失败或资料更新失败
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '401':
+ description: token 无效或缺少 openid
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '415':
+ description: 请求体不是 JSON
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '429':
+ description: 请求过于频繁
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
+ '500':
+ description: 服务端错误
+ content:
+ application/json:
+ schema:
+ $ref: '../../openapi-wx.yaml#/components/schemas/ErrorResponse'
diff --git a/pocket-base/spec/openapi-wx/schemas/attachments.yaml b/pocket-base/spec/openapi-wx/schemas/attachments.yaml
new file mode 100644
index 0000000..aee528f
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/attachments.yaml
@@ -0,0 +1,125 @@
+PocketBaseAttachmentRecord:
+ type: object
+ properties:
+ id:
+ type: string
+ description: PocketBase 记录主键
+ example: PocketBase记录主键 | string
+ collectionId:
+ type: string
+ description: 集合ID
+ example: 集合ID | string
+ collectionName:
+ type: string
+ description: 集合名称
+ example: 集合名称 | string
+ attachments_id:
+ type: string
+ description: 附件业务 ID
+ example: 附件业务ID | string
+ attachments_link:
+ type: string
+ description: PocketBase 文件字段值,可按标准文件路径拼接文件流地址
+ example: PocketBase文件字段值,可拼接文件流地址 | string
+ attachments_filename:
+ type: string
+ description: 原始文件名
+ example: 原始文件名 | string
+ attachments_filetype:
+ type: string
+ description: 文件类型 / MIME
+ example: 文件类型或MIME | string
+ attachments_size:
+ type:
+ - number
+ - integer
+ - string
+ description: 文件大小
+ example: 文件大小 | number
+ attachments_owner:
+ type: string
+ description: 上传者业务标识
+ example: 上传者业务标识 | string
+ attachments_md5:
+ type: string
+ description: 文件 MD5
+ example: 文件MD5 | string
+ attachments_ocr:
+ type: string
+ description: OCR 识别结果
+ example: OCR识别结果 | string
+ attachments_status:
+ type: string
+ description: 附件状态
+ example: 附件状态 | string
+ attachments_remark:
+ type: string
+ description: 备注
+ example: 备注 | string
+ example:
+ id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ attachments_id: 附件业务ID | string
+ attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
+ attachments_filename: 原始文件名 | string
+ attachments_filetype: 文件类型或MIME | string
+ attachments_size: 文件大小 | number
+ attachments_owner: 上传者业务标识 | string
+ attachments_md5: 文件MD5 | string
+ attachments_ocr: OCR识别结果 | string
+ attachments_status: 附件状态 | string
+ attachments_remark: 备注 | string
+
+PocketBaseAttachmentListResponse:
+ type: object
+ required:
+ - page
+ - perPage
+ - totalItems
+ - totalPages
+ - items
+ properties:
+ page:
+ type:
+ - integer
+ - string
+ example: 页码 | integer
+ perPage:
+ type:
+ - integer
+ - string
+ example: 每页条数 | integer
+ totalItems:
+ type:
+ - integer
+ - string
+ example: 总记录数 | integer
+ totalPages:
+ type:
+ - integer
+ - string
+ example: 总页数 | integer
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseAttachmentRecord'
+ example:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ attachments_id: 附件业务ID | string
+ attachments_link: PocketBase文件字段值,可拼接文件流地址 | string
+ attachments_filename: 原始文件名 | string
+ attachments_filetype: 文件类型或MIME | string
+ attachments_size: 文件大小 | number
+ attachments_owner: 上传者业务标识 | string
+ attachments_md5: 文件MD5 | string
+ attachments_ocr: OCR识别结果 | string
+ attachments_status: 附件状态 | string
+ attachments_remark: 备注 | string
diff --git a/pocket-base/spec/openapi-wx/schemas/cart-hooks.yaml b/pocket-base/spec/openapi-wx/schemas/cart-hooks.yaml
new file mode 100644
index 0000000..894f79a
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/cart-hooks.yaml
@@ -0,0 +1,185 @@
+CartRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/HookRecordBase'
+ - type: object
+ properties:
+ cart_id:
+ type: string
+ description: 购物车业务 ID
+ example: CART-1770000000000-abc123
+ cart_number:
+ type: string
+ description: 购物车名称或分组号
+ example: wx-user-20260403153000
+ cart_create:
+ type: string
+ description: 购物车项创建时间,由数据库自动生成
+ example: 2026-04-03 15:30:00.000Z
+ cart_owner:
+ type: string
+ description: 购物车所有者 openid
+ example: wx-openid-user-001
+ cart_product_id:
+ type: string
+ description: 产品业务 ID
+ example: PROD-1770000000000-abcd12
+ cart_product_quantity:
+ type:
+ - integer
+ - number
+ description: 产品数量
+ example: 2
+ cart_status:
+ type: string
+ description: 购物车状态
+ example: 有效
+ cart_at_price:
+ type:
+ - integer
+ - number
+ description: 加入购物车时价格
+ example: 1999
+ cart_remark:
+ type: string
+ description: 备注
+ example: 小程序加入购物车示例
+ is_delete:
+ type:
+ - integer
+ - number
+ description: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出
+ example: 0
+ product_name:
+ type: string
+ description: 产品名称(服务端联动补充)
+ example: BAI 智能主机
+ product_modelnumber:
+ type: string
+ description: 产品型号(服务端联动补充)
+ example: BAI-HOST-01
+ product_basic_price:
+ type:
+ - integer
+ - number
+ - 'null'
+ description: 产品基础价格(服务端联动补充)
+ example: 1999
+
+CartListRequest:
+ type: object
+ properties:
+ keyword:
+ type: string
+ description: 对 `cart_id`、`cart_number`、`cart_product_id` 等索引相关字段的统一模糊搜索关键字
+ example: CART-1770
+ cart_status:
+ type: string
+ description: 购物车状态精确过滤
+ example: 有效
+ cart_number:
+ type: string
+ description: 购物车名称或分组号精确过滤
+ example: wx-user-20260403153000
+
+CartDetailRequest:
+ type: object
+ required:
+ - cart_id
+ properties:
+ cart_id:
+ type: string
+ description: 购物车业务 ID
+ example: CART-1770000000000-abc123
+
+CartCreateRequest:
+ type: object
+ required:
+ - cart_product_id
+ - cart_product_quantity
+ - cart_at_price
+ properties:
+ cart_number:
+ type: string
+ description: 可选;未传时服务端自动生成
+ cart_product_id:
+ type: string
+ description: 产品业务 ID
+ example: PROD-1770000000000-abcd12
+ cart_product_quantity:
+ type:
+ - integer
+ - number
+ description: 产品数量,需为正整数
+ example: 2
+ cart_status:
+ type: string
+ description: 可选;未传时默认 `有效`
+ example: 有效
+ cart_at_price:
+ type:
+ - integer
+ - number
+ description: 加入购物车时价格
+ example: 1999
+ cart_remark:
+ type: string
+ description: 备注
+ example: 小程序加入购物车示例
+
+CartUpdateRequest:
+ type: object
+ required:
+ - cart_id
+ properties:
+ cart_id:
+ type: string
+ description: 购物车业务 ID
+ example: CART-1770000000000-abc123
+ cart_number:
+ type: string
+ cart_product_id:
+ type: string
+ cart_product_quantity:
+ type:
+ - integer
+ - number
+ cart_status:
+ type: string
+ cart_at_price:
+ type:
+ - integer
+ - number
+ cart_remark:
+ type: string
+
+CartDeleteRequest:
+ type: object
+ required:
+ - cart_id
+ properties:
+ cart_id:
+ type: string
+ description: 购物车业务 ID
+ example: CART-1770000000000-abc123
+
+CartListResponse:
+ type: object
+ properties:
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CartRecord'
+
+CartDeleteResponse:
+ type: object
+ properties:
+ cart_id:
+ type: string
+ description: 购物车业务 ID
+ example: CART-1770000000000-abc123
+ is_delete:
+ type:
+ - integer
+ - number
+ description: 目标软删除标记值;当前实现可能仍返回物理删除结果
+ example: 1
diff --git a/pocket-base/spec/openapi-wx/schemas/cart-native.yaml b/pocket-base/spec/openapi-wx/schemas/cart-native.yaml
new file mode 100644
index 0000000..8e4ea47
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/cart-native.yaml
@@ -0,0 +1,134 @@
+PocketBaseCartFields:
+ type: object
+ properties:
+ cart_id:
+ type: string
+ description: 购物车业务 ID
+ example: CART-1770000000000-abc123
+ cart_number:
+ type: string
+ description: 购物车名称或分组号
+ example: wx-user-20260403153000
+ cart_create:
+ type: string
+ description: 购物车项创建时间,由数据库自动生成
+ example: 2026-04-03 15:30:00.000Z
+ cart_owner:
+ type: string
+ description: 购物车所有者 openid
+ example: wx-openid-user-001
+ cart_product_id:
+ type: string
+ description: 产品业务 ID
+ example: PROD-1770000000000-abcd12
+ cart_product_quantity:
+ type:
+ - integer
+ - number
+ description: 产品数量
+ example: 2
+ cart_status:
+ type: string
+ description: 购物车状态
+ example: 有效
+ cart_at_price:
+ type:
+ - number
+ - integer
+ description: 加入购物车时价格
+ example: 1999
+ cart_remark:
+ type: string
+ description: 备注
+ example: 小程序加入购物车示例
+
+PocketBaseCartRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseRecordBase'
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartFields'
+
+PocketBaseCartCreateRequest:
+ type: object
+ required:
+ - cart_id
+ - cart_number
+ - cart_owner
+ - cart_product_id
+ - cart_product_quantity
+ - cart_status
+ - cart_at_price
+ properties:
+ cart_id:
+ type: string
+ cart_number:
+ type: string
+ cart_owner:
+ type: string
+ description: 必须显式提交,且值必须等于当前 token 对应 openid
+ cart_product_id:
+ type: string
+ cart_product_quantity:
+ type:
+ - integer
+ - number
+ cart_status:
+ type: string
+ cart_at_price:
+ type:
+ - number
+ - integer
+ cart_remark:
+ type: string
+
+PocketBaseCartUpdateRequest:
+ type: object
+ properties:
+ cart_number:
+ type: string
+ cart_owner:
+ type: string
+ description: 若提交,必须仍等于当前 token 对应 openid
+ cart_product_id:
+ type: string
+ cart_product_quantity:
+ type:
+ - integer
+ - number
+ cart_status:
+ type: string
+ cart_at_price:
+ type:
+ - number
+ - integer
+ cart_remark:
+ type: string
+
+PocketBaseCartListResponse:
+ type: object
+ required:
+ - page
+ - perPage
+ - totalItems
+ - totalPages
+ - items
+ properties:
+ page:
+ type:
+ - integer
+ - string
+ perPage:
+ type:
+ - integer
+ - string
+ totalItems:
+ type:
+ - integer
+ - string
+ totalPages:
+ type:
+ - integer
+ - string
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCartRecord'
diff --git a/pocket-base/spec/openapi-wx/schemas/common.yaml b/pocket-base/spec/openapi-wx/schemas/common.yaml
new file mode 100644
index 0000000..de62eb5
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/common.yaml
@@ -0,0 +1,125 @@
+ApiResponseBase:
+ type: object
+ required:
+ - statusCode
+ - errMsg
+ - data
+ properties:
+ statusCode:
+ type:
+ - integer
+ - string
+ description: "业务状态码"
+ example: 业务状态码 | integer
+ errMsg:
+ type: string
+ description: "业务提示信息"
+ example: 业务提示信息 | string
+ data:
+ description: "业务响应数据"
+ type: object
+ additionalProperties: true
+ example:
+ statusCode: 业务状态码 | integer
+ errMsg: 业务提示信息 | string
+ data:
+ 任意业务字段: 业务响应数据 | object
+
+ErrorResponse:
+ type: object
+ required:
+ - statusCode
+ - errMsg
+ - data
+ properties:
+ statusCode:
+ type:
+ - integer
+ - string
+ description: "业务状态码"
+ example: 业务状态码 | integer
+ errMsg:
+ type: string
+ description: "业务提示信息"
+ example: 失败原因提示 | string
+ data:
+ description: "业务响应数据"
+ type: object
+ additionalProperties: true
+ example:
+ statusCode: 业务状态码 | integer
+ errMsg: 失败原因提示 | string
+ data:
+ 任意错误字段: 错误附加信息 | object
+
+HookRecordBase:
+ type: object
+ properties:
+ pb_id:
+ type: string
+ description: PocketBase 记录主键 id
+ example: l2r3nq7rqhuob0h
+ created:
+ type: string
+ description: PocketBase 系统创建时间
+ example: 2026-04-03 15:30:00.000Z
+ updated:
+ type: string
+ description: PocketBase 系统更新时间
+ example: 2026-04-03 15:35:00.000Z
+
+PocketBaseNativeError:
+ type: object
+ properties:
+ code:
+ type:
+ - integer
+ - string
+ description: "业务状态码"
+ example: 错误状态码 | integer
+ message:
+ type: string
+ example: PocketBase原生错误信息 | string
+ data:
+ description: "业务响应数据"
+ type: object
+ additionalProperties: true
+ example:
+ code: 错误状态码 | integer
+ message: PocketBase原生错误信息 | string
+ data:
+ 任意错误字段: 原生错误附加信息 | object
+
+PocketBaseRecordBase:
+ type: object
+ required:
+ - id
+ - collectionId
+ - collectionName
+ - created
+ - updated
+ properties:
+ id:
+ type: string
+ description: "PocketBase 记录主键"
+ example: PocketBase记录主键 | string
+ collectionId:
+ type: string
+ example: 集合ID | string
+ collectionName:
+ type: string
+ example: 集合名称 | string
+ created:
+ type: string
+ description: "记录创建时间"
+ example: 记录创建时间 | string
+ updated:
+ type: string
+ description: "记录更新时间"
+ example: 记录更新时间 | string
+ example:
+ id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
diff --git a/pocket-base/spec/openapi-wx/schemas/company.yaml b/pocket-base/spec/openapi-wx/schemas/company.yaml
new file mode 100644
index 0000000..a1c6610
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/company.yaml
@@ -0,0 +1,412 @@
+CompanyInfo:
+ anyOf:
+ - type: object
+ description: 用户所属公司信息;当用户尚未绑定公司时返回 `null`
+ properties:
+ pb_id:
+ type: string
+ description: PocketBase 记录主键 id
+ example: PocketBase记录主键id | string
+ company_id:
+ type: string
+ description: 公司业务 id,由数据库自动生成
+ example: 公司业务id,由数据库自动生成 | string
+ company_name:
+ type: string
+ description: 公司名称
+ example: 公司名称 | string
+ company_type:
+ type: string
+ description: 公司类型
+ example: 公司类型 | string
+ company_entity:
+ type: string
+ description: 公司法人
+ example: 公司法人 | string
+ company_usci:
+ type: string
+ description: 统一社会信用代码
+ example: 统一社会信用代码 | string
+ company_nationality:
+ type: string
+ description: 国家名称
+ example: 国家名称 | string
+ company_nationality_code:
+ type: string
+ description: 国家编码
+ example: 国家编码 | string
+ company_province:
+ type: string
+ description: 省份名称
+ example: 省份名称 | string
+ company_province_code:
+ type: string
+ description: 省份编码
+ example: 省份编码 | string
+ company_city:
+ type: string
+ description: 城市名称
+ example: 城市名称 | string
+ company_city_code:
+ type: string
+ description: 城市编码
+ example: 城市编码 | string
+ company_district:
+ type: string
+ description: 区/县名称
+ example: 区县名称 | string
+ company_district_code:
+ type: string
+ description: 区/县编码
+ example: 区县编码 | string
+ company_postalcode:
+ type: string
+ description: 邮政编码
+ example: 邮政编码 | string
+ company_add:
+ type: string
+ description: 公司地址
+ example: 公司地址 | string
+ company_status:
+ type: string
+ description: 公司状态
+ example: 公司状态 | string
+ company_level:
+ type: string
+ description: 公司等级
+ example: 公司等级 | string
+ company_owner_openid:
+ type: string
+ description: 公司所有者 openid
+ example: 公司所有者openid | string
+ company_remark:
+ type: string
+ description: 备注
+ example: 备注 | string
+ created:
+ type: string
+ description: 记录创建时间
+ example: 记录创建时间 | string
+ updated:
+ type: string
+ description: 记录更新时间
+ example: 记录更新时间 | string
+ example:
+ pb_id: PocketBase记录主键id | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ - type: 'null'
+
+PocketBaseCompanyFields:
+ type: object
+ properties:
+ company_id:
+ type: string
+ description: 公司业务 id,由数据库自动生成
+ example: 公司业务id,由数据库自动生成 | string
+ company_name:
+ type: string
+ description: 公司名称
+ example: 公司名称 | string
+ company_type:
+ type: string
+ description: 公司类型
+ example: 公司类型 | string
+ company_entity:
+ type: string
+ description: 公司法人
+ example: 公司法人 | string
+ company_usci:
+ type: string
+ description: 统一社会信用代码
+ example: 统一社会信用代码 | string
+ company_nationality:
+ type: string
+ description: 国家名称
+ example: 国家名称 | string
+ company_nationality_code:
+ type: string
+ description: 国家编码
+ example: 国家编码 | string
+ company_province:
+ type: string
+ description: 省份名称
+ example: 省份名称 | string
+ company_province_code:
+ type: string
+ description: 省份编码
+ example: 省份编码 | string
+ company_city:
+ type: string
+ description: 城市名称
+ example: 城市名称 | string
+ company_city_code:
+ type: string
+ description: 城市编码
+ example: 城市编码 | string
+ company_district:
+ type: string
+ description: 区/县名称
+ example: 区县名称 | string
+ company_district_code:
+ type: string
+ description: 区/县编码
+ example: 区县编码 | string
+ company_postalcode:
+ type: string
+ description: 邮政编码
+ example: 邮政编码 | string
+ company_add:
+ type: string
+ description: 公司地址
+ example: 公司地址 | string
+ company_status:
+ type: string
+ description: 公司状态
+ example: 公司状态 | string
+ company_level:
+ type: string
+ description: 公司等级
+ example: 公司等级 | string
+ company_owner_openid:
+ type: string
+ description: 公司所有者 openid
+ example: 公司所有者openid | string
+ company_remark:
+ type: string
+ description: 备注
+ example: 备注 | string
+
+PocketBaseCompanyRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseRecordBase'
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyFields'
+ example:
+ id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+
+PocketBaseCompanyCreateRequest:
+ type: object
+ properties:
+ company_name:
+ description: "公司名称"
+ type: string
+ company_type:
+ description: "公司类型"
+ type: string
+ company_entity:
+ description: "公司法人"
+ type: string
+ company_usci:
+ description: "统一社会信用代码"
+ type: string
+ company_nationality:
+ description: "国家名称"
+ type: string
+ company_nationality_code:
+ description: "国家编码"
+ type: string
+ company_province:
+ description: "省份名称"
+ type: string
+ company_province_code:
+ description: "省份编码"
+ type: string
+ company_city:
+ description: "城市名称"
+ type: string
+ company_city_code:
+ description: "城市编码"
+ type: string
+ company_district:
+ description: "区 / 县名称"
+ type: string
+ company_district_code:
+ description: "区 / 县编码"
+ type: string
+ company_postalcode:
+ description: "邮编"
+ type: string
+ company_add:
+ description: "地址"
+ type: string
+ company_status:
+ description: "公司状态"
+ type: string
+ company_level:
+ description: "公司等级"
+ type: string
+ company_owner_openid:
+ description: "公司所有者 openid"
+ type: string
+ company_remark:
+ description: "备注"
+ type: string
+ additionalProperties: false
+
+PocketBaseCompanyUpdateRequest:
+ type: object
+ properties:
+ company_id:
+ description: "所属公司业务 ID"
+ type: string
+ company_name:
+ description: "公司名称"
+ type: string
+ company_type:
+ description: "公司类型"
+ type: string
+ company_entity:
+ description: "公司法人"
+ type: string
+ company_usci:
+ description: "统一社会信用代码"
+ type: string
+ company_nationality:
+ description: "国家名称"
+ type: string
+ company_nationality_code:
+ description: "国家编码"
+ type: string
+ company_province:
+ description: "省份名称"
+ type: string
+ company_province_code:
+ description: "省份编码"
+ type: string
+ company_city:
+ description: "城市名称"
+ type: string
+ company_city_code:
+ description: "城市编码"
+ type: string
+ company_district:
+ description: "区 / 县名称"
+ type: string
+ company_district_code:
+ description: "区 / 县编码"
+ type: string
+ company_postalcode:
+ description: "邮编"
+ type: string
+ company_add:
+ description: "地址"
+ type: string
+ company_status:
+ description: "公司状态"
+ type: string
+ company_level:
+ description: "公司等级"
+ type: string
+ company_owner_openid:
+ description: "公司所有者 openid"
+ type: string
+ company_remark:
+ description: "备注"
+ type: string
+ additionalProperties: false
+
+PocketBaseCompanyListResponse:
+ type: object
+ required:
+ - page
+ - perPage
+ - totalItems
+ - totalPages
+ - items
+ properties:
+ page:
+ type:
+ - integer
+ - string
+ example: 页码 | integer
+ perPage:
+ type:
+ - integer
+ - string
+ example: 每页条数 | integer
+ totalItems:
+ type:
+ - integer
+ - string
+ example: 总记录数 | integer
+ totalPages:
+ type:
+ - integer
+ - string
+ example: 总页数 | integer
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseCompanyRecord'
+ example:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
diff --git a/pocket-base/spec/openapi-wx/schemas/documents.yaml b/pocket-base/spec/openapi-wx/schemas/documents.yaml
new file mode 100644
index 0000000..b0a9ebd
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/documents.yaml
@@ -0,0 +1,221 @@
+PocketBaseDocumentFields:
+ type: object
+ properties:
+ document_id:
+ type: string
+ description: "文档业务 ID"
+ example: 文档业务ID | string
+ document_create:
+ type: string
+ description: "文档创建时间,由数据库自动生成"
+ example: 文档创建时间,由数据库自动生成 | string
+ document_effect_date:
+ type: string
+ description: "文档生效日期"
+ example: 文档生效日期 | string
+ document_expiry_date:
+ type: string
+ description: "文档到期日期"
+ example: 文档到期日期 | string
+ document_title:
+ type: string
+ description: "文档标题"
+ example: 文档标题 | string
+ document_type:
+ type: string
+ description: "文档类型,多选时按 system_dict_id@dict_word_enum|... 保存"
+ example: 文档类型,按system_dict_id@dict_word_enum保存 | string
+ document_subtitle:
+ type: string
+ description: "文档副标题"
+ example: 文档副标题 | string
+ document_summary:
+ type: string
+ description: "文档摘要"
+ example: 文档摘要 | string
+ document_content:
+ type: string
+ description: "正文内容,保存 Markdown"
+ example: 正文内容 | string
+ document_image:
+ type: string
+ description: "图片附件 ID 集合,底层以 | 分隔"
+ example: 图片附件ID串,底层按|分隔 | string
+ document_video:
+ type: string
+ description: "视频附件 ID 集合,底层以 | 分隔"
+ example: 视频附件ID串,底层按|分隔 | string
+ document_file:
+ type: string
+ description: "文件附件 ID 集合,底层以 | 分隔"
+ example: 文件附件ID串,底层按|分隔 | string
+ document_status:
+ type: string
+ description: "文档状态,仅 `有效` / `过期`"
+ example: 文档状态 | string
+ document_owner:
+ type: string
+ description: "上传者 openid"
+ example: 上传者openid | string
+ document_relation_model:
+ type: string
+ description: "关联机型 / 模型标识"
+ example: 关联机型标识 | string
+ document_keywords:
+ type: string
+ description: "关键词,多选后以 | 分隔"
+ example: 关键词,多选按|分隔 | string
+ document_share_count:
+ type: number
+ description: "分享次数"
+ example: 0
+ document_download_count:
+ type: number
+ description: "下载次数"
+ example: 0
+ document_favorite_count:
+ type: number
+ description: "收藏次数"
+ example: 0
+ document_embedding_status:
+ type: string
+ description: "文档嵌入状态"
+ example: 文档嵌入状态 | string
+ document_embedding_error:
+ type: string
+ description: "文档嵌入错误原因"
+ example: 文档嵌入错误原因 | string
+ document_embedding_lasttime:
+ type: string
+ description: "最后一次嵌入更新时间"
+ example: 最后一次嵌入更新时间 | string
+ document_vector_version:
+ type: string
+ description: "向量版本号 / 模型名称"
+ example: 向量版本号或模型名称 | string
+ document_product_categories:
+ type: string
+ description: "产品关联文档,多选后以 | 分隔"
+ example: 产品关联文档,多选按|分隔 | string
+ document_application_scenarios:
+ type: string
+ description: "筛选依据,多选后以 | 分隔"
+ example: 筛选依据,多选按|分隔 | string
+ document_hotel_type:
+ type: string
+ description: "适用场景,多选后以 | 分隔"
+ example: 适用场景,多选按|分隔 | string
+ document_remark:
+ type: string
+ description: "备注"
+ example: 备注 | string
+
+PocketBaseDocumentRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseRecordBase'
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseDocumentFields'
+ example:
+ id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ document_id: 文档业务ID | string
+ document_create: 文档创建时间,由数据库自动生成 | string
+ document_effect_date: 文档生效日期 | string
+ document_expiry_date: 文档到期日期 | string
+ document_title: 文档标题 | string
+ document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
+ document_subtitle: 文档副标题 | string
+ document_summary: 文档摘要 | string
+ document_content: 正文内容 | string
+ document_image: 图片附件ID串,底层按|分隔 | string
+ document_video: 视频附件ID串,底层按|分隔 | string
+ document_file: 文件附件ID串,底层按|分隔 | string
+ document_status: 文档状态 | string
+ document_owner: 上传者openid | string
+ document_relation_model: 关联机型标识 | string
+ document_keywords: 关键词,多选按|分隔 | string
+ document_share_count: 0
+ document_download_count: 0
+ document_favorite_count: 0
+ document_embedding_status: 文档嵌入状态 | string
+ document_embedding_error: 文档嵌入错误原因 | string
+ document_embedding_lasttime: 最后一次嵌入更新时间 | string
+ document_vector_version: 向量版本号或模型名称 | string
+ document_product_categories: 产品关联文档,多选按|分隔 | string
+ document_application_scenarios: 筛选依据,多选按|分隔 | string
+ document_hotel_type: 适用场景,多选按|分隔 | string
+ document_remark: 备注 | string
+
+PocketBaseDocumentListResponse:
+ type: object
+ required:
+ - page
+ - perPage
+ - totalItems
+ - totalPages
+ - items
+ properties:
+ page:
+ type:
+ - integer
+ - string
+ example: 页码 | integer
+ perPage:
+ type:
+ - integer
+ - string
+ example: 每页条数 | integer
+ totalItems:
+ type:
+ - integer
+ - string
+ example: 总记录数 | integer
+ totalPages:
+ type:
+ - integer
+ - string
+ example: 总页数 | integer
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseDocumentRecord'
+ example:
+ page: 页码 | integer
+ perPage: 每页条数 | integer
+ totalItems: 总记录数 | integer
+ totalPages: 总页数 | integer
+ items:
+ - id: PocketBase记录主键 | string
+ collectionId: 集合ID | string
+ collectionName: 集合名称 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ document_id: 文档业务ID | string
+ document_create: 文档创建时间,由数据库自动生成 | string
+ document_effect_date: 文档生效日期 | string
+ document_expiry_date: 文档到期日期 | string
+ document_title: 文档标题 | string
+ document_type: 文档类型,按system_dict_id@dict_word_enum保存 | string
+ document_subtitle: 文档副标题 | string
+ document_summary: 文档摘要 | string
+ document_content: 正文内容 | string
+ document_image: 图片附件ID串,底层按|分隔 | string
+ document_video: 视频附件ID串,底层按|分隔 | string
+ document_file: 文件附件ID串,底层按|分隔 | string
+ document_status: 文档状态 | string
+ document_owner: 上传者openid | string
+ document_relation_model: 关联机型标识 | string
+ document_keywords: 关键词,多选按|分隔 | string
+ document_share_count: 0
+ document_download_count: 0
+ document_favorite_count: 0
+ document_embedding_status: 文档嵌入状态 | string
+ document_embedding_error: 文档嵌入错误原因 | string
+ document_embedding_lasttime: 最后一次嵌入更新时间 | string
+ document_vector_version: 向量版本号或模型名称 | string
+ document_product_categories: 产品关联文档,多选按|分隔 | string
+ document_application_scenarios: 筛选依据,多选按|分隔 | string
+ document_hotel_type: 适用场景,多选按|分隔 | string
+ document_remark: 备注 | string
diff --git a/pocket-base/spec/openapi-wx/schemas/order-hooks.yaml b/pocket-base/spec/openapi-wx/schemas/order-hooks.yaml
new file mode 100644
index 0000000..79d9ecc
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/order-hooks.yaml
@@ -0,0 +1,191 @@
+OrderRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/HookRecordBase'
+ - type: object
+ properties:
+ order_id:
+ type: string
+ description: 订单业务 ID
+ example: ORDER-1770000000000-abc123
+ order_number:
+ type: string
+ description: 订单编号
+ example: wx-user-20260403153500
+ order_create:
+ type: string
+ description: 订单创建时间,由数据库自动生成
+ example: 2026-04-03 15:35:00.000Z
+ order_owner:
+ type: string
+ description: 订单所有者 openid
+ example: wx-openid-user-001
+ order_source:
+ type: string
+ description: 订单来源
+ example: 购物车
+ order_status:
+ type: string
+ description: 订单状态
+ example: 订单已生成
+ order_source_id:
+ type: string
+ description: 来源关联业务 ID
+ example: CART-1770000000000-abc123
+ order_snap:
+ description: 订单快照 JSON
+ oneOf:
+ - type: object
+ additionalProperties: true
+ - type: array
+ items:
+ type: object
+ additionalProperties: true
+ order_amount:
+ type:
+ - integer
+ - number
+ description: 订单金额
+ example: 3998
+ order_remark:
+ type: string
+ description: 备注
+ example: 小程序订单示例
+ is_delete:
+ type:
+ - integer
+ - number
+ description: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出
+ example: 0
+
+OrderListRequest:
+ type: object
+ properties:
+ keyword:
+ type: string
+ description: 对 `order_id`、`order_number`、`order_source_id` 等索引相关字段的统一模糊搜索关键字
+ example: ORDER-1770
+ order_status:
+ type: string
+ description: 订单状态精确过滤
+ example: 订单已生成
+ order_source:
+ type: string
+ description: 订单来源精确过滤
+ example: 购物车
+
+OrderDetailRequest:
+ type: object
+ required:
+ - order_id
+ properties:
+ order_id:
+ type: string
+ description: 订单业务 ID
+ example: ORDER-1770000000000-abc123
+
+OrderCreateRequest:
+ type: object
+ required:
+ - order_source
+ - order_source_id
+ - order_snap
+ - order_amount
+ properties:
+ order_number:
+ type: string
+ description: 可选;未传时服务端自动生成
+ order_source:
+ type: string
+ description: 订单来源
+ example: 购物车
+ order_status:
+ type: string
+ description: 可选;未传时默认 `订单已生成`
+ example: 订单已生成
+ order_source_id:
+ type: string
+ description: 来源关联业务 ID
+ example: CART-1770000000000-abc123
+ order_snap:
+ description: 订单快照 JSON
+ oneOf:
+ - type: object
+ additionalProperties: true
+ - type: array
+ items:
+ type: object
+ additionalProperties: true
+ order_amount:
+ type:
+ - integer
+ - number
+ description: 订单金额
+ example: 3998
+ order_remark:
+ type: string
+ description: 备注
+ example: 小程序订单示例
+
+OrderUpdateRequest:
+ type: object
+ required:
+ - order_id
+ properties:
+ order_id:
+ type: string
+ description: 订单业务 ID
+ example: ORDER-1770000000000-abc123
+ order_number:
+ type: string
+ order_source:
+ type: string
+ order_status:
+ type: string
+ order_source_id:
+ type: string
+ order_snap:
+ oneOf:
+ - type: object
+ additionalProperties: true
+ - type: array
+ items:
+ type: object
+ additionalProperties: true
+ order_amount:
+ type:
+ - integer
+ - number
+ order_remark:
+ type: string
+
+OrderDeleteRequest:
+ type: object
+ required:
+ - order_id
+ properties:
+ order_id:
+ type: string
+ description: 订单业务 ID
+ example: ORDER-1770000000000-abc123
+
+OrderListResponse:
+ type: object
+ properties:
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/OrderRecord'
+
+OrderDeleteResponse:
+ type: object
+ properties:
+ order_id:
+ type: string
+ description: 订单业务 ID
+ example: ORDER-1770000000000-abc123
+ is_delete:
+ type:
+ - integer
+ - number
+ description: 目标软删除标记值;当前实现可能仍返回物理删除结果
+ example: 1
diff --git a/pocket-base/spec/openapi-wx/schemas/order-native.yaml b/pocket-base/spec/openapi-wx/schemas/order-native.yaml
new file mode 100644
index 0000000..52de03d
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/order-native.yaml
@@ -0,0 +1,154 @@
+PocketBaseOrderFields:
+ type: object
+ properties:
+ order_id:
+ type: string
+ description: 订单业务 ID
+ example: ORDER-1770000000000-abc123
+ order_number:
+ type: string
+ description: 订单编号
+ example: wx-user-20260403153500
+ order_create:
+ type: string
+ description: 订单创建时间,由数据库自动生成
+ example: 2026-04-03 15:35:00.000Z
+ order_owner:
+ type: string
+ description: 订单所有者 openid
+ example: wx-openid-user-001
+ order_source:
+ type: string
+ description: 订单来源
+ example: 购物车
+ order_status:
+ type: string
+ description: 订单状态
+ example: 订单已生成
+ order_source_id:
+ type: string
+ description: 来源关联业务 ID
+ example: CART-1770000000000-abc123
+ order_snap:
+ description: 订单快照 JSON
+ oneOf:
+ - type: object
+ additionalProperties: true
+ - type: array
+ items:
+ type: object
+ additionalProperties: true
+ order_amount:
+ type:
+ - number
+ - integer
+ description: 订单金额
+ example: 3998
+ order_remark:
+ type: string
+ description: 备注
+ example: 小程序订单示例
+
+PocketBaseOrderRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseRecordBase'
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderFields'
+
+PocketBaseOrderCreateRequest:
+ type: object
+ required:
+ - order_id
+ - order_number
+ - order_owner
+ - order_source
+ - order_status
+ - order_source_id
+ - order_snap
+ - order_amount
+ properties:
+ order_id:
+ type: string
+ order_number:
+ type: string
+ order_owner:
+ type: string
+ description: 必须显式提交,且值必须等于当前 token 对应 openid
+ order_source:
+ type: string
+ order_status:
+ type: string
+ order_source_id:
+ type: string
+ order_snap:
+ oneOf:
+ - type: object
+ additionalProperties: true
+ - type: array
+ items:
+ type: object
+ additionalProperties: true
+ order_amount:
+ type:
+ - number
+ - integer
+ order_remark:
+ type: string
+
+PocketBaseOrderUpdateRequest:
+ type: object
+ properties:
+ order_number:
+ type: string
+ order_owner:
+ type: string
+ description: 若提交,必须仍等于当前 token 对应 openid
+ order_source:
+ type: string
+ order_status:
+ type: string
+ order_source_id:
+ type: string
+ order_snap:
+ oneOf:
+ - type: object
+ additionalProperties: true
+ - type: array
+ items:
+ type: object
+ additionalProperties: true
+ order_amount:
+ type:
+ - number
+ - integer
+ order_remark:
+ type: string
+
+PocketBaseOrderListResponse:
+ type: object
+ required:
+ - page
+ - perPage
+ - totalItems
+ - totalPages
+ - items
+ properties:
+ page:
+ type:
+ - integer
+ - string
+ perPage:
+ type:
+ - integer
+ - string
+ totalItems:
+ type:
+ - integer
+ - string
+ totalPages:
+ type:
+ - integer
+ - string
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseOrderRecord'
diff --git a/pocket-base/spec/openapi-wx/schemas/products.yaml b/pocket-base/spec/openapi-wx/schemas/products.yaml
new file mode 100644
index 0000000..e842800
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/products.yaml
@@ -0,0 +1,203 @@
+PocketBaseProductListFields:
+ type: object
+ properties:
+ prod_list_id:
+ type: string
+ description: 产品列表业务 ID,唯一标识
+ example: <产品列表业务ID>|
+ prod_list_name:
+ type: string
+ description: 产品名称
+ example: <产品名称>|
+ prod_list_modelnumber:
+ type: string
+ description: 产品型号
+ example: <产品型号>|
+ prod_list_icon:
+ type: string
+ description: 产品图标附件 ID,保存 `tbl_attachments.attachments_id`
+ example: <产品图标附件ID>|
+ prod_list_description:
+ type: string
+ description: 产品说明
+ example: <产品说明>|
+ prod_list_feature:
+ type: string
+ description: 产品特色
+ example: <产品特色>|
+ prod_list_parameters:
+ type: array
+ description: 产品参数数组,每项包含 name/value
+ items:
+ type: object
+ properties:
+ name:
+ type: string
+ example: <属性名>|
+ value:
+ type: string
+ example: <属性值>|
+ example:
+ - name: <属性名>|
+ value: <属性值>|
+ prod_list_plantype:
+ type: string
+ description: 产品方案
+ example: <产品方案>|
+ prod_list_category:
+ type: string
+ description: 产品分类(必填,单选)
+ example: <产品分类>|
+ prod_list_sort:
+ type:
+ - number
+ - integer
+ description: 排序值(同分类内按升序)
+ example: 10
+ prod_list_comm_type:
+ type: string
+ description: 通讯类型
+ example: <通讯类型>|
+ prod_list_series:
+ type: string
+ description: 产品系列
+ example: <产品系列>|
+ prod_list_power_supply:
+ type: string
+ description: 供电方式
+ example: <供电方式>|
+ prod_list_tags:
+ type: string
+ description: 产品标签(辅助检索,以 `|` 聚合)
+ example: <产品标签>|
+ prod_list_status:
+ type: string
+ description: 产品状态(有效 / 过期 / 主推等)
+ example: <产品状态>|
+ prod_list_basic_price:
+ type:
+ - number
+ - integer
+ description: 基础价格
+ example: 1999
+ prod_list_vip_price:
+ type: array
+ description: 会员价数组,每项包含会员等级枚举值与价格
+ items:
+ type: object
+ properties:
+ viplevel:
+ type: string
+ example: <会员等级枚举值>|
+ price:
+ type:
+ - number
+ - integer
+ example: 1899
+ example:
+ - viplevel: VIP1
+ price: 1899
+ prod_list_remark:
+ type: string
+ description: 备注
+ example: <备注>|
+
+PocketBaseProductListRecord:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseRecordBase'
+ - $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseProductListFields'
+ example:
+ id: |
+ collectionId: <集合ID>|
+ collectionName: <集合名称>|
+ created: <记录创建时间>|
+ updated: <记录更新时间>|
+ prod_list_id: <产品列表业务ID>|
+ prod_list_name: <产品名称>|
+ prod_list_modelnumber: <产品型号>|
+ prod_list_icon: <产品图标附件ID>|
+ prod_list_description: <产品说明>|
+ prod_list_feature: <产品特色>|
+ prod_list_parameters:
+ - name: <属性名>|
+ value: <属性值>|
+ prod_list_plantype: <产品方案>|
+ prod_list_category: <产品分类>|
+ prod_list_sort: 10
+ prod_list_comm_type: <通讯类型>|
+ prod_list_series: <产品系列>|
+ prod_list_power_supply: <供电方式>|
+ prod_list_tags: <产品标签>|
+ prod_list_status: <产品状态>|
+ prod_list_basic_price: 1999
+ prod_list_vip_price:
+ - viplevel: VIP1
+ price: 1899
+ prod_list_remark: <备注>|
+
+PocketBaseProductListListResponse:
+ type: object
+ required:
+ - page
+ - perPage
+ - totalItems
+ - totalPages
+ - items
+ properties:
+ page:
+ type:
+ - integer
+ - string
+ example: <页码>|
+ perPage:
+ type:
+ - integer
+ - string
+ example: <每页条数>|
+ totalItems:
+ type:
+ - integer
+ - string
+ example: <总记录数>|
+ totalPages:
+ type:
+ - integer
+ - string
+ example: <总页数>|
+ items:
+ type: array
+ items:
+ $ref: '../../openapi-wx.yaml#/components/schemas/PocketBaseProductListRecord'
+ example:
+ page: <页码>|
+ perPage: <每页条数>|
+ totalItems: <总记录数>|
+ totalPages: <总页数>|
+ items:
+ - id: |
+ collectionId: <集合ID>|
+ collectionName: <集合名称>|
+ created: <记录创建时间>|
+ updated: <记录更新时间>|
+ prod_list_id: <产品列表业务ID>|
+ prod_list_name: <产品名称>|
+ prod_list_modelnumber: <产品型号>|
+ prod_list_icon: <产品图标附件ID>|
+ prod_list_description: <产品说明>|
+ prod_list_feature: <产品特色>|
+ prod_list_parameters:
+ - name: <属性名>|
+ value: <属性值>|
+ prod_list_plantype: <产品方案>|
+ prod_list_category: <产品分类>|
+ prod_list_sort: 10
+ prod_list_comm_type: <通讯类型>|
+ prod_list_series: <产品系列>|
+ prod_list_power_supply: <供电方式>|
+ prod_list_tags: <产品标签>|
+ prod_list_status: <产品状态>|
+ prod_list_basic_price: 1999
+ prod_list_vip_price:
+ - viplevel: VIP1
+ price: 1899
+ prod_list_remark: <备注>|
diff --git a/pocket-base/spec/openapi-wx/schemas/system.yaml b/pocket-base/spec/openapi-wx/schemas/system.yaml
new file mode 100644
index 0000000..ebeeb55
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/system.yaml
@@ -0,0 +1,55 @@
+SystemRefreshTokenRequest:
+ type: object
+ properties:
+ users_wx_code:
+ type:
+ - string
+ - 'null'
+ description: |
+ 可选。
+ 当前 token 失效时,可通过该 code 重新签发 token。
+ example: 0a1b2c3d4e5f6g
+
+RefreshTokenResponse:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/ApiResponseBase'
+ - type: object
+ required:
+ - token
+ properties:
+ data:
+ type: object
+ additionalProperties: true
+ description: "业务响应数据"
+ example: {}
+ token:
+ type: string
+ description: 新签发的 PocketBase 原生 auth token
+ example:
+ statusCode: 业务状态码 | integer
+ errMsg: 刷新成功 | string
+ data: {}
+ token: 新签发的PocketBase原生auth token | string
+
+UsersCountData:
+ type: object
+ properties:
+ total_users:
+ type:
+ - integer
+ - string
+ example: 用户总数 | integer
+
+UsersCountResponse:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/ApiResponseBase'
+ - type: object
+ properties:
+ data:
+ description: "业务响应数据"
+ $ref: '../../openapi-wx.yaml#/components/schemas/UsersCountData'
+ example:
+ statusCode: 业务状态码 | integer
+ errMsg: 业务提示信息 | string
+ data:
+ total_users: 用户总数 | integer
diff --git a/pocket-base/spec/openapi-wx/schemas/wechat-auth.yaml b/pocket-base/spec/openapi-wx/schemas/wechat-auth.yaml
new file mode 100644
index 0000000..d6bb510
--- /dev/null
+++ b/pocket-base/spec/openapi-wx/schemas/wechat-auth.yaml
@@ -0,0 +1,389 @@
+UserInfo:
+ type: object
+ properties:
+ pb_id:
+ description: "PocketBase 记录主键 id"
+ type: string
+ users_convers_id:
+ description: "会话侧用户 ID"
+ type: string
+ users_id:
+ description: "用户业务 ID"
+ type: string
+ users_idtype:
+ description: "身份来源类型或证件类型"
+ anyOf:
+ - type: string
+ enum:
+ - WeChat
+ - ManagePlatform
+ - type: string
+ users_id_number:
+ description: "证件号"
+ type: string
+ users_type:
+ description: "用户类型"
+ anyOf:
+ - type: string
+ enum:
+ - 游客
+ - 注册用户
+ - type: string
+ users_name:
+ description: "用户姓名 / 昵称"
+ type: string
+ users_status:
+ description: "用户状态"
+ type:
+ - string
+ - number
+ users_rank_level:
+ description: "用户星级数值"
+ type:
+ - number
+ - integer
+ - string
+ users_auth_type:
+ description: "账户类型"
+ type:
+ - number
+ - integer
+ - string
+ users_phone:
+ description: "手机号"
+ type: string
+ users_phone_masked:
+ type: string
+ users_level:
+ type: string
+ description: 用户等级
+ users_level_name:
+ type: string
+ description: 用户等级名称,按 `users_level -> 数据-会员等级` 字典描述实时解析
+ users_tag:
+ type: string
+ description: 用户标签
+ users_picture:
+ type: string
+ description: 用户头像附件的 `attachments_id`
+ users_picture_url:
+ type: string
+ description: 根据 `users_picture -> tbl_attachments` 自动解析出的头像文件流链接
+ users_id_pic_a:
+ type: string
+ description: 证件正面附件的 `attachments_id`
+ users_id_pic_a_url:
+ type: string
+ description: 根据 `users_id_pic_a -> tbl_attachments` 自动解析出的文件流链接
+ users_id_pic_b:
+ type: string
+ description: 证件反面附件的 `attachments_id`
+ users_id_pic_b_url:
+ type: string
+ description: 根据 `users_id_pic_b -> tbl_attachments` 自动解析出的文件流链接
+ users_title_picture:
+ type: string
+ description: 资质附件的 `attachments_id`
+ users_title_picture_url:
+ type: string
+ description: 根据 `users_title_picture -> tbl_attachments` 自动解析出的文件流链接
+ openid:
+ type: string
+ description: 全平台统一身份标识
+ company_id:
+ type: string
+ description: 公司业务 id,存储 `tbl_company.company_id`
+ users_parent_id:
+ type: string
+ description: 上级用户业务 id
+ users_promo_code:
+ type: string
+ description: 推广码
+ usergroups_id:
+ type: string
+ description: 用户组业务 id
+ company:
+ $ref: '../../openapi-wx.yaml#/components/schemas/CompanyInfo'
+ created:
+ type: string
+ description: 用户创建时间
+ updated:
+ type: string
+ description: 用户更新时间
+ example:
+ pb_id: PocketBase记录主键id | string
+ users_convers_id: 会话侧用户ID | string
+ users_id: 用户业务ID | string
+ users_idtype: 用户身份来源类型 | string
+ users_id_number: 证件号 | string
+ users_type: 用户类型 | string
+ users_name: 用户姓名或昵称 | string
+ users_status: 用户状态 | string
+ users_rank_level: 用户星级数值 | number
+ users_auth_type: 账户类型 | number
+ users_phone: 手机号 | string
+ users_phone_masked: 手机号脱敏值 | string
+ users_level: 用户等级 | string
+ users_level_name: 用户等级名称 | string
+ users_tag: 用户标签 | string
+ users_picture: 用户头像附件ID | string
+ users_picture_url: 用户头像文件流链接 | string
+ users_id_pic_a: 证件正面附件ID | string
+ users_id_pic_a_url: 证件正面文件流链接 | string
+ users_id_pic_b: 证件反面附件ID | string
+ users_id_pic_b_url: 证件反面文件流链接 | string
+ users_title_picture: 资质附件ID | string
+ users_title_picture_url: 资质附件文件流链接 | string
+ openid: 全平台统一身份标识 | string
+ company_id: 公司业务id | string
+ users_parent_id: 上级用户业务id | string
+ users_promo_code: 推广码 | string
+ usergroups_id: 用户组业务id | string
+ company:
+ pb_id: PocketBase记录主键id | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ created: 用户创建时间 | string
+ updated: 用户更新时间 | string
+
+WechatLoginRequest:
+ type: object
+ required:
+ - users_wx_code
+ properties:
+ users_wx_code:
+ type: string
+ description: 微信小程序登录临时凭证 code
+ example: 0a1b2c3d4e5f6g
+
+WechatProfileRequest:
+ type: object
+ properties:
+ users_name:
+ type: string
+ description: "用户姓名 / 昵称"
+ example: 张三
+ users_phone_code:
+ type: string
+ description: 可选。若传入,服务端优先通过微信接口换取真实手机号并写入数据库
+ example: 2b7d9f2e3c4a5b6d7e8f
+ users_phone:
+ type: string
+ description: 可选。未传 `users_phone_code` 时,可直接写入手机号
+ example: '13800138000'
+ users_type:
+ type: string
+ description: 可选。用户类型;仅在传入非空值时更新
+ example: 服务商
+ company_id:
+ type: string
+ description: 可选。公司业务 id;仅在传入非空值时更新
+ example: WX-COMPANY-10001
+ users_tag:
+ type: string
+ description: 可选。用户标签;非空时才更新
+ example: 核心客户
+ users_picture:
+ type: string
+ description: 可选。用户头像附件的 `attachments_id`
+ example: ATT-1743123456789-abc123
+ users_id_pic_a:
+ type: string
+ description: 可选。证件正面附件的 `attachments_id`
+ users_id_pic_b:
+ type: string
+ description: 可选。证件反面附件的 `attachments_id`
+ users_title_picture:
+ type: string
+ description: 可选。资质附件的 `attachments_id`
+
+AuthSuccessData:
+ type: object
+ properties:
+ status:
+ anyOf:
+ - type: string
+ enum:
+ - register_success
+ - login_success
+ - type: string
+ is_info_complete:
+ type:
+ - boolean
+ - string
+ user:
+ $ref: '../../openapi-wx.yaml#/components/schemas/UserInfo'
+
+AuthSuccessResponse:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/ApiResponseBase'
+ - type: object
+ required:
+ - token
+ properties:
+ data:
+ description: "业务响应数据"
+ $ref: '../../openapi-wx.yaml#/components/schemas/AuthSuccessData'
+ token:
+ type: string
+ description: PocketBase 原生 auth token
+ example:
+ statusCode: 业务状态码 | integer
+ errMsg: 业务提示信息 | string
+ data:
+ status: 登录或注册状态 | string
+ is_info_complete: 资料是否完整 | boolean
+ user:
+ pb_id: PocketBase记录主键id | string
+ users_convers_id: 会话侧用户ID | string
+ users_id: 用户业务ID | string
+ users_idtype: 用户身份来源类型 | string
+ users_id_number: 证件号 | string
+ users_type: 用户类型 | string
+ users_name: 用户姓名或昵称 | string
+ users_status: 用户状态 | string
+ users_rank_level: 用户星级数值 | number
+ users_auth_type: 账户类型 | number
+ users_phone: 手机号 | string
+ users_phone_masked: 手机号脱敏值 | string
+ users_level: 用户等级 | string
+ users_level_name: 用户等级名称 | string
+ users_tag: 用户标签 | string
+ users_picture: 用户头像附件ID | string
+ users_picture_url: 用户头像文件流链接 | string
+ users_id_pic_a: 证件正面附件ID | string
+ users_id_pic_a_url: 证件正面文件流链接 | string
+ users_id_pic_b: 证件反面附件ID | string
+ users_id_pic_b_url: 证件反面文件流链接 | string
+ users_title_picture: 资质附件ID | string
+ users_title_picture_url: 资质附件文件流链接 | string
+ openid: 全平台统一身份标识 | string
+ company_id: 公司业务id | string
+ users_parent_id: 上级用户业务id | string
+ users_promo_code: 推广码 | string
+ usergroups_id: 用户组业务id | string
+ company:
+ pb_id: PocketBase记录主键id | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ created: 用户创建时间 | string
+ updated: 用户更新时间 | string
+ token: PocketBase原生认证token | string
+
+WechatProfileResponseData:
+ type: object
+ properties:
+ status:
+ anyOf:
+ - type: string
+ enum:
+ - update_success
+ - type: string
+ user:
+ $ref: '../../openapi-wx.yaml#/components/schemas/UserInfo'
+
+WechatProfileResponse:
+ allOf:
+ - $ref: '../../openapi-wx.yaml#/components/schemas/ApiResponseBase'
+ - type: object
+ properties:
+ data:
+ description: "业务响应数据"
+ $ref: '../../openapi-wx.yaml#/components/schemas/WechatProfileResponseData'
+ example:
+ statusCode: 业务状态码 | integer
+ errMsg: 业务提示信息 | string
+ data:
+ status: 资料更新状态 | string
+ user:
+ pb_id: PocketBase记录主键id | string
+ users_convers_id: 会话侧用户ID | string
+ users_id: 用户业务ID | string
+ users_idtype: 用户身份来源类型 | string
+ users_id_number: 证件号 | string
+ users_type: 用户类型 | string
+ users_name: 用户姓名或昵称 | string
+ users_status: 用户状态 | string
+ users_rank_level: 用户星级数值 | number
+ users_auth_type: 账户类型 | number
+ users_phone: 手机号 | string
+ users_phone_masked: 手机号脱敏值 | string
+ users_level: 用户等级 | string
+ users_level_name: 用户等级名称 | string
+ users_tag: 用户标签 | string
+ users_picture: 用户头像附件ID | string
+ users_picture_url: 用户头像文件流链接 | string
+ users_id_pic_a: 证件正面附件ID | string
+ users_id_pic_a_url: 证件正面文件流链接 | string
+ users_id_pic_b: 证件反面附件ID | string
+ users_id_pic_b_url: 证件反面文件流链接 | string
+ users_title_picture: 资质附件ID | string
+ users_title_picture_url: 资质附件文件流链接 | string
+ openid: 全平台统一身份标识 | string
+ company_id: 公司业务id | string
+ users_parent_id: 上级用户业务id | string
+ users_promo_code: 推广码 | string
+ usergroups_id: 用户组业务id | string
+ company:
+ pb_id: PocketBase记录主键id | string
+ company_id: 公司业务id,由数据库自动生成 | string
+ company_name: 公司名称 | string
+ company_type: 公司类型 | string
+ company_entity: 公司法人 | string
+ company_usci: 统一社会信用代码 | string
+ company_nationality: 国家名称 | string
+ company_nationality_code: 国家编码 | string
+ company_province: 省份名称 | string
+ company_province_code: 省份编码 | string
+ company_city: 城市名称 | string
+ company_city_code: 城市编码 | string
+ company_district: 区县名称 | string
+ company_district_code: 区县编码 | string
+ company_postalcode: 邮政编码 | string
+ company_add: 公司地址 | string
+ company_status: 公司状态 | string
+ company_level: 公司等级 | string
+ company_owner_openid: 公司所有者openid | string
+ company_remark: 备注 | string
+ created: 记录创建时间 | string
+ updated: 记录更新时间 | string
+ created: 用户创建时间 | string
+ updated: 用户更新时间 | string
diff --git a/script/add-product-function-field.js b/script/add-product-function-field.js
index c7637e3..b6fa445 100644
--- a/script/add-product-function-field.js
+++ b/script/add-product-function-field.js
@@ -89,7 +89,9 @@ async function run() {
const targetFieldType = 'json';
const existingField = (collection.fields || []).find((field) => field.name === targetFieldName);
- if (existingField && existingField.type === targetFieldType) {
+ const hasBrokenCustomIdField = (collection.fields || []).some((field) => field && field.name === 'id');
+
+ if (existingField && existingField.type === targetFieldType && !hasBrokenCustomIdField) {
console.log('✅ 字段已存在且类型正确,无需变更。');
console.log('✅ 校验完成: tbl_product_list.prod_list_function (json)');
return;
@@ -100,6 +102,10 @@ async function run() {
for (let i = 0; i < (collection.fields || []).length; i += 1) {
const field = collection.fields[i];
+ if (field && field.name === 'id') {
+ // PocketBase system id is implicit; custom required id field will break record creation.
+ continue;
+ }
if (field.name === targetFieldName) {
nextFields.push(normalizeFieldPayload(field, { name: targetFieldName, type: targetFieldType }));
patched = true;
diff --git a/script/package.json b/script/package.json
index 7f3796a..c7020eb 100644
--- a/script/package.json
+++ b/script/package.json
@@ -10,6 +10,9 @@
"init:product-list": "node pocketbase.product-list.js",
"init:dictionary": "node pocketbase.dictionary.js",
"migrate:file-fields": "node pocketbase.file-fields-to-attachments.js",
+ "migrate:add-is-delete-field": "node pocketbase.add-is-delete-field.js",
+ "migrate:apply-soft-delete-rules": "node pocketbase.apply-soft-delete-rules.js",
+ "migrate:ensure-cart-order-autogen-id": "node pocketbase.ensure-cart-order-autogen-id.js",
"migrate:product-params-array": "node migrate-product-parameters-to-array.js",
"migrate:add-product-function-field": "node add-product-function-field.js",
"test:company-native-api": "node test-tbl-company-native-api.js",
diff --git a/script/pocketbase.add-is-delete-field.js b/script/pocketbase.add-is-delete-field.js
new file mode 100644
index 0000000..febd44b
--- /dev/null
+++ b/script/pocketbase.add-is-delete-field.js
@@ -0,0 +1,233 @@
+import { createRequire } from 'module';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import PocketBase from 'pocketbase';
+
+const require = createRequire(import.meta.url);
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+let runtimeConfig = {};
+try {
+ runtimeConfig = require('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js');
+} catch (_error) {
+ runtimeConfig = {};
+}
+
+function readEnvFile(filePath) {
+ if (!fs.existsSync(filePath)) return {};
+
+ const content = fs.readFileSync(filePath, 'utf8');
+ const result = {};
+
+ for (const rawLine of content.split(/\r?\n/)) {
+ const line = rawLine.trim();
+ if (!line || line.startsWith('#')) continue;
+
+ const index = line.indexOf('=');
+ if (index === -1) continue;
+
+ const key = line.slice(0, index).trim();
+ const value = line.slice(index + 1).trim();
+ result[key] = value;
+ }
+
+ return result;
+}
+
+const backendEnv = readEnvFile(path.resolve(__dirname, '..', 'back-end', '.env'));
+const PB_URL = String(
+ process.env.PB_URL
+ || backendEnv.POCKETBASE_API_URL
+ || runtimeConfig.POCKETBASE_API_URL
+ || 'http://127.0.0.1:8090'
+).replace(/\/+$/, '');
+const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
+
+if (!AUTH_TOKEN) {
+ console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行 is_delete 字段迁移。');
+ process.exit(1);
+}
+
+const pb = new PocketBase(PB_URL);
+const TARGET_FIELD = {
+ name: 'is_delete',
+ type: 'number',
+ required: false,
+ presentable: true,
+ hidden: false,
+ default: 0,
+ min: 0,
+ max: 1,
+ onlyInt: true,
+};
+
+function isSystemCollection(collection) {
+ return !!(collection && (collection.system || String(collection.name || '').startsWith('_')));
+}
+
+function normalizeFieldPayload(field, targetSpec) {
+ const payload = field ? Object.assign({}, field) : {};
+ const next = targetSpec || field || {};
+
+ if (field && field.id) {
+ payload.id = field.id;
+ }
+
+ payload.name = next.name;
+ payload.type = next.type;
+
+ if (typeof next.required !== 'undefined') {
+ payload.required = !!next.required;
+ }
+ if (typeof next.presentable !== 'undefined') {
+ payload.presentable = !!next.presentable;
+ }
+ if (typeof next.hidden !== 'undefined') {
+ payload.hidden = !!next.hidden;
+ }
+
+ if (next.type === 'number') {
+ if (Object.prototype.hasOwnProperty.call(next, 'default')) payload.default = next.default;
+ if (Object.prototype.hasOwnProperty.call(next, 'min')) payload.min = next.min;
+ if (Object.prototype.hasOwnProperty.call(next, 'max')) payload.max = next.max;
+ if (Object.prototype.hasOwnProperty.call(next, 'onlyInt')) payload.onlyInt = !!next.onlyInt;
+ }
+
+ if (next.type === 'autodate') {
+ payload.onCreate = typeof next.onCreate === 'boolean' ? next.onCreate : (typeof field?.onCreate === 'boolean' ? field.onCreate : true);
+ payload.onUpdate = typeof next.onUpdate === 'boolean' ? next.onUpdate : (typeof field?.onUpdate === 'boolean' ? field.onUpdate : false);
+ }
+
+ if (next.type === 'file') {
+ payload.maxSelect = typeof next.maxSelect === 'number' ? next.maxSelect : (typeof field?.maxSelect === 'number' ? field.maxSelect : 0);
+ payload.maxSize = typeof next.maxSize === 'number' ? next.maxSize : (typeof field?.maxSize === 'number' ? field.maxSize : 0);
+ payload.mimeTypes = Array.isArray(next.mimeTypes)
+ ? next.mimeTypes
+ : (Array.isArray(field?.mimeTypes) ? field.mimeTypes : null);
+ }
+
+ return payload;
+}
+
+function buildCollectionPayload(collection, fields) {
+ return {
+ name: collection.name,
+ type: collection.type,
+ listRule: collection.listRule,
+ viewRule: collection.viewRule,
+ createRule: collection.createRule,
+ updateRule: collection.updateRule,
+ deleteRule: collection.deleteRule,
+ fields,
+ indexes: collection.indexes || [],
+ };
+}
+
+async function addFieldToCollections() {
+ const collections = await pb.collections.getFullList({ sort: 'name' });
+ const changed = [];
+
+ for (const collection of collections) {
+ if (isSystemCollection(collection)) continue;
+
+ const currentFields = Array.isArray(collection.fields) ? collection.fields : [];
+ const existingField = currentFields.find((field) => field && field.name === TARGET_FIELD.name);
+ const nextFields = currentFields
+ .filter((field) => field && field.name !== 'id')
+ .map((field) => normalizeFieldPayload(field));
+
+ if (existingField) {
+ for (let i = 0; i < nextFields.length; i += 1) {
+ if (nextFields[i].name === TARGET_FIELD.name) {
+ nextFields[i] = normalizeFieldPayload(existingField, TARGET_FIELD);
+ break;
+ }
+ }
+ } else {
+ nextFields.push(normalizeFieldPayload(null, TARGET_FIELD));
+ }
+
+ await pb.collections.update(collection.id, buildCollectionPayload(collection, nextFields));
+ changed.push({ name: collection.name, action: existingField ? 'normalized' : 'added' });
+ }
+
+ return changed;
+}
+
+async function backfillRecords() {
+ const collections = await pb.collections.getFullList({ sort: 'name' });
+ const summary = [];
+
+ for (const collection of collections) {
+ if (isSystemCollection(collection)) continue;
+
+ let page = 1;
+ let patched = 0;
+ const perPage = 200;
+
+ while (true) {
+ const list = await pb.collection(collection.name).getList(page, perPage, {
+ fields: 'id,is_delete',
+ skipTotal: false,
+ });
+
+ const items = Array.isArray(list.items) ? list.items : [];
+ for (const item of items) {
+ const value = item && Object.prototype.hasOwnProperty.call(item, 'is_delete') ? item.is_delete : null;
+ if (value === 0 || value === '0') continue;
+
+ await pb.collection(collection.name).update(item.id, { is_delete: 0 });
+ patched += 1;
+ }
+
+ if (page >= list.totalPages) break;
+ page += 1;
+ }
+
+ summary.push({ name: collection.name, backfilledRecords: patched });
+ }
+
+ return summary;
+}
+
+async function verifyCollections() {
+ const collections = await pb.collections.getFullList({ sort: 'name' });
+ const failed = [];
+
+ for (const collection of collections) {
+ if (isSystemCollection(collection)) continue;
+ const field = (collection.fields || []).find((item) => item && item.name === TARGET_FIELD.name);
+ if (!field || field.type !== 'number') failed.push(collection.name);
+ }
+
+ if (failed.length) {
+ throw new Error('以下集合缺少 is_delete 字段或类型不正确: ' + failed.join(', '));
+ }
+}
+
+async function main() {
+ try {
+ console.log(`🔄 正在连接 PocketBase: ${PB_URL}`);
+ pb.authStore.save(AUTH_TOKEN, null);
+ console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。');
+
+ const changed = await addFieldToCollections();
+ console.log('📝 已更新集合:');
+ console.log(JSON.stringify(changed, null, 2));
+
+ const backfilled = await backfillRecords();
+ console.log('📝 已回填记录:');
+ console.log(JSON.stringify(backfilled, null, 2));
+
+ await verifyCollections();
+ console.log('✅ 校验通过:所有业务集合已包含 is_delete(number, default=0) 字段。');
+ console.log('🎉 is_delete 字段迁移完成!');
+ } catch (error) {
+ console.error('❌ is_delete 字段迁移失败:', error.response?.data || error.message || error);
+ process.exitCode = 1;
+ }
+}
+
+main();
diff --git a/script/pocketbase.apply-soft-delete-rules.js b/script/pocketbase.apply-soft-delete-rules.js
new file mode 100644
index 0000000..353e01b
--- /dev/null
+++ b/script/pocketbase.apply-soft-delete-rules.js
@@ -0,0 +1,176 @@
+import { createRequire } from 'module';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import PocketBase from 'pocketbase';
+
+const require = createRequire(import.meta.url);
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+let runtimeConfig = {};
+try {
+ runtimeConfig = require('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js');
+} catch (_error) {
+ runtimeConfig = {};
+}
+
+function readEnvFile(filePath) {
+ if (!fs.existsSync(filePath)) return {};
+
+ const content = fs.readFileSync(filePath, 'utf8');
+ const result = {};
+
+ for (const rawLine of content.split(/\r?\n/)) {
+ const line = rawLine.trim();
+ if (!line || line.startsWith('#')) continue;
+
+ const index = line.indexOf('=');
+ if (index === -1) continue;
+
+ const key = line.slice(0, index).trim();
+ const value = line.slice(index + 1).trim();
+ result[key] = value;
+ }
+
+ return result;
+}
+
+const backendEnv = readEnvFile(path.resolve(__dirname, '..', 'back-end', '.env'));
+const PB_URL = String(
+ process.env.PB_URL
+ || backendEnv.POCKETBASE_API_URL
+ || runtimeConfig.POCKETBASE_API_URL
+ || 'http://127.0.0.1:8090'
+).replace(/\/+$/, '');
+const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
+const SOFT_DELETE_RULE = 'is_delete = 0';
+
+if (!AUTH_TOKEN) {
+ console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行软删除规则迁移。');
+ process.exit(1);
+}
+
+const pb = new PocketBase(PB_URL);
+
+function isSystemCollection(collection) {
+ return !!(collection && (collection.system || String(collection.name || '').startsWith('_')));
+}
+
+function normalizeFieldPayload(field) {
+ const payload = Object.assign({}, field);
+
+ if (field.type === 'number') {
+ if (Object.prototype.hasOwnProperty.call(field, 'default')) payload.default = field.default;
+ if (Object.prototype.hasOwnProperty.call(field, 'min')) payload.min = field.min;
+ if (Object.prototype.hasOwnProperty.call(field, 'max')) payload.max = field.max;
+ if (Object.prototype.hasOwnProperty.call(field, 'onlyInt')) payload.onlyInt = !!field.onlyInt;
+ }
+
+ if (field.type === 'autodate') {
+ payload.onCreate = typeof field.onCreate === 'boolean' ? field.onCreate : true;
+ payload.onUpdate = typeof field.onUpdate === 'boolean' ? field.onUpdate : false;
+ }
+
+ if (field.type === 'file') {
+ payload.maxSelect = typeof field.maxSelect === 'number' ? field.maxSelect : 0;
+ payload.maxSize = typeof field.maxSize === 'number' ? field.maxSize : 0;
+ payload.mimeTypes = Array.isArray(field.mimeTypes) ? field.mimeTypes : null;
+ }
+
+ return payload;
+}
+
+function mergeRuleWithSoftDelete(rule) {
+ const currentRule = typeof rule === 'string' ? rule.trim() : '';
+
+ if (!currentRule) return SOFT_DELETE_RULE;
+ if (currentRule === SOFT_DELETE_RULE) return currentRule;
+ if (currentRule.includes(SOFT_DELETE_RULE)) return currentRule;
+
+ return `(${currentRule}) && ${SOFT_DELETE_RULE}`;
+}
+
+function buildCollectionPayload(collection) {
+ return {
+ name: collection.name,
+ type: collection.type,
+ listRule: mergeRuleWithSoftDelete(collection.listRule),
+ viewRule: mergeRuleWithSoftDelete(collection.viewRule),
+ createRule: collection.createRule,
+ updateRule: collection.updateRule,
+ deleteRule: collection.deleteRule,
+ fields: (collection.fields || []).filter((field) => field && field.name !== 'id').map((field) => normalizeFieldPayload(field)),
+ indexes: collection.indexes || [],
+ };
+}
+
+async function applyRules() {
+ const collections = await pb.collections.getFullList({ sort: 'name' });
+ const changed = [];
+
+ for (const collection of collections) {
+ if (isSystemCollection(collection)) continue;
+
+ const hasSoftDelete = (collection.fields || []).some((field) => field && field.name === 'is_delete');
+ if (!hasSoftDelete) continue;
+
+ const nextListRule = mergeRuleWithSoftDelete(collection.listRule);
+ const nextViewRule = mergeRuleWithSoftDelete(collection.viewRule);
+ const listChanged = nextListRule !== (collection.listRule || '');
+ const viewChanged = nextViewRule !== (collection.viewRule || '');
+
+ if (!listChanged && !viewChanged) {
+ changed.push({ name: collection.name, action: 'skipped', listRule: nextListRule, viewRule: nextViewRule });
+ continue;
+ }
+
+ await pb.collections.update(collection.id, buildCollectionPayload(collection));
+ changed.push({ name: collection.name, action: 'updated', listRule: nextListRule, viewRule: nextViewRule });
+ }
+
+ return changed;
+}
+
+async function verifyRules() {
+ const collections = await pb.collections.getFullList({ sort: 'name' });
+ const failed = [];
+
+ for (const collection of collections) {
+ if (isSystemCollection(collection)) continue;
+ const hasSoftDelete = (collection.fields || []).some((field) => field && field.name === 'is_delete');
+ if (!hasSoftDelete) continue;
+
+ const listRule = String(collection.listRule || '');
+ const viewRule = String(collection.viewRule || '');
+
+ if (!listRule.includes(SOFT_DELETE_RULE) || !viewRule.includes(SOFT_DELETE_RULE)) {
+ failed.push({ name: collection.name, listRule, viewRule });
+ }
+ }
+
+ if (failed.length) {
+ throw new Error(`以下集合未正确应用软删除规则: ${JSON.stringify(failed, null, 2)}`);
+ }
+}
+
+async function main() {
+ try {
+ console.log(`🔄 正在连接 PocketBase: ${PB_URL}`);
+ pb.authStore.save(AUTH_TOKEN, null);
+ console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。');
+
+ const changed = await applyRules();
+ console.log('📝 规则更新结果:');
+ console.log(JSON.stringify(changed, null, 2));
+
+ await verifyRules();
+ console.log('✅ 校验通过:所有带 is_delete 的业务集合已默认过滤 is_delete = 0。');
+ console.log('🎉 软删除默认查询规则迁移完成!');
+ } catch (error) {
+ console.error('❌ 软删除默认查询规则迁移失败:', error.response?.data || error.message || error);
+ process.exitCode = 1;
+ }
+}
+
+main();
\ No newline at end of file
diff --git a/script/pocketbase.cart-order.js b/script/pocketbase.cart-order.js
index 34eb88d..6a9f961 100644
--- a/script/pocketbase.cart-order.js
+++ b/script/pocketbase.cart-order.js
@@ -15,6 +15,7 @@ const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE
const OWNER_AUTH_RULE = '@request.auth.id != ""';
const CART_OWNER_MATCH_RULE = 'cart_owner = @request.auth.openid';
const ORDER_OWNER_MATCH_RULE = 'order_owner = @request.auth.openid';
+const SOFT_DELETE_RULE = 'is_delete = 0';
if (!AUTH_TOKEN) {
console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行建表。');
@@ -27,13 +28,13 @@ const collections = [
{
name: 'tbl_cart',
type: 'base',
- listRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
- viewRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
+ listRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE} && ${SOFT_DELETE_RULE}`,
+ viewRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE} && ${SOFT_DELETE_RULE}`,
createRule: `${OWNER_AUTH_RULE} && @request.body.cart_owner = @request.auth.openid`,
updateRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
deleteRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
fields: [
- { name: 'cart_id', type: 'text', required: true },
+ { name: 'cart_id', type: 'text', required: true, autogeneratePattern: 'CART-[0-9]{13}-[A-Za-z0-9]{6}' },
{ name: 'cart_number', type: 'text', required: true },
{ name: 'cart_create', type: 'autodate', onCreate: true, onUpdate: false },
{ name: 'cart_owner', type: 'text', required: true },
@@ -42,6 +43,7 @@ const collections = [
{ name: 'cart_status', type: 'text', required: true },
{ name: 'cart_at_price', type: 'number', required: true },
{ name: 'cart_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_cart_cart_id ON tbl_cart (cart_id)',
@@ -57,13 +59,13 @@ const collections = [
{
name: 'tbl_order',
type: 'base',
- listRule: `${OWNER_AUTH_RULE} && ${ORDER_OWNER_MATCH_RULE}`,
- viewRule: `${OWNER_AUTH_RULE} && ${ORDER_OWNER_MATCH_RULE}`,
+ listRule: `${OWNER_AUTH_RULE} && ${ORDER_OWNER_MATCH_RULE} && ${SOFT_DELETE_RULE}`,
+ viewRule: `${OWNER_AUTH_RULE} && ${ORDER_OWNER_MATCH_RULE} && ${SOFT_DELETE_RULE}`,
createRule: `${OWNER_AUTH_RULE} && @request.body.order_owner = @request.auth.openid`,
updateRule: `${OWNER_AUTH_RULE} && ${ORDER_OWNER_MATCH_RULE}`,
deleteRule: `${OWNER_AUTH_RULE} && ${ORDER_OWNER_MATCH_RULE}`,
fields: [
- { name: 'order_id', type: 'text', required: true },
+ { name: 'order_id', type: 'text', required: true, autogeneratePattern: 'ORDER-[0-9]{13}-[A-Za-z0-9]{6}' },
{ name: 'order_number', type: 'text', required: true },
{ name: 'order_create', type: 'autodate', onCreate: true, onUpdate: false },
{ name: 'order_owner', type: 'text', required: true },
@@ -73,6 +75,7 @@ const collections = [
{ name: 'order_snap', type: 'json', required: true },
{ name: 'order_amount', type: 'number', required: true },
{ name: 'order_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_order_order_id ON tbl_order (order_id)',
diff --git a/script/pocketbase.dictionary.js b/script/pocketbase.dictionary.js
index 59b4ed2..870783a 100644
--- a/script/pocketbase.dictionary.js
+++ b/script/pocketbase.dictionary.js
@@ -12,6 +12,7 @@ try {
const PB_URL = (process.env.PB_URL || 'https://bai-api.blv-oa.com/pb').replace(/\/+$/, '');
const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
+const SOFT_DELETE_RULE = 'is_delete = 0';
if (!AUTH_TOKEN) {
console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行字典建表。');
@@ -23,8 +24,8 @@ const pb = new PocketBase(PB_URL);
const collectionData = {
name: 'tbl_system_dict',
type: 'base',
- listRule: '',
- viewRule: '',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
createRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
updateRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
deleteRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
@@ -38,6 +39,7 @@ const collectionData = {
{ name: 'dict_word_sort_order', type: 'text' },
{ name: 'dict_word_parent_id', type: 'text' },
{ name: 'dict_word_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_system_dict_id ON tbl_system_dict (system_dict_id)',
diff --git a/script/pocketbase.documents.js b/script/pocketbase.documents.js
index 65d28a1..a70f5f9 100644
--- a/script/pocketbase.documents.js
+++ b/script/pocketbase.documents.js
@@ -12,6 +12,7 @@ try {
const PB_URL = (process.env.PB_URL || 'https://bai-api.blv-oa.com/pb').replace(/\/+$/, '');
const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
+const SOFT_DELETE_RULE = 'is_delete = 0';
if (!AUTH_TOKEN) {
console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行建表。');
@@ -24,8 +25,8 @@ const collections = [
{
name: 'tbl_attachments',
type: 'base',
- listRule: '',
- viewRule: '',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'attachments_id', type: 'text', required: true },
{ name: 'attachments_link', type: 'file', maxSelect: 0, maxSize: 4294967296, mimeTypes: [] },
@@ -37,6 +38,7 @@ const collections = [
{ name: 'attachments_ocr', type: 'text' },
{ name: 'attachments_status', type: 'text' },
{ name: 'attachments_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_attachments_attachments_id ON tbl_attachments (attachments_id)',
@@ -47,8 +49,8 @@ const collections = [
{
name: 'tbl_document',
type: 'base',
- listRule: '',
- viewRule: '',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'document_id', type: 'text', required: true },
{ name: 'document_create', type: 'autodate', onCreate: true, onUpdate: false },
@@ -77,6 +79,7 @@ const collections = [
{ name: 'document_application_scenarios', type: 'text' },
{ name: 'document_hotel_type', type: 'text' },
{ name: 'document_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_document_document_id ON tbl_document (document_id)',
@@ -99,6 +102,7 @@ const collections = [
{ name: 'doh_user_id', type: 'text' },
{ name: 'doh_current_count', type: 'number' },
{ name: 'doh_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_document_operation_history_doh_id ON tbl_document_operation_history (doh_id)',
diff --git a/script/pocketbase.ensure-cart-order-autogen-id.js b/script/pocketbase.ensure-cart-order-autogen-id.js
new file mode 100644
index 0000000..a054ab1
--- /dev/null
+++ b/script/pocketbase.ensure-cart-order-autogen-id.js
@@ -0,0 +1,198 @@
+import { createRequire } from 'module';
+import fs from 'fs';
+import path from 'path';
+import { fileURLToPath } from 'url';
+import PocketBase from 'pocketbase';
+
+const require = createRequire(import.meta.url);
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+let runtimeConfig = {};
+try {
+ runtimeConfig = require('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js');
+} catch (_error) {
+ runtimeConfig = {};
+}
+
+function readEnvFile(filePath) {
+ if (!fs.existsSync(filePath)) return {};
+
+ const content = fs.readFileSync(filePath, 'utf8');
+ const result = {};
+
+ for (const rawLine of content.split(/\r?\n/)) {
+ const line = rawLine.trim();
+ if (!line || line.startsWith('#')) continue;
+
+ const index = line.indexOf('=');
+ if (index === -1) continue;
+
+ const key = line.slice(0, index).trim();
+ const value = line.slice(index + 1).trim();
+ result[key] = value;
+ }
+
+ return result;
+}
+
+const backendEnv = readEnvFile(path.resolve(__dirname, '..', 'back-end', '.env'));
+const PB_URL = String(
+ process.env.PB_URL
+ || backendEnv.POCKETBASE_API_URL
+ || runtimeConfig.POCKETBASE_API_URL
+ || 'http://127.0.0.1:8090'
+).replace(/\/+$/, '');
+const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
+
+if (!AUTH_TOKEN) {
+ console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行 cart/order 业务 ID 自动生成迁移。');
+ process.exit(1);
+}
+
+const pb = new PocketBase(PB_URL);
+
+const TARGETS = [
+ {
+ collectionName: 'tbl_cart',
+ fieldName: 'cart_id',
+ autogeneratePattern: 'CART-[0-9]{13}-[A-Za-z0-9]{6}',
+ },
+ {
+ collectionName: 'tbl_order',
+ fieldName: 'order_id',
+ autogeneratePattern: 'ORDER-[0-9]{13}-[A-Za-z0-9]{6}',
+ },
+];
+
+function normalizeFieldPayload(field, overrides) {
+ const payload = Object.assign({}, field, overrides || {});
+
+ if (payload.type === 'number') {
+ if (Object.prototype.hasOwnProperty.call(payload, 'onlyInt')) payload.onlyInt = !!payload.onlyInt;
+ }
+
+ if (payload.type === 'autodate') {
+ payload.onCreate = typeof payload.onCreate === 'boolean' ? payload.onCreate : true;
+ payload.onUpdate = typeof payload.onUpdate === 'boolean' ? payload.onUpdate : false;
+ }
+
+ if (payload.type === 'file') {
+ payload.maxSelect = typeof payload.maxSelect === 'number' ? payload.maxSelect : 0;
+ payload.maxSize = typeof payload.maxSize === 'number' ? payload.maxSize : 0;
+ payload.mimeTypes = Array.isArray(payload.mimeTypes) ? payload.mimeTypes : null;
+ }
+
+ return payload;
+}
+
+function buildCollectionPayload(collection, fields) {
+ return {
+ name: collection.name,
+ type: collection.type,
+ listRule: collection.listRule,
+ viewRule: collection.viewRule,
+ createRule: collection.createRule,
+ updateRule: collection.updateRule,
+ deleteRule: collection.deleteRule,
+ fields,
+ indexes: collection.indexes || [],
+ };
+}
+
+async function ensureAutoGenerateField(target) {
+ const collection = await pb.collections.getOne(target.collectionName);
+ const fields = Array.isArray(collection.fields) ? collection.fields : [];
+ const existingField = fields.find((field) => field && field.name === target.fieldName);
+
+ if (!existingField) {
+ throw new Error(`${target.collectionName}.${target.fieldName} 不存在`);
+ }
+
+ const currentPattern = String(existingField.autogeneratePattern || '');
+ if (existingField.type === 'text' && currentPattern === target.autogeneratePattern) {
+ return {
+ collectionName: target.collectionName,
+ fieldName: target.fieldName,
+ action: 'skipped',
+ autogeneratePattern: currentPattern,
+ };
+ }
+
+ const nextFields = fields
+ .filter((field) => field && field.name !== 'id')
+ .map((field) => {
+ if (field.name !== target.fieldName) {
+ return normalizeFieldPayload(field);
+ }
+
+ return normalizeFieldPayload(field, {
+ type: 'text',
+ required: true,
+ autogeneratePattern: target.autogeneratePattern,
+ });
+ });
+
+ await pb.collections.update(collection.id, buildCollectionPayload(collection, nextFields));
+
+ return {
+ collectionName: target.collectionName,
+ fieldName: target.fieldName,
+ action: 'updated',
+ autogeneratePattern: target.autogeneratePattern,
+ };
+}
+
+async function verifyTargets() {
+ const result = [];
+
+ for (const target of TARGETS) {
+ const collection = await pb.collections.getOne(target.collectionName);
+ const field = (collection.fields || []).find((item) => item && item.name === target.fieldName);
+ const pattern = String(field && field.autogeneratePattern || '');
+ const ok = !!field && field.type === 'text' && pattern === target.autogeneratePattern;
+
+ result.push({
+ collectionName: target.collectionName,
+ fieldName: target.fieldName,
+ type: field ? field.type : '',
+ required: !!(field && field.required),
+ autogeneratePattern: pattern,
+ ok,
+ });
+ }
+
+ const failed = result.filter((item) => !item.ok);
+ if (failed.length) {
+ throw new Error(`以下字段未正确启用自动生成: ${JSON.stringify(failed, null, 2)}`);
+ }
+
+ return result;
+}
+
+async function main() {
+ try {
+ console.log(`🔄 正在连接 PocketBase: ${PB_URL}`);
+ pb.authStore.save(AUTH_TOKEN, null);
+ console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。');
+
+ const changed = [];
+ for (const target of TARGETS) {
+ changed.push(await ensureAutoGenerateField(target));
+ }
+
+ console.log('📝 业务 ID 自动生成处理结果:');
+ console.log(JSON.stringify(changed, null, 2));
+
+ const verified = await verifyTargets();
+ console.log('📝 校验结果:');
+ console.log(JSON.stringify(verified, null, 2));
+
+ console.log('🎉 cart_id / order_id 自动生成规则已就绪。');
+ } catch (error) {
+ console.error('❌ cart/order 业务 ID 自动生成迁移失败:', error.response?.data || error.message || error);
+ process.exitCode = 1;
+ }
+}
+
+main();
\ No newline at end of file
diff --git a/script/pocketbase.js b/script/pocketbase.js
index 1caf649..614998a 100644
--- a/script/pocketbase.js
+++ b/script/pocketbase.js
@@ -8,13 +8,14 @@ const ADMIN_PASSWORD = 'Momo123456';
// ==========================================
const pb = new PocketBase(PB_URL);
+const SOFT_DELETE_RULE = 'is_delete = 0';
const collections = [
{
name: 'tbl_system_dict',
type: 'base',
- listRule: '',
- viewRule: '',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
createRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
updateRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
deleteRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
@@ -26,7 +27,8 @@ const collections = [
{ name: 'dict_word_is_enabled', type: 'bool' },
{ name: 'dict_word_sort_order', type: 'text' },
{ name: 'dict_word_parent_id', type: 'text' },
- { name: 'dict_word_remark', type: 'text' }
+ { name: 'dict_word_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_system_dict_id ON tbl_system_dict (system_dict_id)',
@@ -56,7 +58,8 @@ const collections = [
{ name: 'company_status', type: 'text' },
{ name: 'company_level', type: 'text' },
{ name: 'company_owner_openid', type: 'text' },
- { name: 'company_remark', type: 'text' }
+ { name: 'company_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_company_id ON tbl_company (company_id)',
@@ -71,7 +74,8 @@ const collections = [
{ name: 'usergroups_id', type: 'text', required: true },
{ name: 'usergroups_name', type: 'text' },
{ name: 'usergroups_level', type: 'number' },
- { name: 'usergroups_remark', type: 'text' }
+ { name: 'usergroups_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_usergroups_id ON tbl_user_groups (usergroups_id)'
@@ -97,7 +101,8 @@ const collections = [
{ name: 'users_id_pic_b', type: 'text' },
{ name: 'users_title_picture', type: 'text' },
{ name: 'users_picture', type: 'text' },
- { name: 'usergroups_id', type: 'text' }
+ { name: 'usergroups_id', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_users_id ON tbl_users (users_id)',
diff --git a/script/pocketbase.newpb.js b/script/pocketbase.newpb.js
index b9207a3..ca76395b 100644
--- a/script/pocketbase.newpb.js
+++ b/script/pocketbase.newpb.js
@@ -32,13 +32,14 @@ const ADMIN_PASSWORD = process.env.PB_ADMIN_PASSWORD || backendEnv.PB_ADMIN_PASS
const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || backendEnv.POCKETBASE_AUTH_TOKEN || '';
const pb = new PocketBase(PB_URL);
+const SOFT_DELETE_RULE = 'is_delete = 0';
const collections = [
{
name: 'tbl_auth_users',
type: 'auth',
- listRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB"',
- viewRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB"',
+ listRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB" && is_delete = 0',
+ viewRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB" && is_delete = 0',
createRule: '',
updateRule: '',
deleteRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB"',
@@ -66,6 +67,7 @@ const collections = [
{ name: 'users_id_pic_b', type: 'text' },
{ name: 'users_title_picture', type: 'text' },
{ name: 'users_picture', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
{ name: 'usergroups_id', type: 'text' }
],
indexes: [
@@ -86,11 +88,14 @@ const collections = [
{
name: 'tbl_auth_resources',
type: 'base',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'res_id', type: 'text', required: true },
{ name: 'table_name', type: 'text', required: true },
{ name: 'column_name', type: 'text' },
- { name: 'res_type', type: 'text', required: true }
+ { name: 'res_type', type: 'text', required: true },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_auth_resources_res_id ON tbl_auth_resources (res_id)',
@@ -102,12 +107,15 @@ const collections = [
{
name: 'tbl_auth_roles',
type: 'base',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'role_id', type: 'text', required: true },
{ name: 'role_name', type: 'text', required: true },
{ name: 'role_code', type: 'text' },
{ name: 'role_status', type: 'number' },
- { name: 'role_remark', type: 'text' }
+ { name: 'role_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_auth_roles_role_id ON tbl_auth_roles (role_id)',
@@ -118,12 +126,15 @@ const collections = [
{
name: 'tbl_auth_role_perms',
type: 'base',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'role_perm_id', type: 'text', required: true },
{ name: 'role_id', type: 'text', required: true },
{ name: 'res_id', type: 'text', required: true },
{ name: 'access_level', type: 'number', required: true },
- { name: 'priority', type: 'number' }
+ { name: 'priority', type: 'number' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_auth_role_perms_role_perm_id ON tbl_auth_role_perms (role_perm_id)',
@@ -135,12 +146,15 @@ const collections = [
{
name: 'tbl_auth_user_overrides',
type: 'base',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'override_id', type: 'text', required: true },
{ name: 'users_convers_id', type: 'text', required: true },
{ name: 'res_id', type: 'text', required: true },
{ name: 'access_level', type: 'number', required: true },
- { name: 'priority', type: 'number' }
+ { name: 'priority', type: 'number' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_auth_user_overrides_override_id ON tbl_auth_user_overrides (override_id)',
@@ -152,12 +166,15 @@ const collections = [
{
name: 'tbl_auth_row_scopes',
type: 'base',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
fields: [
{ name: 'scope_id', type: 'text', required: true },
{ name: 'target_type', type: 'text', required: true },
{ name: 'target_id', type: 'text', required: true },
{ name: 'table_name', type: 'text', required: true },
- { name: 'filter_sql', type: 'editor', required: true }
+ { name: 'filter_sql', type: 'editor', required: true },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true }
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_auth_row_scopes_scope_id ON tbl_auth_row_scopes (scope_id)',
diff --git a/script/pocketbase.product-list.js b/script/pocketbase.product-list.js
index 85e0f2a..415d953 100644
--- a/script/pocketbase.product-list.js
+++ b/script/pocketbase.product-list.js
@@ -12,6 +12,7 @@ try {
const PB_URL = (process.env.PB_URL || 'https://bai-api.blv-oa.com/pb').replace(/\/+$/, '');
const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
+const SOFT_DELETE_RULE = 'is_delete = 0';
if (!AUTH_TOKEN) {
console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行建表。');
@@ -25,8 +26,8 @@ const collections = [
name: 'tbl_product_list',
type: 'base',
// Empty rules in PocketBase mean public read access.
- listRule: '',
- viewRule: '',
+ listRule: SOFT_DELETE_RULE,
+ viewRule: SOFT_DELETE_RULE,
createRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
updateRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
deleteRule: '(@request.auth.users_idtype = "ManagePlatform" || @request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB")',
@@ -50,6 +51,7 @@ const collections = [
{ name: 'prod_list_basic_price', type: 'number' },
{ name: 'prod_list_vip_price', type: 'json' },
{ name: 'prod_list_remark', type: 'text' },
+ { name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
],
indexes: [
'CREATE UNIQUE INDEX idx_tbl_product_list_prod_list_id ON tbl_product_list (prod_list_id)',
@@ -108,7 +110,9 @@ function buildCollectionPayload(collectionData, existingCollection) {
}
const targetFieldMap = new Map(collectionData.fields.map((field) => [field.name, field]));
- const fields = (existingCollection.fields || []).map((existingField) => {
+ const fields = (existingCollection.fields || []).filter((existingField) => {
+ return existingField && existingField.name !== 'id';
+ }).map((existingField) => {
const targetField = targetFieldMap.get(existingField.name);
if (!targetField) {
return existingField;