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 + ? '' + escapeHtml(label) + '' + : '
暂无预览
') + + '
' + + '
当前附件ID:' + escapeHtml(value || '-') + '
' + + (url + ? '查看图片:' + escapeHtml(filename || '附件') + '' + : '
上传后将先保存到 tbl_attachments,再自动回填附件ID
') + + '
' + + '
' + + '
' + + '
' + } + 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 = '
共 0 条
' + + '
' + sizeSelector + '
' + 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 = '
共 ' + total + ' 条,显示第 ' + startIndex + '-' + endIndex + ' 条,第 ' + page + '/' + totalPages + ' 页
' + + '
' + sizeSelector + buttons.join('') + '
' + } + + 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 = '
共 0 条
' + + '
' + sizeSelector + '
' + 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 = '
共 ' + total + ' 条,显示第 ' + startIndex + '-' + endIndex + ' 条,第 ' + page + '/' + totalPages + ' 页
' + + '
' + sizeSelector + buttons.join('') + '
' + } + + 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;