feat: 添加 PocketBase 管理端与自定义 hooks 的 API 文档
- 新增 openapi.yaml 文件,定义管理端与自定义 hooks 的接口文档,包括系统、微信认证、平台认证、字典管理、附件管理、文档管理、购物车和订单等接口。 - 新增 order.yaml 文件,定义订单相关的接口,包括查询订单列表、查询订单详情、新增订单记录、修改订单记录和删除订单记录的请求和响应结构。 - 新增 openapi-wx/openapi.yaml 文件,定义 PocketBase 原生 API 文档,包含企业信息、附件信息、产品信息、文档信息、购物车和订单的接口。 - 新增 pocketbase.scheme.js 文件,包含 PocketBase 集合的创建和更新逻辑,定义了多个集合的字段、索引和权限规则。
This commit is contained in:
152
docs/pb_collection_permission_overview.md
Normal file
152
docs/pb_collection_permission_overview.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# PocketBase Collection 权限总览
|
||||||
|
|
||||||
|
> 范围:当前仓库 `docs/pb_tbl_*.md` 已存在表结构文档 + 本次新增的 3 张方案相关草案表
|
||||||
|
> 目标:把“每张表该如何做 PocketBase 原生权限控制”整理成一份便于确认、评审和后续写脚本的总览
|
||||||
|
> 状态:`working draft`
|
||||||
|
|
||||||
|
## 阅读说明
|
||||||
|
|
||||||
|
- 本文档分为“现状规则”和“本次新增建议规则”两部分。
|
||||||
|
- “现状规则”优先参考现有 `docs/pb_tbl_*.md` 与 `script/pocketbase*.js`。
|
||||||
|
- “新增建议规则”对应你这次要加的 3 张表,当前仅为文档设计,尚未执行。
|
||||||
|
- 本文所有 owner 行级规则都默认使用 `@request.auth.openid` 作为业务身份锚点。
|
||||||
|
- 软删除约定统一为:`is_delete = 0` 才视为可见、可操作数据。
|
||||||
|
|
||||||
|
## 一、推荐统一规则模板
|
||||||
|
|
||||||
|
### 1. owner 私有数据表
|
||||||
|
|
||||||
|
适用场景:
|
||||||
|
|
||||||
|
- 购物车
|
||||||
|
- 订单
|
||||||
|
- 本次新增的方案 / 方案分享 / 方案模板
|
||||||
|
|
||||||
|
推荐模板:
|
||||||
|
|
||||||
|
| Rule | 推荐表达式 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `listRule` | `@request.auth.id != "" && <owner_field> = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `viewRule` | `@request.auth.id != "" && <owner_field> = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `createRule` | `@request.auth.id != "" && @request.body.<owner_field> = @request.auth.openid` |
|
||||||
|
| `updateRule` | `@request.auth.id != "" && <owner_field> = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `deleteRule` | `@request.auth.id != "" && <owner_field> = @request.auth.openid && is_delete = 0` |
|
||||||
|
|
||||||
|
### 2. 公开可读、管理写
|
||||||
|
|
||||||
|
适用场景:
|
||||||
|
|
||||||
|
- 字典
|
||||||
|
- 产品
|
||||||
|
- 文档
|
||||||
|
- 附件
|
||||||
|
|
||||||
|
典型模式:
|
||||||
|
|
||||||
|
| Rule | 推荐表达式 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `listRule` | `is_delete = 0` |
|
||||||
|
| `viewRule` | `is_delete = 0` |
|
||||||
|
| `createRule` | `@request.auth.users_idtype = "ManagePlatform" \|\| @request.auth.usergroups_id = "<管理角色ID>"` |
|
||||||
|
| `updateRule` | `@request.auth.users_idtype = "ManagePlatform" \|\| @request.auth.usergroups_id = "<管理角色ID>"` |
|
||||||
|
| `deleteRule` | `@request.auth.users_idtype = "ManagePlatform" \|\| @request.auth.usergroups_id = "<管理角色ID>"` |
|
||||||
|
|
||||||
|
### 3. 管理/权限元数据表
|
||||||
|
|
||||||
|
适用场景:
|
||||||
|
|
||||||
|
- 角色
|
||||||
|
- 资源
|
||||||
|
- 角色权限
|
||||||
|
- 用户覆盖权限
|
||||||
|
- 行级范围
|
||||||
|
- 文档操作历史
|
||||||
|
|
||||||
|
典型模式:
|
||||||
|
|
||||||
|
- 默认不建议公开给普通用户直接访问。
|
||||||
|
- 推荐仅管理端 / 管理角色通过 hooks 页面或内部脚本维护。
|
||||||
|
|
||||||
|
## 二、现有表规则总表
|
||||||
|
|
||||||
|
## 2.1 owner 私有数据
|
||||||
|
|
||||||
|
| 表名 | owner 字段 | list/view | create | update/delete | 说明 |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| `tbl_cart` | `cart_owner` | `@request.auth.id != "" && cart_owner = @request.auth.openid && is_delete = 0` | `@request.body.cart_owner != ""` | `@request.auth.id != "" && cart_owner = @request.auth.openid` | 已在 `script/pocketbase.cart-order.js` 明确;当前 create 已放宽为只要显式提交非空 owner 即可 |
|
||||||
|
| `tbl_order` | `order_owner` | `@request.auth.id != "" && order_owner = @request.auth.openid && is_delete = 0` | `@request.auth.id != "" && @request.body.order_owner = @request.auth.openid` | `@request.auth.id != "" && order_owner = @request.auth.openid` | 已在 `script/pocketbase.cart-order.js` 明确 |
|
||||||
|
|
||||||
|
## 2.2 公开可读、管理写
|
||||||
|
|
||||||
|
| 表名 | list/view | create/update/delete | 说明 |
|
||||||
|
| :--- | :--- | :--- | :--- |
|
||||||
|
| `tbl_system_dict` | `is_delete = 0` | `ManagePlatform` 或管理角色 | `script/pocketbase.dictionary.js` 已明确 |
|
||||||
|
| `tbl_product_list` | `is_delete = 0` | `ManagePlatform` 或管理角色 | `script/pocketbase.product-list.js` 已明确 |
|
||||||
|
| `tbl_document` | `is_delete = 0` | 文档当前脚本未显式开放原生写;实际业务通过 hooks 管理端写入 | `script/pocketbase.documents.js` + 现有文档口径 |
|
||||||
|
| `tbl_attachments` | `is_delete = 0` | 原生写默认不开放,实际通过 hooks / 管理端上传 | `script/pocketbase.documents.js` + 现有文档口径 |
|
||||||
|
|
||||||
|
## 2.3 管理端/权限模型表
|
||||||
|
|
||||||
|
| 表名 | 当前口径 | 说明 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `tbl_auth_resources` | 管理用途,不建议公开给普通用户直连 | 现有文档写为“仅管理员 / 管理角色允许” |
|
||||||
|
| `tbl_auth_roles` | 管理用途,不建议公开给普通用户直连 | 同上 |
|
||||||
|
| `tbl_auth_role_perms` | 管理用途,不建议公开给普通用户直连 | 同上 |
|
||||||
|
| `tbl_auth_user_overrides` | 管理用途,不建议公开给普通用户直连 | 同上 |
|
||||||
|
| `tbl_auth_row_scopes` | 管理用途,不建议公开给普通用户直连 | 同上 |
|
||||||
|
| `tbl_document_operation_history` | 管理用途,不建议公开给普通用户直连 | 同上 |
|
||||||
|
|
||||||
|
## 2.4 特殊表
|
||||||
|
|
||||||
|
| 表名 | 当前口径 | 备注 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `tbl_auth_users` | 当前文档口径为“公开允许新增与修改;列表 / 详情 / 删除仅管理员 / 管理角色允许” | 这是 auth collection,既受 PB auth 机制影响,也受 hooks 认证流程影响 |
|
||||||
|
| `tbl_company` | 当前文档口径为“公开可创建、公开可列出;详情 / 更新 / 删除仅管理员或管理后台用户允许” | 更偏业务接入表,不属于 owner 私有表 |
|
||||||
|
|
||||||
|
## 三、本次新增 3 表建议规则
|
||||||
|
|
||||||
|
| 表名 | owner 字段 | list/view | create | update/delete | 备注 |
|
||||||
|
| :--- | :--- | :--- | :--- | :--- | :--- |
|
||||||
|
| `tbl_scheme` | `scheme_owner` | `@request.auth.id != "" && scheme_owner = @request.auth.openid && is_delete = 0` | `@request.auth.id != "" && @request.body.scheme_owner = @request.auth.openid` | `@request.auth.id != "" && scheme_owner = @request.auth.openid && is_delete = 0` | 私有方案表 |
|
||||||
|
| `tbl_scheme_share` | `scheme_share_owner` | `@request.auth.id != "" && scheme_share_owner = @request.auth.openid && is_delete = 0` | `@request.auth.id != "" && @request.body.scheme_share_owner = @request.auth.openid` | `@request.auth.id != "" && scheme_share_owner = @request.auth.openid && is_delete = 0` | 按你的要求,分享表也采用 owner 私有规则,因此接收方默认不能直连查看 |
|
||||||
|
| `tbl_scheme_template` | `scheme_template_owner` | `@request.auth.id != "" && scheme_template_owner = @request.auth.openid && is_delete = 0` | `@request.auth.id != "" && @request.body.scheme_template_owner = @request.auth.openid` | `@request.auth.id != "" && scheme_template_owner = @request.auth.openid && is_delete = 0` | 私有模板表 |
|
||||||
|
|
||||||
|
## 四、这次设计里需要你重点确认的点
|
||||||
|
|
||||||
|
### 1. `tbl_scheme_share` 是否接受新增 `scheme_share_owner`
|
||||||
|
|
||||||
|
这是本次最关键的结构补充。
|
||||||
|
|
||||||
|
原因:
|
||||||
|
|
||||||
|
- 你要求 3 张表统一按 `xxx_owner = token 对应 openid` 做 PB 行级限制。
|
||||||
|
- 原始草图里的分享表没有 owner 字段,无法直接套用这套规则。
|
||||||
|
|
||||||
|
如果你确认这条设计,后续脚本会把它作为正式字段创建。
|
||||||
|
|
||||||
|
### 2. `createRule` 是否必须校验 `@request.body.<owner_field> = @request.auth.openid`
|
||||||
|
|
||||||
|
当前文档建议“要校验”。
|
||||||
|
|
||||||
|
原因:
|
||||||
|
|
||||||
|
- 这样可以防止普通用户伪造别人的 owner 值直接创建越权数据。
|
||||||
|
- 这也是现有 `tbl_cart` / `tbl_order` 的做法,风格一致。
|
||||||
|
|
||||||
|
### 3. 分享表是否允许“被分享人”直接走 PB 原生 API 查看
|
||||||
|
|
||||||
|
当前文档按你的要求,先设计成:
|
||||||
|
|
||||||
|
- 只有 `scheme_share_owner` 能删改查
|
||||||
|
|
||||||
|
这最严格,但也意味着:
|
||||||
|
|
||||||
|
- `scheme_share_to` 对应的用户不能直接通过 PB 原生 `list/view` 看到分享记录
|
||||||
|
|
||||||
|
如果你的实际业务是“被分享人需要能直接看到收到的分享”,那这张表的 `listRule` / `viewRule` 需要改成双边可见,而不是纯 owner 模式。
|
||||||
|
|
||||||
|
## 五、对应文档索引
|
||||||
|
|
||||||
|
- [pb_tbl_scheme.md](/E:/Project_Class/BAI/Web_BAI_Manage_ApiServer/docs/pb_tbl_scheme.md)
|
||||||
|
- [pb_tbl_scheme_share.md](/E:/Project_Class/BAI/Web_BAI_Manage_ApiServer/docs/pb_tbl_scheme_share.md)
|
||||||
|
- [pb_tbl_scheme_template.md](/E:/Project_Class/BAI/Web_BAI_Manage_ApiServer/docs/pb_tbl_scheme_template.md)
|
||||||
@@ -20,13 +20,13 @@
|
|||||||
| :--- | :--- | :---: | :--- |
|
| :--- | :--- | :---: | :--- |
|
||||||
| `id` | `text` | 是 | PocketBase 记录主键 |
|
| `id` | `text` | 是 | PocketBase 记录主键 |
|
||||||
| `cart_id` | `text` | 是 | 购物车项业务 ID,唯一标识 |
|
| `cart_id` | `text` | 是 | 购物车项业务 ID,唯一标识 |
|
||||||
| `cart_number` | `text` | 是 | 购物车名称 / 分组号,默认可按“用户名+年月日时分秒”生成 |
|
| `cart_number` | `text` | 否 | 购物车名称 / 分组号,可为空;如需展示编号建议在 hooks / 业务侧补齐 |
|
||||||
| `cart_create` | `autodate` | 否 | 购物车项创建时间,由数据库自动生成 |
|
| `cart_create` | `autodate` | 否 | 购物车项创建时间,由数据库自动生成 |
|
||||||
| `cart_owner` | `text` | 是 | 生成者 openid,约定保存 `tbl_auth_users.openid` |
|
| `cart_owner` | `text` | 是 | 生成者 openid,约定保存 `tbl_auth_users.openid` |
|
||||||
| `cart_product_id` | `text` | 是 | 产品 ID,建议保存 `tbl_product_list.prod_list_id` |
|
| `cart_product_id` | `relation` | 是 | 原生关联 `tbl_product_list`,单选,保存的是目标产品记录的 PocketBase `recordId` |
|
||||||
| `cart_product_quantity` | `number` | 是 | 产品数量,建议业务侧约束为正整数 |
|
| `cart_product_quantity` | `number` | 否 | 产品数量,可为空;如传值,建议业务侧约束为正整数 |
|
||||||
| `cart_status` | `text` | 是 | 购物车状态,建议值:`有效` / `无效` |
|
| `cart_status` | `text` | 否 | 购物车状态,可为空;如传值,建议值:`有效` / `无效` |
|
||||||
| `cart_at_price` | `number` | 是 | 加入购物车时的价格,用于后续降价提醒或对比 |
|
| `cart_at_price` | `number` | 否 | 加入购物车时的价格,可为空;如传值用于后续降价提醒或对比 |
|
||||||
| `cart_remark` | `text` | 否 | 备注 |
|
| `cart_remark` | `text` | 否 | 备注 |
|
||||||
| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
|
| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
|
||||||
|
|
||||||
@@ -45,7 +45,12 @@
|
|||||||
|
|
||||||
## 补充约定
|
## 补充约定
|
||||||
|
|
||||||
- `cart_owner`、`cart_product_id` 当前按文本字段保存业务 ID,不直接建立 relation,便于兼容现有 hooks 业务模型。
|
- `cart_owner` 当前按文本字段保存业务 ID。
|
||||||
|
- `cart_product_id` 已改为 PocketBase 原生 `relation` 字段,关联目标集合为 `tbl_product_list`,且 `maxSelect = 1`。
|
||||||
|
- 调用原生 records create / update 接口时,`cart_product_id` 必须传 **`tbl_product_list` 的 PocketBase 记录主键 `id`**,不能再传业务 ID `prod_list_id`。
|
||||||
|
- 如前端当前拿到的是业务 ID `prod_list_id`,需先调用 `GET /pb/api/collections/tbl_product_list/records?filter=prod_list_id="..."&perPage=1&page=1` 查出对应 `recordId`,再把该 `recordId` 作为 `cart_product_id` 提交。
|
||||||
|
- 当前原生 collection 层面,创建时仅要求客户端显式提交非空 `cart_owner`;不再强制要求与当前 token 对应 `openid` 相等。
|
||||||
|
- 当前表结构层面,除 `cart_id`、`cart_owner`、`cart_product_id` 外,其余业务字段均允许为空。
|
||||||
- `cart_owner` 统一保存 `tbl_auth_users.openid`,便于直接使用微信登录返回 token 做原生访问控制。
|
- `cart_owner` 统一保存 `tbl_auth_users.openid`,便于直接使用微信登录返回 token 做原生访问控制。
|
||||||
- `is_delete` 用于软删除控制,购物车项删除时建议优先标记为 `1`。
|
- `is_delete` 用于软删除控制,购物车项删除时建议优先标记为 `1`。
|
||||||
- 集合默认查询规则已内置 `is_delete = 0`,常规列表/详情不会返回已软删除数据。
|
- 集合默认查询规则已内置 `is_delete = 0`,常规列表/详情不会返回已软删除数据。
|
||||||
|
|||||||
73
docs/pb_tbl_scheme.md
Normal file
73
docs/pb_tbl_scheme.md
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# pb_tbl_scheme
|
||||||
|
|
||||||
|
> 来源:用户提供的结构草图(2026-04-08)与现有 `tbl_cart` / `tbl_order` owner 行级权限模式
|
||||||
|
> 类型:`base`
|
||||||
|
> 状态:`draft`,仅文档设计,尚未执行建表
|
||||||
|
> 读写规则:任意已登录用户可新增,但仅可访问 `scheme_owner = 当前 token 对应 openid` 且 `is_delete = 0` 的记录
|
||||||
|
|
||||||
|
## 表用途
|
||||||
|
|
||||||
|
用于存储用户自己的方案主表,承载方案名称、适用酒店类型、方案筛选条件、房型配置、设备偏好以及引用的高/中/低端模板。
|
||||||
|
|
||||||
|
## 字段清单
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 说明 |
|
||||||
|
| :--- | :--- | :---: | :--- |
|
||||||
|
| `id` | `text` | 是 | PocketBase 记录主键 |
|
||||||
|
| `scheme_id` | `text` | 是 | 方案业务 ID,唯一标识 |
|
||||||
|
| `scheme_name` | `text` | 是 | 方案名称 |
|
||||||
|
| `scheme_owner` | `text` | 是 | 方案所有者 openid,保存 `tbl_auth_users.openid` |
|
||||||
|
| `scheme_share_status` | `text` | 否 | 方案分享状态,便于快速识别是否已分享 |
|
||||||
|
| `scheme_expires_at` | `date` | 否 | 方案有效期 |
|
||||||
|
| `scheme_hotel_type` | `text` | 否 | 酒店类型,如经济型 / 中高端 / 连锁型 / 特色名宿 / 特色酒店 |
|
||||||
|
| `scheme_solution_type` | `text` | 否 | 方案类型,建议保存枚举或 `|` 聚合值 |
|
||||||
|
| `scheme_solution_feature` | `text` | 否 | 方案特点,建议保存枚举或 `|` 聚合值 |
|
||||||
|
| `scheme_room_type` | `json` | 否 | 房型配置,建议格式:`[{"room_type":"大床房","qty":20}]` |
|
||||||
|
| `scheme_curtains` | `text` | 否 | 窗帘类型 |
|
||||||
|
| `scheme_voice_device` | `text` | 否 | 语音设备 |
|
||||||
|
| `scheme_ac_type` | `text` | 否 | 空调类型 |
|
||||||
|
| `scheme_template_highend` | `text` | 否 | 高端模板 ID,建议保存 `tbl_scheme_template.scheme_template_id` |
|
||||||
|
| `scheme_template_midend` | `text` | 否 | 中端模板 ID,建议保存 `tbl_scheme_template.scheme_template_id` |
|
||||||
|
| `scheme_template_lowend` | `text` | 否 | 地端模板 ID,建议保存 `tbl_scheme_template.scheme_template_id` |
|
||||||
|
| `scheme_status` | `text` | 否 | 方案状态,如草稿 / 生效 / 失效 |
|
||||||
|
| `scheme_remark` | `text` | 否 | 备注 |
|
||||||
|
| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
|
||||||
|
|
||||||
|
## 索引
|
||||||
|
|
||||||
|
| 索引名 | 类型 | 说明 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `idx_tbl_scheme_scheme_id` | `UNIQUE INDEX` | 保证 `scheme_id` 唯一 |
|
||||||
|
| `idx_tbl_scheme_scheme_owner` | `INDEX` | 加速按方案所有者查询 |
|
||||||
|
| `idx_tbl_scheme_scheme_name` | `INDEX` | 加速按方案名称检索 |
|
||||||
|
| `idx_tbl_scheme_scheme_share_status` | `INDEX` | 加速按分享状态过滤 |
|
||||||
|
| `idx_tbl_scheme_scheme_expires_at` | `INDEX` | 加速按有效期过滤 |
|
||||||
|
| `idx_tbl_scheme_scheme_hotel_type` | `INDEX` | 加速按酒店类型过滤 |
|
||||||
|
| `idx_tbl_scheme_scheme_solution_type` | `INDEX` | 加速按方案类型过滤 |
|
||||||
|
| `idx_tbl_scheme_scheme_solution_feature` | `INDEX` | 加速按方案特点过滤 |
|
||||||
|
| `idx_tbl_scheme_scheme_status` | `INDEX` | 加速按方案状态过滤 |
|
||||||
|
| `idx_tbl_scheme_owner_status` | `INDEX` | 加速同一用户下按状态查询 |
|
||||||
|
|
||||||
|
## 建议 PocketBase 原生权限规则
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- 采用和 `tbl_cart` / `tbl_order` 一致的 owner 行级隔离模式。
|
||||||
|
- 这里按“任意已登录用户可创建自己的方案”设计,因此 `createRule` 仍要求携带有效 token。
|
||||||
|
- 若后续由 hooks 自动回填 `scheme_owner`,也建议保留 `@request.body.scheme_owner = @request.auth.openid` 约束,避免越权代建。
|
||||||
|
|
||||||
|
| Rule | 建议表达式 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `listRule` | `@request.auth.id != "" && scheme_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `viewRule` | `@request.auth.id != "" && scheme_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `createRule` | `@request.auth.id != "" && @request.body.scheme_owner = @request.auth.openid` |
|
||||||
|
| `updateRule` | `@request.auth.id != "" && scheme_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `deleteRule` | `@request.auth.id != "" && scheme_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
|
||||||
|
## 补充约定
|
||||||
|
|
||||||
|
- `scheme_template_highend` / `scheme_template_midend` / `scheme_template_lowend` 当前建议先保存模板业务 ID,不直接建立 relation,便于兼容现有 hooks 风格。
|
||||||
|
- `scheme_room_type` 推荐使用 `json` 数组,避免后续字符串解析成本。
|
||||||
|
- `scheme_solution_type` 与 `scheme_solution_feature` 如果后续要做模板筛选,建议统一保存字典枚举值,而不是自由文本。
|
||||||
|
- `is_delete` 用于软删除控制;对外列表、详情、修改、删除规则都建议默认排除已删除数据。
|
||||||
|
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
|
||||||
71
docs/pb_tbl_scheme_share.md
Normal file
71
docs/pb_tbl_scheme_share.md
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
# pb_tbl_scheme_share
|
||||||
|
|
||||||
|
> 来源:用户提供的结构草图(2026-04-08)与本次 owner 行级权限要求
|
||||||
|
> 类型:`base`
|
||||||
|
> 状态:`draft`,仅文档设计,尚未执行建表
|
||||||
|
> 读写规则:任意已登录用户可新增,但仅可访问 `scheme_share_owner = 当前 token 对应 openid` 且 `is_delete = 0` 的记录
|
||||||
|
|
||||||
|
## 表用途
|
||||||
|
|
||||||
|
用于存储方案分享记录,描述某个方案被分享给谁、分享权限、有效期以及接收状态。
|
||||||
|
|
||||||
|
## 关键设计说明
|
||||||
|
|
||||||
|
原始草图里没有 owner 字段,但你要求这 3 张表统一采用 `xxx_owner = 当前 token 对应 openid` 的 PB 行级约束。
|
||||||
|
|
||||||
|
因此本表文档中补充新增:
|
||||||
|
|
||||||
|
- `scheme_share_owner`
|
||||||
|
|
||||||
|
该字段用于表示“谁发起了这条分享记录”,并作为 PocketBase 原生访问控制的 owner 锚点。
|
||||||
|
|
||||||
|
## 字段清单
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 说明 |
|
||||||
|
| :--- | :--- | :---: | :--- |
|
||||||
|
| `id` | `text` | 是 | PocketBase 记录主键 |
|
||||||
|
| `scheme_share_id` | `text` | 是 | 分享业务 ID,唯一标识 |
|
||||||
|
| `scheme_id` | `text` | 是 | 关联方案 ID,建议保存 `tbl_scheme.scheme_id` |
|
||||||
|
| `scheme_share_owner` | `text` | 是 | 分享发起人 openid,用于 owner 行级权限控制 |
|
||||||
|
| `scheme_share_to` | `text` | 是 | 被分享目标用户标识,建议保存目标用户 openid |
|
||||||
|
| `scheme_share_acceptance_status` | `text` | 否 | 分享接收状态,如待接收 / 已接收 / 已参与 / 已拒绝 |
|
||||||
|
| `scheme_share_expires_at` | `date` | 否 | 分享有效期 |
|
||||||
|
| `scheme_share_permission` | `text` | 否 | 分享权限,如只读 / 可复制 / 可编辑 |
|
||||||
|
| `scheme_share_remark` | `text` | 否 | 分享备注 |
|
||||||
|
| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
|
||||||
|
|
||||||
|
## 索引
|
||||||
|
|
||||||
|
| 索引名 | 类型 | 说明 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `idx_tbl_scheme_share_scheme_share_id` | `UNIQUE INDEX` | 保证 `scheme_share_id` 唯一 |
|
||||||
|
| `idx_tbl_scheme_share_scheme_id` | `INDEX` | 加速按方案查询分享记录 |
|
||||||
|
| `idx_tbl_scheme_share_scheme_share_owner` | `INDEX` | 加速按分享发起人查询 |
|
||||||
|
| `idx_tbl_scheme_share_scheme_share_to` | `INDEX` | 加速按被分享目标查询 |
|
||||||
|
| `idx_tbl_scheme_share_acceptance_status` | `INDEX` | 加速按接收状态过滤 |
|
||||||
|
| `idx_tbl_scheme_share_expires_at` | `INDEX` | 加速按有效期过滤 |
|
||||||
|
| `idx_tbl_scheme_share_owner_to` | `INDEX` | 加速同一发起人向某目标用户的查询 |
|
||||||
|
| `idx_tbl_scheme_share_unique_map` | `UNIQUE INDEX` | 保证同一发起人对同一方案给同一目标的分享记录唯一 |
|
||||||
|
|
||||||
|
## 建议 PocketBase 原生权限规则
|
||||||
|
|
||||||
|
说明:
|
||||||
|
|
||||||
|
- 严格按你的要求,所有删改查都只允许 owner 访问。
|
||||||
|
- 这意味着“被分享人”不能直接通过 PocketBase 原生 records API 读取这张表;如果后续要支持被分享方直连访问,需要另行放宽 `listRule` / `viewRule` 或改由 hooks 中转。
|
||||||
|
|
||||||
|
| Rule | 建议表达式 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `listRule` | `@request.auth.id != "" && scheme_share_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `viewRule` | `@request.auth.id != "" && scheme_share_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `createRule` | `@request.auth.id != "" && @request.body.scheme_share_owner = @request.auth.openid` |
|
||||||
|
| `updateRule` | `@request.auth.id != "" && scheme_share_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `deleteRule` | `@request.auth.id != "" && scheme_share_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
|
||||||
|
## 补充约定
|
||||||
|
|
||||||
|
- `scheme_share_to` 当前建议保存目标用户 `openid`,这样后续如果要做 hooks 查询或消息通知,字段可直接复用。
|
||||||
|
- 若后续要支持“接收方修改 `scheme_share_acceptance_status`”,则当前 owner-only 原生规则不够,需要追加 hooks 或重新设计 PB 规则。
|
||||||
|
- 推荐把 `scheme_share_permission` 控制在有限枚举内,例如:`readonly`、`copyable`、`editable`。
|
||||||
|
- `is_delete` 用于软删除控制,撤销分享时建议优先标记为 `1`。
|
||||||
|
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
|
||||||
59
docs/pb_tbl_scheme_template.md
Normal file
59
docs/pb_tbl_scheme_template.md
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
# pb_tbl_scheme_template
|
||||||
|
|
||||||
|
> 来源:用户提供的结构草图(2026-04-08)与现有附件 / 产品字段设计规范
|
||||||
|
> 类型:`base`
|
||||||
|
> 状态:`draft`,仅文档设计,尚未执行建表
|
||||||
|
> 读写规则:任意已登录用户可新增,但仅可访问 `scheme_template_owner = 当前 token 对应 openid` 且 `is_delete = 0` 的记录
|
||||||
|
|
||||||
|
## 表用途
|
||||||
|
|
||||||
|
用于存储方案模板,作为方案配置时可复用的标准模板库,承载图标、标签、方案适配条件以及模板内产品清单。
|
||||||
|
|
||||||
|
## 字段清单
|
||||||
|
|
||||||
|
| 字段名 | 类型 | 必填 | 说明 |
|
||||||
|
| :--- | :--- | :---: | :--- |
|
||||||
|
| `id` | `text` | 是 | PocketBase 记录主键 |
|
||||||
|
| `scheme_template_id` | `text` | 是 | 模板业务 ID,唯一标识 |
|
||||||
|
| `scheme_template_icon` | `text` | 否 | 模板图标,建议保存 `tbl_attachments.attachments_id` |
|
||||||
|
| `scheme_template_label` | `text` | 否 | 模板标签,便于关键词检索 |
|
||||||
|
| `scheme_template_name` | `text` | 是 | 模板名称 |
|
||||||
|
| `scheme_template_owner` | `text` | 是 | 模板所有者 openid,保存 `tbl_auth_users.openid` |
|
||||||
|
| `scheme_template_status` | `text` | 否 | 模板状态,如有效 / 主推 / 过期 |
|
||||||
|
| `scheme_template_solution_type` | `text` | 否 | 适用方案类型 |
|
||||||
|
| `scheme_template_solution_feature` | `text` | 否 | 适用方案特点 |
|
||||||
|
| `scheme_template_product_list` | `json` | 否 | 产品清单,建议格式:`[{"product_id":"PROD-xxx","qty":5,"note":"客厅使用"}]` |
|
||||||
|
| `scheme_template_description` | `text` | 否 | 模板说明 |
|
||||||
|
| `scheme_template_remark` | `text` | 否 | 备注 |
|
||||||
|
| `is_delete` | `number` | 否 | 软删除标记,`0` 表示未删除,`1` 表示已删除,默认 `0` |
|
||||||
|
|
||||||
|
## 索引
|
||||||
|
|
||||||
|
| 索引名 | 类型 | 说明 |
|
||||||
|
| :--- | :--- | :--- |
|
||||||
|
| `idx_tbl_scheme_template_scheme_template_id` | `UNIQUE INDEX` | 保证 `scheme_template_id` 唯一 |
|
||||||
|
| `idx_tbl_scheme_template_scheme_template_owner` | `INDEX` | 加速按模板所有者查询 |
|
||||||
|
| `idx_tbl_scheme_template_scheme_template_name` | `INDEX` | 加速按模板名称检索 |
|
||||||
|
| `idx_tbl_scheme_template_scheme_template_label` | `INDEX` | 加速按模板标签检索 |
|
||||||
|
| `idx_tbl_scheme_template_scheme_template_status` | `INDEX` | 加速按模板状态过滤 |
|
||||||
|
| `idx_tbl_scheme_template_solution_type` | `INDEX` | 加速按适用方案类型过滤 |
|
||||||
|
| `idx_tbl_scheme_template_solution_feature` | `INDEX` | 加速按适用方案特点过滤 |
|
||||||
|
| `idx_tbl_scheme_template_owner_status` | `INDEX` | 加速同一用户下按状态查询 |
|
||||||
|
|
||||||
|
## 建议 PocketBase 原生权限规则
|
||||||
|
|
||||||
|
| Rule | 建议表达式 |
|
||||||
|
| :--- | :--- |
|
||||||
|
| `listRule` | `@request.auth.id != "" && scheme_template_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `viewRule` | `@request.auth.id != "" && scheme_template_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `createRule` | `@request.auth.id != "" && @request.body.scheme_template_owner = @request.auth.openid` |
|
||||||
|
| `updateRule` | `@request.auth.id != "" && scheme_template_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
| `deleteRule` | `@request.auth.id != "" && scheme_template_owner = @request.auth.openid && is_delete = 0` |
|
||||||
|
|
||||||
|
## 补充约定
|
||||||
|
|
||||||
|
- `scheme_template_icon` 建议延续现有项目做法,仅保存 `attachments_id`,真实文件统一走 `tbl_attachments`。
|
||||||
|
- `scheme_template_product_list` 建议使用 `json` 数组,单项推荐结构:`product_id`、`qty`、`note`;不要保存自由格式长字符串。
|
||||||
|
- 若模板将来需要“官方模板 + 用户私有模板”并存,则需要额外引入发布状态字段或放宽公共模板读取规则;当前文档严格按 owner 私有模板设计。
|
||||||
|
- `is_delete` 用于软删除控制,模板删除时建议优先标记为 `1`。
|
||||||
|
- PocketBase 系统字段 `created`、`updated` 仍然存在,只是不在 collection 字段清单里单独声明。
|
||||||
@@ -41,6 +41,16 @@ require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/detail.js`)
|
|||||||
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/create.js`)
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/create.js`)
|
||||||
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/update.js`)
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/update.js`)
|
||||||
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/delete.js`)
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/product/delete.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme-template/list.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme-template/detail.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme-template/create.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme-template/update.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme-template/delete.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme/list.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme/detail.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme/create.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme/update.js`)
|
||||||
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/scheme/delete.js`)
|
||||||
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/cart/list.js`)
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/cart/list.js`)
|
||||||
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/cart/detail.js`)
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/cart/detail.js`)
|
||||||
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/cart/create.js`)
|
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/cart/create.js`)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ require(`${__hooks}/bai_web_pb_hooks/pages/index.js`)
|
|||||||
require(`${__hooks}/bai_web_pb_hooks/pages/login.js`)
|
require(`${__hooks}/bai_web_pb_hooks/pages/login.js`)
|
||||||
require(`${__hooks}/bai_web_pb_hooks/pages/document-manage.js`)
|
require(`${__hooks}/bai_web_pb_hooks/pages/document-manage.js`)
|
||||||
require(`${__hooks}/bai_web_pb_hooks/pages/product-manage.js`)
|
require(`${__hooks}/bai_web_pb_hooks/pages/product-manage.js`)
|
||||||
|
require(`${__hooks}/bai_web_pb_hooks/pages/scheme-manage.js`)
|
||||||
require(`${__hooks}/bai_web_pb_hooks/pages/dictionary-manage.js`)
|
require(`${__hooks}/bai_web_pb_hooks/pages/dictionary-manage.js`)
|
||||||
require(`${__hooks}/bai_web_pb_hooks/pages/sdk-permission-manage.js`)
|
require(`${__hooks}/bai_web_pb_hooks/pages/sdk-permission-manage.js`)
|
||||||
require(`${__hooks}/bai_web_pb_hooks/pages/cart-order-manage.js`)
|
require(`${__hooks}/bai_web_pb_hooks/pages/cart-order-manage.js`)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
routerAdd('POST', '/api/scheme-template/create', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
const authState = guards.requireManagePlatformUser(e)
|
||||||
|
guards.duplicateGuard(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeTemplateMutationBody(e, false)
|
||||||
|
const data = schemeService.createSchemeTemplate(authState.openid, payload)
|
||||||
|
|
||||||
|
return success(e, '新增方案模板成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('新增方案模板失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
routerAdd('POST', '/api/scheme-template/delete', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
const authState = guards.requireManagePlatformUser(e)
|
||||||
|
guards.duplicateGuard(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeTemplateDeleteBody(e)
|
||||||
|
const data = schemeService.deleteSchemeTemplate(authState.openid, payload.scheme_template_id)
|
||||||
|
|
||||||
|
return success(e, '删除方案模板成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('删除方案模板失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
routerAdd('POST', '/api/scheme-template/detail', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
guards.requireManagePlatformUser(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeTemplateDetailBody(e)
|
||||||
|
const data = schemeService.getSchemeTemplateDetail(payload.scheme_template_id)
|
||||||
|
|
||||||
|
return success(e, '查询方案模板详情成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('查询方案模板详情失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
routerAdd('POST', '/api/scheme-template/list', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
guards.requireManagePlatformUser(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeTemplateListBody(e)
|
||||||
|
const data = schemeService.listSchemeTemplates(payload)
|
||||||
|
|
||||||
|
return success(e, '查询方案模板列表成功', {
|
||||||
|
items: data,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('查询方案模板列表失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
routerAdd('POST', '/api/scheme-template/update', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
const authState = guards.requireManagePlatformUser(e)
|
||||||
|
guards.duplicateGuard(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeTemplateMutationBody(e, true)
|
||||||
|
const data = schemeService.updateSchemeTemplate(authState.openid, payload)
|
||||||
|
|
||||||
|
return success(e, '更新方案模板成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('更新方案模板失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
25
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/create.js
Normal file
25
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/create.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
routerAdd('POST', '/api/scheme/create', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
const authState = guards.requireManagePlatformUser(e)
|
||||||
|
guards.duplicateGuard(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeMutationBody(e, false)
|
||||||
|
const data = schemeService.createScheme(authState.openid, payload)
|
||||||
|
|
||||||
|
return success(e, '新增方案成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('新增方案失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
25
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/delete.js
Normal file
25
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/delete.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
routerAdd('POST', '/api/scheme/delete', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
const authState = guards.requireManagePlatformUser(e)
|
||||||
|
guards.duplicateGuard(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeDeleteBody(e)
|
||||||
|
const data = schemeService.deleteScheme(authState.openid, payload.scheme_id)
|
||||||
|
|
||||||
|
return success(e, '删除方案成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('删除方案失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
24
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/detail.js
Normal file
24
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/detail.js
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
routerAdd('POST', '/api/scheme/detail', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
guards.requireManagePlatformUser(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeDetailBody(e)
|
||||||
|
const data = schemeService.getSchemeDetail(payload.scheme_id)
|
||||||
|
|
||||||
|
return success(e, '查询方案详情成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('查询方案详情失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
26
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/list.js
Normal file
26
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/list.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
routerAdd('POST', '/api/scheme/list', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
guards.requireManagePlatformUser(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeListBody(e)
|
||||||
|
const data = schemeService.listSchemes(payload)
|
||||||
|
|
||||||
|
return success(e, '查询方案列表成功', {
|
||||||
|
items: data,
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('查询方案列表失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
25
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/update.js
Normal file
25
pocket-base/bai_api_pb_hooks/bai_api_routes/scheme/update.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
routerAdd('POST', '/api/scheme/update', function (e) {
|
||||||
|
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
|
||||||
|
const schemeService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/schemeService.js`)
|
||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
guards.requireJson(e)
|
||||||
|
const authState = guards.requireManagePlatformUser(e)
|
||||||
|
guards.duplicateGuard(e)
|
||||||
|
|
||||||
|
const payload = guards.validateSchemeMutationBody(e, true)
|
||||||
|
const data = schemeService.updateScheme(authState.openid, payload)
|
||||||
|
|
||||||
|
return success(e, '更新方案成功', data)
|
||||||
|
} catch (err) {
|
||||||
|
const status = (err && err.statusCode) || (err && err.status) || 400
|
||||||
|
logger.error('更新方案失败', {
|
||||||
|
status: status,
|
||||||
|
errMsg: (err && err.message) || '未知错误',
|
||||||
|
data: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -548,6 +548,126 @@ function validateProductDeleteBody(e) {
|
|||||||
return validateProductDetailBody(e)
|
return validateProductDetailBody(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateSchemeTemplateListBody(e) {
|
||||||
|
const payload = parseBody(e)
|
||||||
|
|
||||||
|
return {
|
||||||
|
keyword: payload.keyword || '',
|
||||||
|
scheme_template_owner: payload.scheme_template_owner || '',
|
||||||
|
scheme_template_status: payload.scheme_template_status || '',
|
||||||
|
scheme_template_solution_type: payload.scheme_template_solution_type || '',
|
||||||
|
scheme_template_solution_feature: payload.scheme_template_solution_feature || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeTemplateDetailBody(e) {
|
||||||
|
const payload = parseBody(e)
|
||||||
|
if (!payload.scheme_template_id) {
|
||||||
|
throw createAppError(400, 'scheme_template_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheme_template_id: String(payload.scheme_template_id || '').trim(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeTemplateMutationBody(e, isUpdate) {
|
||||||
|
const payload = parseBody(e)
|
||||||
|
|
||||||
|
if (isUpdate && !payload.scheme_template_id) {
|
||||||
|
throw createAppError(400, 'scheme_template_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payload.scheme_template_name) {
|
||||||
|
throw createAppError(400, 'scheme_template_name 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payload.scheme_template_owner) {
|
||||||
|
throw createAppError(400, 'scheme_template_owner 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheme_template_id: payload.scheme_template_id || '',
|
||||||
|
scheme_template_icon: Object.prototype.hasOwnProperty.call(payload, 'scheme_template_icon') ? payload.scheme_template_icon : '',
|
||||||
|
scheme_template_label: payload.scheme_template_label || '',
|
||||||
|
scheme_template_name: payload.scheme_template_name || '',
|
||||||
|
scheme_template_owner: payload.scheme_template_owner || '',
|
||||||
|
scheme_template_status: payload.scheme_template_status || '',
|
||||||
|
scheme_template_solution_type: payload.scheme_template_solution_type || '',
|
||||||
|
scheme_template_solution_feature: payload.scheme_template_solution_feature || '',
|
||||||
|
scheme_template_product_list: Object.prototype.hasOwnProperty.call(payload, 'scheme_template_product_list') ? payload.scheme_template_product_list : [],
|
||||||
|
scheme_template_description: payload.scheme_template_description || '',
|
||||||
|
scheme_template_remark: payload.scheme_template_remark || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeTemplateDeleteBody(e) {
|
||||||
|
return validateSchemeTemplateDetailBody(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeListBody(e) {
|
||||||
|
const payload = parseBody(e)
|
||||||
|
|
||||||
|
return {
|
||||||
|
keyword: payload.keyword || '',
|
||||||
|
scheme_owner: payload.scheme_owner || '',
|
||||||
|
scheme_status: payload.scheme_status || '',
|
||||||
|
scheme_share_status: payload.scheme_share_status || '',
|
||||||
|
scheme_hotel_type: payload.scheme_hotel_type || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeDetailBody(e) {
|
||||||
|
const payload = parseBody(e)
|
||||||
|
if (!payload.scheme_id) {
|
||||||
|
throw createAppError(400, 'scheme_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheme_id: String(payload.scheme_id || '').trim(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeMutationBody(e, isUpdate) {
|
||||||
|
const payload = parseBody(e)
|
||||||
|
|
||||||
|
if (isUpdate && !payload.scheme_id) {
|
||||||
|
throw createAppError(400, 'scheme_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payload.scheme_name) {
|
||||||
|
throw createAppError(400, 'scheme_name 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!payload.scheme_owner) {
|
||||||
|
throw createAppError(400, 'scheme_owner 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheme_id: payload.scheme_id || '',
|
||||||
|
scheme_name: payload.scheme_name || '',
|
||||||
|
scheme_owner: payload.scheme_owner || '',
|
||||||
|
scheme_share_status: payload.scheme_share_status || '',
|
||||||
|
scheme_expires_at: payload.scheme_expires_at || '',
|
||||||
|
scheme_hotel_type: payload.scheme_hotel_type || '',
|
||||||
|
scheme_solution_type: payload.scheme_solution_type || '',
|
||||||
|
scheme_solution_feature: payload.scheme_solution_feature || '',
|
||||||
|
scheme_room_type: Object.prototype.hasOwnProperty.call(payload, 'scheme_room_type') ? payload.scheme_room_type : [],
|
||||||
|
scheme_curtains: payload.scheme_curtains || '',
|
||||||
|
scheme_voice_device: payload.scheme_voice_device || '',
|
||||||
|
scheme_ac_type: payload.scheme_ac_type || '',
|
||||||
|
scheme_template_highend: payload.scheme_template_highend || '',
|
||||||
|
scheme_template_midend: payload.scheme_template_midend || '',
|
||||||
|
scheme_template_lowend: payload.scheme_template_lowend || '',
|
||||||
|
scheme_status: payload.scheme_status || '',
|
||||||
|
scheme_remark: payload.scheme_remark || '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSchemeDeleteBody(e) {
|
||||||
|
return validateSchemeDetailBody(e)
|
||||||
|
}
|
||||||
|
|
||||||
function validateCartListBody(e) {
|
function validateCartListBody(e) {
|
||||||
const payload = parseBody(e)
|
const payload = parseBody(e)
|
||||||
|
|
||||||
@@ -874,6 +994,14 @@ module.exports = {
|
|||||||
validateProductDetailBody,
|
validateProductDetailBody,
|
||||||
validateProductMutationBody,
|
validateProductMutationBody,
|
||||||
validateProductDeleteBody,
|
validateProductDeleteBody,
|
||||||
|
validateSchemeTemplateListBody,
|
||||||
|
validateSchemeTemplateDetailBody,
|
||||||
|
validateSchemeTemplateMutationBody,
|
||||||
|
validateSchemeTemplateDeleteBody,
|
||||||
|
validateSchemeListBody,
|
||||||
|
validateSchemeDetailBody,
|
||||||
|
validateSchemeMutationBody,
|
||||||
|
validateSchemeDeleteBody,
|
||||||
validateCartListBody,
|
validateCartListBody,
|
||||||
validateCartDetailBody,
|
validateCartDetailBody,
|
||||||
validateCartMutationBody,
|
validateCartMutationBody,
|
||||||
|
|||||||
@@ -208,6 +208,19 @@ function findProductRecordByBusinessId(productId) {
|
|||||||
return records.length ? records[0] : null
|
return records.length ? records[0] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function findProductRecordByRecordId(recordId) {
|
||||||
|
const targetId = normalizeText(recordId)
|
||||||
|
if (!targetId) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return $app.findRecordById('tbl_product_list', targetId)
|
||||||
|
} catch (_error) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function buildProductInfo(record) {
|
function buildProductInfo(record) {
|
||||||
if (!record) {
|
if (!record) {
|
||||||
return {
|
return {
|
||||||
@@ -232,9 +245,9 @@ function ensureProductExists(productId) {
|
|||||||
throw createAppError(400, 'cart_product_id 为必填项')
|
throw createAppError(400, 'cart_product_id 为必填项')
|
||||||
}
|
}
|
||||||
|
|
||||||
const record = findProductRecordByBusinessId(targetId)
|
const record = findProductRecordByRecordId(targetId)
|
||||||
if (!record) {
|
if (!record) {
|
||||||
throw createAppError(400, 'cart_product_id 对应产品不存在:' + targetId)
|
throw createAppError(400, 'cart_product_id 必须为 tbl_product_list 的 recordId,且对应产品必须存在:' + targetId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return record
|
return record
|
||||||
@@ -264,7 +277,9 @@ function ensureRecordOwner(record, fieldName, authOpenid, resourceLabel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function exportCartRecord(record, productRecord) {
|
function exportCartRecord(record, productRecord) {
|
||||||
const productInfo = buildProductInfo(productRecord || findProductRecordByBusinessId(record.getString('cart_product_id')))
|
const relationValue = record.getString('cart_product_id')
|
||||||
|
const productSource = productRecord || findProductRecordByRecordId(relationValue)
|
||||||
|
const productInfo = buildProductInfo(productSource)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pb_id: record.id,
|
pb_id: record.id,
|
||||||
@@ -272,7 +287,8 @@ function exportCartRecord(record, productRecord) {
|
|||||||
cart_number: record.getString('cart_number'),
|
cart_number: record.getString('cart_number'),
|
||||||
cart_create: String(record.get('cart_create') || ''),
|
cart_create: String(record.get('cart_create') || ''),
|
||||||
cart_owner: record.getString('cart_owner'),
|
cart_owner: record.getString('cart_owner'),
|
||||||
cart_product_id: record.getString('cart_product_id'),
|
cart_product_id: productSource ? productSource.id : relationValue,
|
||||||
|
cart_product_business_id: productInfo.prod_list_id,
|
||||||
cart_product_quantity: Number(record.get('cart_product_quantity') || 0),
|
cart_product_quantity: Number(record.get('cart_product_quantity') || 0),
|
||||||
cart_status: record.getString('cart_status'),
|
cart_status: record.getString('cart_status'),
|
||||||
cart_at_price: Number(record.get('cart_at_price') || 0),
|
cart_at_price: Number(record.get('cart_at_price') || 0),
|
||||||
@@ -307,7 +323,8 @@ function exportAdminCartRecord(record) {
|
|||||||
const productId = record && typeof record.getString === 'function'
|
const productId = record && typeof record.getString === 'function'
|
||||||
? record.getString('cart_product_id')
|
? record.getString('cart_product_id')
|
||||||
: String(record && record.cart_product_id || '')
|
: String(record && record.cart_product_id || '')
|
||||||
const productInfo = buildProductInfo(findProductRecordByBusinessId(productId))
|
const productSource = findProductRecordByRecordId(productId)
|
||||||
|
const productInfo = buildProductInfo(productSource)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
pb_id: String(record && record.id || ''),
|
pb_id: String(record && record.id || ''),
|
||||||
@@ -315,7 +332,8 @@ function exportAdminCartRecord(record) {
|
|||||||
cart_number: record && typeof record.getString === 'function' ? record.getString('cart_number') : String(record && record.cart_number || ''),
|
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_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_owner: record && typeof record.getString === 'function' ? record.getString('cart_owner') : String(record && record.cart_owner || ''),
|
||||||
cart_product_id: productId,
|
cart_product_id: productSource ? productSource.id : productId,
|
||||||
|
cart_product_business_id: productInfo.prod_list_id,
|
||||||
cart_product_quantity: record && typeof record.get === 'function' ? Number(record.get('cart_product_quantity') || 0) : Number(record && record.cart_product_quantity || 0),
|
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_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_at_price: record && typeof record.get === 'function' ? Number(record.get('cart_at_price') || 0) : Number(record && record.cart_at_price || 0),
|
||||||
@@ -419,6 +437,7 @@ function listCarts(authOpenid, payload) {
|
|||||||
|| item.cart_id.toLowerCase().indexOf(keyword) !== -1
|
|| item.cart_id.toLowerCase().indexOf(keyword) !== -1
|
||||||
|| item.cart_number.toLowerCase().indexOf(keyword) !== -1
|
|| item.cart_number.toLowerCase().indexOf(keyword) !== -1
|
||||||
|| item.cart_product_id.toLowerCase().indexOf(keyword) !== -1
|
|| item.cart_product_id.toLowerCase().indexOf(keyword) !== -1
|
||||||
|
|| normalizeText(item.cart_product_business_id).toLowerCase().indexOf(keyword) !== -1
|
||||||
|| normalizeText(item.product_name).toLowerCase().indexOf(keyword) !== -1
|
|| normalizeText(item.product_name).toLowerCase().indexOf(keyword) !== -1
|
||||||
const matchedStatus = !cartStatus || item.cart_status === cartStatus
|
const matchedStatus = !cartStatus || item.cart_status === cartStatus
|
||||||
const matchedNumber = !cartNumber || item.cart_number === cartNumber
|
const matchedNumber = !cartNumber || item.cart_number === cartNumber
|
||||||
@@ -449,7 +468,7 @@ function createCart(authState, payload) {
|
|||||||
record.set('cart_id', buildBusinessId('CART'))
|
record.set('cart_id', buildBusinessId('CART'))
|
||||||
record.set('cart_number', normalizeText(payload.cart_number) || buildDisplayNumber('CART', authState.authRecord, authState.openid))
|
record.set('cart_number', normalizeText(payload.cart_number) || buildDisplayNumber('CART', authState.authRecord, authState.openid))
|
||||||
record.set('cart_owner', authState.openid)
|
record.set('cart_owner', authState.openid)
|
||||||
record.set('cart_product_id', productRecord.getString('prod_list_id'))
|
record.set('cart_product_id', productRecord.id)
|
||||||
record.set('cart_product_quantity', normalizePositiveIntegerValue(payload.cart_product_quantity, 'cart_product_quantity'))
|
record.set('cart_product_quantity', normalizePositiveIntegerValue(payload.cart_product_quantity, 'cart_product_quantity'))
|
||||||
record.set('cart_status', normalizeCartStatus(payload.cart_status))
|
record.set('cart_status', normalizeCartStatus(payload.cart_status))
|
||||||
record.set('cart_at_price', normalizeNumberValue(payload.cart_at_price, 'cart_at_price'))
|
record.set('cart_at_price', normalizeNumberValue(payload.cart_at_price, 'cart_at_price'))
|
||||||
@@ -486,7 +505,7 @@ function updateCart(authOpenid, payload) {
|
|||||||
}
|
}
|
||||||
if (typeof payload.cart_product_id !== 'undefined') {
|
if (typeof payload.cart_product_id !== 'undefined') {
|
||||||
productRecord = ensureProductExists(payload.cart_product_id)
|
productRecord = ensureProductExists(payload.cart_product_id)
|
||||||
record.set('cart_product_id', productRecord.getString('prod_list_id'))
|
record.set('cart_product_id', productRecord.id)
|
||||||
}
|
}
|
||||||
if (typeof payload.cart_product_quantity !== 'undefined') {
|
if (typeof payload.cart_product_quantity !== 'undefined') {
|
||||||
record.set('cart_product_quantity', normalizePositiveIntegerValue(payload.cart_product_quantity, 'cart_product_quantity'))
|
record.set('cart_product_quantity', normalizePositiveIntegerValue(payload.cart_product_quantity, 'cart_product_quantity'))
|
||||||
|
|||||||
@@ -0,0 +1,755 @@
|
|||||||
|
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
|
||||||
|
const { createAppError } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/appError.js`)
|
||||||
|
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
|
||||||
|
|
||||||
|
const TEMPLATE_COLLECTION = 'tbl_scheme_template'
|
||||||
|
const SCHEME_COLLECTION = 'tbl_scheme'
|
||||||
|
const USER_COLLECTION = 'tbl_auth_users'
|
||||||
|
const PRODUCT_COLLECTION = 'tbl_product_list'
|
||||||
|
|
||||||
|
function tryFindCollection(collectionName) {
|
||||||
|
try {
|
||||||
|
return $app.findCollectionByNameOrId(collectionName)
|
||||||
|
} catch (_error) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureCollectionReady(collectionName) {
|
||||||
|
const collection = tryFindCollection(collectionName)
|
||||||
|
if (!collection) {
|
||||||
|
throw createAppError(400, '集合未初始化:' + collectionName + ',请先执行方案建表脚本')
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBusinessId(prefix) {
|
||||||
|
return prefix + '-' + new Date().getTime() + '-' + $security.randomString(6)
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeText(value) {
|
||||||
|
return String(value || '').replace(/^\s+|\s+$/g, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeDateValue(value) {
|
||||||
|
const text = normalizeText(value)
|
||||||
|
if (!text) return null
|
||||||
|
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}$/.test(text)) {
|
||||||
|
return text + ' 00:00:00.000Z'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}\s/.test(text) || text.indexOf('T') !== -1) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
throw createAppError(400, '日期字段格式错误')
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJsonInput(value, fieldName) {
|
||||||
|
if (value === null || typeof value === 'undefined' || value === '') {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(value))
|
||||||
|
} catch (_error) {
|
||||||
|
throw createAppError(400, fieldName + ' 必须是可序列化的 JSON 数组')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(value))
|
||||||
|
} catch (_error) {
|
||||||
|
throw createAppError(400, fieldName + ' 必须是可序列化的 JSON 对象或数组')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (_error) {
|
||||||
|
throw createAppError(400, fieldName + ' 必须为合法 JSON')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw createAppError(400, fieldName + ' 必须为 JSON')
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseJsonForOutput(value, fallback) {
|
||||||
|
if (value === null || typeof value === 'undefined' || value === '') {
|
||||||
|
return typeof fallback === 'undefined' ? [] : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(value))
|
||||||
|
} catch (_error) {
|
||||||
|
return typeof fallback === 'undefined' ? [] : fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
try {
|
||||||
|
return JSON.parse(JSON.stringify(value))
|
||||||
|
} catch (_error) {
|
||||||
|
return typeof fallback === 'undefined' ? [] : fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
try {
|
||||||
|
return JSON.parse(value)
|
||||||
|
} catch (_error) {
|
||||||
|
return typeof fallback === 'undefined' ? [] : fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return typeof fallback === 'undefined' ? [] : fallback
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserRecordByOpenid(openid) {
|
||||||
|
const value = normalizeText(openid)
|
||||||
|
if (!value) return null
|
||||||
|
if (!tryFindCollection(USER_COLLECTION)) return null
|
||||||
|
|
||||||
|
const records = $app.findRecordsByFilter(USER_COLLECTION, 'openid = {:openid}', '', 1, 0, {
|
||||||
|
openid: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!records.length) return null
|
||||||
|
if (Number(records[0].get('is_delete') || 0) === 1) return null
|
||||||
|
return records[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureUserExists(openid, fieldName) {
|
||||||
|
const value = normalizeText(openid)
|
||||||
|
if (!value) {
|
||||||
|
throw createAppError(400, fieldName + ' 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = getUserRecordByOpenid(value)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(400, fieldName + ' 对应用户不存在:' + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return record
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureAttachmentExists(attachmentId, fieldName) {
|
||||||
|
const value = normalizeText(attachmentId)
|
||||||
|
if (!value) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
documentService.getAttachmentDetail(value)
|
||||||
|
} catch (_error) {
|
||||||
|
throw createAppError(400, fieldName + ' 对应附件不存在:' + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProductRecordById(productId) {
|
||||||
|
const value = normalizeText(productId)
|
||||||
|
if (!value) return null
|
||||||
|
if (!tryFindCollection(PRODUCT_COLLECTION)) return null
|
||||||
|
|
||||||
|
const records = $app.findRecordsByFilter(PRODUCT_COLLECTION, 'prod_list_id = {:productId}', '', 1, 0, {
|
||||||
|
productId: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!records.length) return null
|
||||||
|
if (Number(records[0].get('is_delete') || 0) === 1) return null
|
||||||
|
return records[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureProductExists(productId, fieldName) {
|
||||||
|
const value = normalizeText(productId)
|
||||||
|
if (!value) {
|
||||||
|
throw createAppError(400, fieldName + ' 不能为空')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = getProductRecordById(value)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(400, fieldName + ' 对应产品不存在:' + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return record
|
||||||
|
}
|
||||||
|
|
||||||
|
function findTemplateRecordByTemplateId(templateId) {
|
||||||
|
const value = normalizeText(templateId)
|
||||||
|
if (!value) return null
|
||||||
|
if (!tryFindCollection(TEMPLATE_COLLECTION)) return null
|
||||||
|
|
||||||
|
const records = $app.findRecordsByFilter(TEMPLATE_COLLECTION, 'scheme_template_id = {:templateId}', '', 1, 0, {
|
||||||
|
templateId: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!records.length) return null
|
||||||
|
if (Number(records[0].get('is_delete') || 0) === 1) return null
|
||||||
|
return records[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTemplateExists(templateId, fieldName) {
|
||||||
|
const value = normalizeText(templateId)
|
||||||
|
if (!value) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = findTemplateRecordByTemplateId(value)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(400, fieldName + ' 对应模板不存在:' + value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return record
|
||||||
|
}
|
||||||
|
|
||||||
|
function findSchemeRecordBySchemeId(schemeId) {
|
||||||
|
const value = normalizeText(schemeId)
|
||||||
|
if (!value) return null
|
||||||
|
if (!tryFindCollection(SCHEME_COLLECTION)) return null
|
||||||
|
|
||||||
|
const records = $app.findRecordsByFilter(SCHEME_COLLECTION, 'scheme_id = {:schemeId}', '', 1, 0, {
|
||||||
|
schemeId: value,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!records.length) return null
|
||||||
|
if (Number(records[0].get('is_delete') || 0) === 1) return null
|
||||||
|
return records[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeTemplateProductList(value) {
|
||||||
|
const parsed = parseJsonInput(value, 'scheme_template_product_list')
|
||||||
|
if (!Array.isArray(parsed)) {
|
||||||
|
throw createAppError(400, 'scheme_template_product_list 必须为 JSON 数组')
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < parsed.length; i += 1) {
|
||||||
|
const item = parsed[i] && typeof parsed[i] === 'object' ? parsed[i] : null
|
||||||
|
if (!item) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const productId = normalizeText(item.product_id)
|
||||||
|
if (!productId) {
|
||||||
|
throw createAppError(400, 'scheme_template_product_list 第 ' + (i + 1) + ' 项 product_id 为必填项')
|
||||||
|
}
|
||||||
|
ensureProductExists(productId, 'scheme_template_product_list.product_id')
|
||||||
|
|
||||||
|
const qty = Number(item.qty)
|
||||||
|
if (!Number.isFinite(qty) || qty <= 0) {
|
||||||
|
throw createAppError(400, 'scheme_template_product_list 第 ' + (i + 1) + ' 项 qty 必须为正数')
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
product_id: productId,
|
||||||
|
qty: qty,
|
||||||
|
note: normalizeText(item.note),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSchemeRoomType(value) {
|
||||||
|
const parsed = parseJsonInput(value, 'scheme_room_type')
|
||||||
|
if (!Array.isArray(parsed)) {
|
||||||
|
throw createAppError(400, 'scheme_room_type 必须为 JSON 数组')
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = []
|
||||||
|
for (let i = 0; i < parsed.length; i += 1) {
|
||||||
|
const item = parsed[i] && typeof parsed[i] === 'object' ? parsed[i] : null
|
||||||
|
if (!item) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const roomType = normalizeText(item.room_type || item.name)
|
||||||
|
if (!roomType) {
|
||||||
|
throw createAppError(400, 'scheme_room_type 第 ' + (i + 1) + ' 项 room_type 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
const qty = Number(item.qty)
|
||||||
|
if (!Number.isFinite(qty) || qty <= 0) {
|
||||||
|
throw createAppError(400, 'scheme_room_type 第 ' + (i + 1) + ' 项 qty 必须为正数')
|
||||||
|
}
|
||||||
|
|
||||||
|
result.push({
|
||||||
|
room_type: roomType,
|
||||||
|
qty: qty,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveAttachment(attachmentId) {
|
||||||
|
const value = normalizeText(attachmentId)
|
||||||
|
if (!value) {
|
||||||
|
return {
|
||||||
|
id: '',
|
||||||
|
url: '',
|
||||||
|
attachment: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const attachment = documentService.getAttachmentDetail(value)
|
||||||
|
return {
|
||||||
|
id: value,
|
||||||
|
url: attachment ? attachment.attachments_url : '',
|
||||||
|
attachment: attachment,
|
||||||
|
}
|
||||||
|
} catch (_error) {
|
||||||
|
return {
|
||||||
|
id: value,
|
||||||
|
url: '',
|
||||||
|
attachment: null,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTemplateSummaryMap() {
|
||||||
|
if (!tryFindCollection(TEMPLATE_COLLECTION)) {
|
||||||
|
logger.warn('方案模板集合未初始化,方案模板摘要映射将返回空结果', {
|
||||||
|
collection: TEMPLATE_COLLECTION,
|
||||||
|
})
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = $app.findRecordsByFilter(TEMPLATE_COLLECTION, 'is_delete = 0', '-updated', 1000, 0)
|
||||||
|
const map = {}
|
||||||
|
|
||||||
|
for (let i = 0; i < records.length; i += 1) {
|
||||||
|
const templateId = normalizeText(records[i].getString('scheme_template_id'))
|
||||||
|
if (!templateId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
map[templateId] = {
|
||||||
|
scheme_template_id: templateId,
|
||||||
|
scheme_template_name: records[i].getString('scheme_template_name'),
|
||||||
|
scheme_template_owner: records[i].getString('scheme_template_owner'),
|
||||||
|
scheme_template_status: records[i].getString('scheme_template_status'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportTemplateRecord(record) {
|
||||||
|
const icon = resolveAttachment(record.getString('scheme_template_icon'))
|
||||||
|
|
||||||
|
return {
|
||||||
|
pb_id: record.id,
|
||||||
|
scheme_template_id: record.getString('scheme_template_id'),
|
||||||
|
scheme_template_icon: icon.id,
|
||||||
|
scheme_template_icon_url: icon.url,
|
||||||
|
scheme_template_icon_attachment: icon.attachment,
|
||||||
|
scheme_template_label: record.getString('scheme_template_label'),
|
||||||
|
scheme_template_name: record.getString('scheme_template_name'),
|
||||||
|
scheme_template_owner: record.getString('scheme_template_owner'),
|
||||||
|
scheme_template_status: record.getString('scheme_template_status'),
|
||||||
|
scheme_template_solution_type: record.getString('scheme_template_solution_type'),
|
||||||
|
scheme_template_solution_feature: record.getString('scheme_template_solution_feature'),
|
||||||
|
scheme_template_product_list: parseJsonForOutput(record.get('scheme_template_product_list'), []),
|
||||||
|
scheme_template_description: record.getString('scheme_template_description'),
|
||||||
|
scheme_template_remark: record.getString('scheme_template_remark'),
|
||||||
|
is_delete: Number(record.get('is_delete') || 0),
|
||||||
|
created: String(record.created || ''),
|
||||||
|
updated: String(record.updated || ''),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportSchemeRecord(record, templateMap) {
|
||||||
|
const highendId = record.getString('scheme_template_highend')
|
||||||
|
const midendId = record.getString('scheme_template_midend')
|
||||||
|
const lowendId = record.getString('scheme_template_lowend')
|
||||||
|
const templates = templateMap || {}
|
||||||
|
|
||||||
|
return {
|
||||||
|
pb_id: record.id,
|
||||||
|
scheme_id: record.getString('scheme_id'),
|
||||||
|
scheme_name: record.getString('scheme_name'),
|
||||||
|
scheme_owner: record.getString('scheme_owner'),
|
||||||
|
scheme_share_status: record.getString('scheme_share_status'),
|
||||||
|
scheme_expires_at: String(record.get('scheme_expires_at') || ''),
|
||||||
|
scheme_hotel_type: record.getString('scheme_hotel_type'),
|
||||||
|
scheme_solution_type: record.getString('scheme_solution_type'),
|
||||||
|
scheme_solution_feature: record.getString('scheme_solution_feature'),
|
||||||
|
scheme_room_type: parseJsonForOutput(record.get('scheme_room_type'), []),
|
||||||
|
scheme_curtains: record.getString('scheme_curtains'),
|
||||||
|
scheme_voice_device: record.getString('scheme_voice_device'),
|
||||||
|
scheme_ac_type: record.getString('scheme_ac_type'),
|
||||||
|
scheme_template_highend: highendId,
|
||||||
|
scheme_template_highend_name: templates[highendId] ? templates[highendId].scheme_template_name : '',
|
||||||
|
scheme_template_midend: midendId,
|
||||||
|
scheme_template_midend_name: templates[midendId] ? templates[midendId].scheme_template_name : '',
|
||||||
|
scheme_template_lowend: lowendId,
|
||||||
|
scheme_template_lowend_name: templates[lowendId] ? templates[lowendId].scheme_template_name : '',
|
||||||
|
scheme_status: record.getString('scheme_status'),
|
||||||
|
scheme_remark: record.getString('scheme_remark'),
|
||||||
|
is_delete: Number(record.get('is_delete') || 0),
|
||||||
|
created: String(record.created || ''),
|
||||||
|
updated: String(record.updated || ''),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyTemplatePayload(record, payload) {
|
||||||
|
ensureUserExists(payload.scheme_template_owner, 'scheme_template_owner')
|
||||||
|
|
||||||
|
record.set('scheme_template_name', normalizeText(payload.scheme_template_name))
|
||||||
|
record.set('scheme_template_owner', normalizeText(payload.scheme_template_owner))
|
||||||
|
record.set('scheme_template_icon', ensureAttachmentExists(payload.scheme_template_icon, 'scheme_template_icon'))
|
||||||
|
record.set('scheme_template_label', normalizeText(payload.scheme_template_label))
|
||||||
|
record.set('scheme_template_status', normalizeText(payload.scheme_template_status))
|
||||||
|
record.set('scheme_template_solution_type', normalizeText(payload.scheme_template_solution_type))
|
||||||
|
record.set('scheme_template_solution_feature', normalizeText(payload.scheme_template_solution_feature))
|
||||||
|
record.set('scheme_template_product_list', normalizeTemplateProductList(payload.scheme_template_product_list))
|
||||||
|
record.set('scheme_template_description', normalizeText(payload.scheme_template_description))
|
||||||
|
record.set('scheme_template_remark', normalizeText(payload.scheme_template_remark))
|
||||||
|
}
|
||||||
|
|
||||||
|
function applySchemePayload(record, payload) {
|
||||||
|
ensureUserExists(payload.scheme_owner, 'scheme_owner')
|
||||||
|
ensureTemplateExists(payload.scheme_template_highend, 'scheme_template_highend')
|
||||||
|
ensureTemplateExists(payload.scheme_template_midend, 'scheme_template_midend')
|
||||||
|
ensureTemplateExists(payload.scheme_template_lowend, 'scheme_template_lowend')
|
||||||
|
|
||||||
|
record.set('scheme_name', normalizeText(payload.scheme_name))
|
||||||
|
record.set('scheme_owner', normalizeText(payload.scheme_owner))
|
||||||
|
record.set('scheme_share_status', normalizeText(payload.scheme_share_status))
|
||||||
|
record.set('scheme_expires_at', normalizeDateValue(payload.scheme_expires_at))
|
||||||
|
record.set('scheme_hotel_type', normalizeText(payload.scheme_hotel_type))
|
||||||
|
record.set('scheme_solution_type', normalizeText(payload.scheme_solution_type))
|
||||||
|
record.set('scheme_solution_feature', normalizeText(payload.scheme_solution_feature))
|
||||||
|
record.set('scheme_room_type', normalizeSchemeRoomType(payload.scheme_room_type))
|
||||||
|
record.set('scheme_curtains', normalizeText(payload.scheme_curtains))
|
||||||
|
record.set('scheme_voice_device', normalizeText(payload.scheme_voice_device))
|
||||||
|
record.set('scheme_ac_type', normalizeText(payload.scheme_ac_type))
|
||||||
|
record.set('scheme_template_highend', normalizeText(payload.scheme_template_highend))
|
||||||
|
record.set('scheme_template_midend', normalizeText(payload.scheme_template_midend))
|
||||||
|
record.set('scheme_template_lowend', normalizeText(payload.scheme_template_lowend))
|
||||||
|
record.set('scheme_status', normalizeText(payload.scheme_status))
|
||||||
|
record.set('scheme_remark', normalizeText(payload.scheme_remark))
|
||||||
|
}
|
||||||
|
|
||||||
|
function listSchemeTemplates(payload) {
|
||||||
|
if (!tryFindCollection(TEMPLATE_COLLECTION)) {
|
||||||
|
logger.warn('方案模板集合未初始化,模板列表返回空结果', {
|
||||||
|
collection: TEMPLATE_COLLECTION,
|
||||||
|
})
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = $app.findRecordsByFilter(TEMPLATE_COLLECTION, 'is_delete = 0', '-updated', 1000, 0)
|
||||||
|
const keyword = normalizeText(payload.keyword).toLowerCase()
|
||||||
|
const owner = normalizeText(payload.scheme_template_owner)
|
||||||
|
const status = normalizeText(payload.scheme_template_status)
|
||||||
|
const solutionType = normalizeText(payload.scheme_template_solution_type)
|
||||||
|
const solutionFeature = normalizeText(payload.scheme_template_solution_feature)
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
for (let i = 0; i < records.length; i += 1) {
|
||||||
|
const item = exportTemplateRecord(records[i])
|
||||||
|
const matchedKeyword = !keyword
|
||||||
|
|| normalizeText(item.scheme_template_id).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
|| normalizeText(item.scheme_template_name).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
|| normalizeText(item.scheme_template_label).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
|| normalizeText(item.scheme_template_owner).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
const matchedOwner = !owner || item.scheme_template_owner === owner
|
||||||
|
const matchedStatus = !status || item.scheme_template_status === status
|
||||||
|
const matchedSolutionType = !solutionType || item.scheme_template_solution_type === solutionType
|
||||||
|
const matchedSolutionFeature = !solutionFeature || item.scheme_template_solution_feature === solutionFeature
|
||||||
|
|
||||||
|
if (matchedKeyword && matchedOwner && matchedStatus && matchedSolutionType && matchedSolutionFeature) {
|
||||||
|
result.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSchemeTemplateDetail(templateId) {
|
||||||
|
ensureCollectionReady(TEMPLATE_COLLECTION)
|
||||||
|
|
||||||
|
const record = findTemplateRecordByTemplateId(templateId)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(404, '未找到对应方案模板')
|
||||||
|
}
|
||||||
|
|
||||||
|
return exportTemplateRecord(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSchemeTemplate(userOpenid, payload) {
|
||||||
|
const collection = ensureCollectionReady(TEMPLATE_COLLECTION)
|
||||||
|
const templateId = normalizeText(payload.scheme_template_id) || buildBusinessId('SCHTPL')
|
||||||
|
if (findTemplateRecordByTemplateId(templateId)) {
|
||||||
|
throw createAppError(400, 'scheme_template_id 已存在')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = new Record(collection)
|
||||||
|
|
||||||
|
record.set('scheme_template_id', templateId)
|
||||||
|
record.set('is_delete', 0)
|
||||||
|
applyTemplatePayload(record, payload)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app.save(record)
|
||||||
|
} catch (err) {
|
||||||
|
throw createAppError(400, '新增方案模板失败', {
|
||||||
|
originalMessage: (err && err.message) || '未知错误',
|
||||||
|
originalData: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('方案模板创建成功', {
|
||||||
|
scheme_template_id: templateId,
|
||||||
|
operator_openid: userOpenid || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return exportTemplateRecord(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSchemeTemplate(userOpenid, payload) {
|
||||||
|
ensureCollectionReady(TEMPLATE_COLLECTION)
|
||||||
|
|
||||||
|
const templateId = normalizeText(payload.scheme_template_id)
|
||||||
|
if (!templateId) {
|
||||||
|
throw createAppError(400, 'scheme_template_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = findTemplateRecordByTemplateId(templateId)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(404, '未找到待修改的方案模板')
|
||||||
|
}
|
||||||
|
|
||||||
|
applyTemplatePayload(record, payload)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app.save(record)
|
||||||
|
} catch (err) {
|
||||||
|
throw createAppError(400, '更新方案模板失败', {
|
||||||
|
originalMessage: (err && err.message) || '未知错误',
|
||||||
|
originalData: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('方案模板更新成功', {
|
||||||
|
scheme_template_id: templateId,
|
||||||
|
operator_openid: userOpenid || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return exportTemplateRecord(record)
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteSchemeTemplate(userOpenid, templateId) {
|
||||||
|
ensureCollectionReady(TEMPLATE_COLLECTION)
|
||||||
|
ensureCollectionReady(SCHEME_COLLECTION)
|
||||||
|
|
||||||
|
const value = normalizeText(templateId)
|
||||||
|
if (!value) {
|
||||||
|
throw createAppError(400, 'scheme_template_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = findTemplateRecordByTemplateId(value)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(404, '未找到待删除的方案模板')
|
||||||
|
}
|
||||||
|
|
||||||
|
const usedSchemes = $app.findRecordsByFilter(
|
||||||
|
SCHEME_COLLECTION,
|
||||||
|
'is_delete = 0 && (scheme_template_highend = {:templateId} || scheme_template_midend = {:templateId} || scheme_template_lowend = {:templateId})',
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
{ templateId: value }
|
||||||
|
)
|
||||||
|
|
||||||
|
if (usedSchemes.length) {
|
||||||
|
throw createAppError(400, '方案模板已被方案引用,无法删除')
|
||||||
|
}
|
||||||
|
|
||||||
|
record.set('is_delete', 1)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app.save(record)
|
||||||
|
} catch (err) {
|
||||||
|
throw createAppError(400, '删除方案模板失败', {
|
||||||
|
originalMessage: (err && err.message) || '未知错误',
|
||||||
|
originalData: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('方案模板删除成功', {
|
||||||
|
scheme_template_id: value,
|
||||||
|
operator_openid: userOpenid || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheme_template_id: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function listSchemes(payload) {
|
||||||
|
if (!tryFindCollection(SCHEME_COLLECTION)) {
|
||||||
|
logger.warn('方案集合未初始化,方案列表返回空结果', {
|
||||||
|
collection: SCHEME_COLLECTION,
|
||||||
|
})
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const templateMap = buildTemplateSummaryMap()
|
||||||
|
const records = $app.findRecordsByFilter(SCHEME_COLLECTION, 'is_delete = 0', '-updated', 1000, 0)
|
||||||
|
const keyword = normalizeText(payload.keyword).toLowerCase()
|
||||||
|
const owner = normalizeText(payload.scheme_owner)
|
||||||
|
const status = normalizeText(payload.scheme_status)
|
||||||
|
const shareStatus = normalizeText(payload.scheme_share_status)
|
||||||
|
const hotelType = normalizeText(payload.scheme_hotel_type)
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
for (let i = 0; i < records.length; i += 1) {
|
||||||
|
const item = exportSchemeRecord(records[i], templateMap)
|
||||||
|
const matchedKeyword = !keyword
|
||||||
|
|| normalizeText(item.scheme_id).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
|| normalizeText(item.scheme_name).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
|| normalizeText(item.scheme_owner).toLowerCase().indexOf(keyword) !== -1
|
||||||
|
const matchedOwner = !owner || item.scheme_owner === owner
|
||||||
|
const matchedStatus = !status || item.scheme_status === status
|
||||||
|
const matchedShareStatus = !shareStatus || item.scheme_share_status === shareStatus
|
||||||
|
const matchedHotelType = !hotelType || item.scheme_hotel_type === hotelType
|
||||||
|
|
||||||
|
if (matchedKeyword && matchedOwner && matchedStatus && matchedShareStatus && matchedHotelType) {
|
||||||
|
result.push(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSchemeDetail(schemeId) {
|
||||||
|
ensureCollectionReady(SCHEME_COLLECTION)
|
||||||
|
|
||||||
|
const record = findSchemeRecordBySchemeId(schemeId)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(404, '未找到对应方案')
|
||||||
|
}
|
||||||
|
|
||||||
|
return exportSchemeRecord(record, buildTemplateSummaryMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
function createScheme(userOpenid, payload) {
|
||||||
|
const collection = ensureCollectionReady(SCHEME_COLLECTION)
|
||||||
|
const schemeId = normalizeText(payload.scheme_id) || buildBusinessId('SCHEME')
|
||||||
|
if (findSchemeRecordBySchemeId(schemeId)) {
|
||||||
|
throw createAppError(400, 'scheme_id 已存在')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = new Record(collection)
|
||||||
|
|
||||||
|
record.set('scheme_id', schemeId)
|
||||||
|
record.set('is_delete', 0)
|
||||||
|
applySchemePayload(record, payload)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app.save(record)
|
||||||
|
} catch (err) {
|
||||||
|
throw createAppError(400, '新增方案失败', {
|
||||||
|
originalMessage: (err && err.message) || '未知错误',
|
||||||
|
originalData: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('方案创建成功', {
|
||||||
|
scheme_id: schemeId,
|
||||||
|
operator_openid: userOpenid || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return exportSchemeRecord(record, buildTemplateSummaryMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateScheme(userOpenid, payload) {
|
||||||
|
ensureCollectionReady(SCHEME_COLLECTION)
|
||||||
|
|
||||||
|
const schemeId = normalizeText(payload.scheme_id)
|
||||||
|
if (!schemeId) {
|
||||||
|
throw createAppError(400, 'scheme_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = findSchemeRecordBySchemeId(schemeId)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(404, '未找到待修改的方案')
|
||||||
|
}
|
||||||
|
|
||||||
|
applySchemePayload(record, payload)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app.save(record)
|
||||||
|
} catch (err) {
|
||||||
|
throw createAppError(400, '更新方案失败', {
|
||||||
|
originalMessage: (err && err.message) || '未知错误',
|
||||||
|
originalData: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('方案更新成功', {
|
||||||
|
scheme_id: schemeId,
|
||||||
|
operator_openid: userOpenid || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return exportSchemeRecord(record, buildTemplateSummaryMap())
|
||||||
|
}
|
||||||
|
|
||||||
|
function deleteScheme(userOpenid, schemeId) {
|
||||||
|
ensureCollectionReady(SCHEME_COLLECTION)
|
||||||
|
|
||||||
|
const value = normalizeText(schemeId)
|
||||||
|
if (!value) {
|
||||||
|
throw createAppError(400, 'scheme_id 为必填项')
|
||||||
|
}
|
||||||
|
|
||||||
|
const record = findSchemeRecordBySchemeId(value)
|
||||||
|
if (!record) {
|
||||||
|
throw createAppError(404, '未找到待删除的方案')
|
||||||
|
}
|
||||||
|
|
||||||
|
record.set('is_delete', 1)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$app.save(record)
|
||||||
|
} catch (err) {
|
||||||
|
throw createAppError(400, '删除方案失败', {
|
||||||
|
originalMessage: (err && err.message) || '未知错误',
|
||||||
|
originalData: (err && err.data) || {},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info('方案删除成功', {
|
||||||
|
scheme_id: value,
|
||||||
|
operator_openid: userOpenid || '',
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
scheme_id: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
listSchemeTemplates,
|
||||||
|
getSchemeTemplateDetail,
|
||||||
|
createSchemeTemplate,
|
||||||
|
updateSchemeTemplate,
|
||||||
|
deleteSchemeTemplate,
|
||||||
|
listSchemes,
|
||||||
|
getSchemeDetail,
|
||||||
|
createScheme,
|
||||||
|
updateScheme,
|
||||||
|
deleteScheme,
|
||||||
|
}
|
||||||
9
pocket-base/bai_web_pb_hooks/pages/scheme-manage.js
Normal file
9
pocket-base/bai_web_pb_hooks/pages/scheme-manage.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
routerAdd('GET', '/manage/scheme', function (e) {
|
||||||
|
const html = $template.loadFiles(
|
||||||
|
__hooks + '/bai_web_pb_hooks/views/scheme-manage.html',
|
||||||
|
__hooks + '/bai_web_pb_hooks/shared/theme-head.html',
|
||||||
|
__hooks + '/bai_web_pb_hooks/shared/theme-body.html'
|
||||||
|
).render({})
|
||||||
|
|
||||||
|
return e.html(200, html)
|
||||||
|
})
|
||||||
@@ -46,25 +46,43 @@
|
|||||||
.summary-label { color: #64748b; font-size: 13px; }
|
.summary-label { color: #64748b; font-size: 13px; }
|
||||||
.summary-value { margin-top: 8px; font-size: 22px; font-weight: 700; color: #0f172a; }
|
.summary-value { margin-top: 8px; font-size: 22px; font-weight: 700; color: #0f172a; }
|
||||||
.profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
|
.profile-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
|
||||||
.preview-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
|
.preview-form { border: 1px solid #dbe3f0; border-radius: 14px; background: #f8fbff; overflow: hidden; margin-bottom: 10px; }
|
||||||
|
.preview-form-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 10px 12px; }
|
||||||
|
.preview-form-summary { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; flex: 1; min-width: 0; }
|
||||||
|
.preview-summary-item { min-width: 0; }
|
||||||
|
.preview-summary-label { font-size: 11px; color: #64748b; margin-bottom: 2px; }
|
||||||
|
.preview-summary-value { font-size: 13px; font-weight: 700; color: #0f172a; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||||
|
.preview-toggle { min-width: 88px; }
|
||||||
|
.preview-form-body { display: grid; grid-template-rows: 0fr; opacity: 0; transition: grid-template-rows 220ms ease, opacity 220ms ease; }
|
||||||
|
.preview-form-body.open { grid-template-rows: 1fr; opacity: 1; }
|
||||||
|
.preview-form-inner { min-height: 0; overflow: hidden; padding: 0 12px 0; }
|
||||||
|
.preview-form-body.open .preview-form-inner { padding-bottom: 12px; }
|
||||||
|
.preview-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; padding-top: 2px; }
|
||||||
.field-block { display: grid; gap: 6px; }
|
.field-block { display: grid; gap: 6px; }
|
||||||
.field-block.full { grid-column: 1 / -1; }
|
.field-block.full { grid-column: 1 / -1; }
|
||||||
.field-label { font-size: 13px; color: #64748b; }
|
.field-label { font-size: 12px; color: #64748b; }
|
||||||
.preview-card { border: 1px solid #dbe3f0; border-radius: 14px; padding: 10px 12px; background: #f8fbff; min-width: 0; }
|
.preview-card { border: 1px solid #dbe3f0; border-radius: 12px; padding: 8px 10px; background: rgba(255,255,255,0.65); min-width: 0; display: grid; gap: 3px; }
|
||||||
.preview-value { color: #0f172a; font-size: 14px; font-weight: 600; line-height: 1.5; word-break: break-word; }
|
.preview-value { color: #0f172a; font-size: 13px; font-weight: 700; line-height: 1.35; word-break: break-word; }
|
||||||
.user-profile-shell { display: grid; gap: 12px; }
|
.user-profile-shell { display: grid; gap: 12px; }
|
||||||
.edit-panel { border-top: 1px dashed #dbe3f0; padding-top: 14px; }
|
.profile-toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 10px; }
|
||||||
.attachment-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 14px; }
|
.edit-hint { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 999px; font-size: 12px; font-weight: 700; }
|
||||||
|
.edit-hint.clean { background: #ecfdf5; color: #047857; border: 1px solid #a7f3d0; }
|
||||||
|
.edit-hint.dirty { background: #fff7ed; color: #c2410c; border: 1px solid #fdba74; }
|
||||||
|
.edit-panel { border-top: 1px dashed #dbe3f0; display: grid; grid-template-rows: 0fr; opacity: 0; transform: translateY(-4px); transition: grid-template-rows 220ms ease, opacity 220ms ease, transform 220ms ease, padding-top 220ms ease, margin-top 220ms ease; padding-top: 0; margin-top: 0; overflow: hidden; }
|
||||||
|
.edit-panel.open { grid-template-rows: 1fr; opacity: 1; transform: translateY(0); padding-top: 14px; margin-top: 2px; }
|
||||||
|
.edit-panel-inner { min-height: 0; overflow: hidden; }
|
||||||
|
.attachment-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; margin-bottom: 12px; }
|
||||||
|
.attachment-grid.editing { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
.attachment-panel { border: 1px solid #dbe3f0; border-radius: 14px; padding: 12px; background: #f8fbff; }
|
.attachment-panel { border: 1px solid #dbe3f0; border-radius: 14px; padding: 12px; background: #f8fbff; }
|
||||||
.attachment-panel.compact { padding: 10px; min-width: 0; }
|
.attachment-panel.compact { padding: 10px; min-width: 0; }
|
||||||
.attachment-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
|
.attachment-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
|
||||||
.attachment-actions .btn { padding: 8px 12px; font-size: 13px; }
|
.attachment-actions .btn { padding: 8px 12px; font-size: 13px; }
|
||||||
.attachment-preview { margin-top: 10px; display: flex; align-items: center; gap: 12px; min-height: 72px; }
|
.attachment-preview { margin-top: 8px; display: flex; align-items: center; gap: 10px; min-height: 60px; }
|
||||||
.attachment-thumb { width: 72px; height: 72px; border-radius: 12px; border: 1px solid #dbe3f0; background: #fff; object-fit: cover; }
|
.attachment-thumb { width: 60px; height: 60px; border-radius: 10px; 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-empty-thumb { width: 60px; height: 60px; border-radius: 10px; border: 1px solid #dbe3f0; background: #fff; color: #94a3b8; display: flex; align-items: center; justify-content: center; font-size: 11px; text-align: center; padding: 6px; }
|
||||||
.attachment-meta { min-width: 0; display: grid; gap: 4px; }
|
.attachment-meta { min-width: 0; display: grid; gap: 4px; }
|
||||||
.attachment-meta.compact { gap: 2px; }
|
.attachment-meta.compact { gap: 2px; }
|
||||||
.attachment-link { color: #2563eb; text-decoration: none; font-size: 13px; font-weight: 600; word-break: break-all; }
|
.attachment-link { color: #2563eb; text-decoration: none; font-size: 12px; font-weight: 600; word-break: break-all; }
|
||||||
.attachment-hidden-input { display: none; }
|
.attachment-hidden-input { display: none; }
|
||||||
.detail-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
|
.detail-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
|
||||||
.section + .section { margin-top: 14px; }
|
.section + .section { margin-top: 14px; }
|
||||||
@@ -78,21 +96,27 @@
|
|||||||
@media (max-width: 1080px) {
|
@media (max-width: 1080px) {
|
||||||
.layout { grid-template-columns: 1fr; }
|
.layout { grid-template-columns: 1fr; }
|
||||||
.summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
.preview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.preview-form-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.preview-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
.attachment-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.attachment-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
.attachment-grid.editing { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
.profile-grid { grid-template-columns: 1fr; }
|
.profile-grid { grid-template-columns: 1fr; }
|
||||||
}
|
}
|
||||||
@media (max-width: 720px) {
|
@media (max-width: 720px) {
|
||||||
.toolbar { grid-template-columns: 1fr; }
|
.toolbar { grid-template-columns: 1fr; }
|
||||||
.summary-grid { grid-template-columns: 1fr; }
|
.summary-grid { grid-template-columns: 1fr; }
|
||||||
.preview-grid { grid-template-columns: 1fr; }
|
.preview-form-head { flex-direction: column; align-items: stretch; }
|
||||||
|
.preview-form-summary { grid-template-columns: 1fr; }
|
||||||
|
.preview-fields { grid-template-columns: 1fr; }
|
||||||
.attachment-grid { grid-template-columns: 1fr; }
|
.attachment-grid { grid-template-columns: 1fr; }
|
||||||
|
.attachment-grid.editing { grid-template-columns: 1fr; }
|
||||||
table, thead, tbody, th, td, tr { display: block; }
|
table, thead, tbody, th, td, tr { display: block; }
|
||||||
thead { display: none; }
|
thead { display: none; }
|
||||||
tr { border-bottom: 1px solid #e5e7eb; }
|
tr { border-bottom: 1px solid #e5e7eb; }
|
||||||
td { display: flex; justify-content: space-between; gap: 10px; }
|
td { display: flex; justify-content: space-between; gap: 10px; }
|
||||||
td::before { content: attr(data-label); font-weight: 700; color: #475569; }
|
td::before { content: attr(data-label); font-weight: 700; color: #475569; }
|
||||||
}
|
}
|
||||||
|
html[data-theme="dark"] .preview-form,
|
||||||
html[data-theme="dark"] .preview-card,
|
html[data-theme="dark"] .preview-card,
|
||||||
html[data-theme="dark"] .attachment-panel {
|
html[data-theme="dark"] .attachment-panel {
|
||||||
background: rgba(0, 0, 0, 0.88) !important;
|
background: rgba(0, 0, 0, 0.88) !important;
|
||||||
@@ -100,6 +124,9 @@
|
|||||||
color: #e5e7eb !important;
|
color: #e5e7eb !important;
|
||||||
box-shadow: 0 18px 50px rgba(2, 6, 23, 0.18) !important;
|
box-shadow: 0 18px 50px rgba(2, 6, 23, 0.18) !important;
|
||||||
}
|
}
|
||||||
|
html[data-theme="dark"] .preview-summary-value {
|
||||||
|
color: #f8fafc !important;
|
||||||
|
}
|
||||||
html[data-theme="dark"] .preview-value,
|
html[data-theme="dark"] .preview-value,
|
||||||
html[data-theme="dark"] .attachment-link {
|
html[data-theme="dark"] .attachment-link {
|
||||||
color: #f8fafc !important;
|
color: #f8fafc !important;
|
||||||
@@ -113,6 +140,16 @@
|
|||||||
html[data-theme="dark"] .edit-panel {
|
html[data-theme="dark"] .edit-panel {
|
||||||
border-top-color: rgba(148, 163, 184, 0.22) !important;
|
border-top-color: rgba(148, 163, 184, 0.22) !important;
|
||||||
}
|
}
|
||||||
|
html[data-theme="dark"] .edit-hint.clean {
|
||||||
|
background: rgba(16, 185, 129, 0.14) !important;
|
||||||
|
color: #86efac !important;
|
||||||
|
border-color: rgba(110, 231, 183, 0.28) !important;
|
||||||
|
}
|
||||||
|
html[data-theme="dark"] .edit-hint.dirty {
|
||||||
|
background: rgba(249, 115, 22, 0.14) !important;
|
||||||
|
color: #fdba74 !important;
|
||||||
|
border-color: rgba(251, 146, 60, 0.28) !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
{{ template "theme_head" . }}
|
{{ template "theme_head" . }}
|
||||||
</head>
|
</head>
|
||||||
@@ -154,6 +191,7 @@
|
|||||||
const state = {
|
const state = {
|
||||||
users: [],
|
users: [],
|
||||||
selectedOpenid: '',
|
selectedOpenid: '',
|
||||||
|
previewExpanded: false,
|
||||||
isEditMode: false,
|
isEditMode: false,
|
||||||
userEditDraft: null,
|
userEditDraft: null,
|
||||||
userLevelOptions: [],
|
userLevelOptions: [],
|
||||||
@@ -258,6 +296,40 @@
|
|||||||
return state.userEditDraft
|
return state.userEditDraft
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeDraftSnapshot(source) {
|
||||||
|
const current = source || {}
|
||||||
|
return {
|
||||||
|
openid: normalizeText(current.openid),
|
||||||
|
users_name: normalizeText(current.users_name),
|
||||||
|
users_phone: normalizeText(current.users_phone),
|
||||||
|
users_level: normalizeText(current.users_level),
|
||||||
|
users_type: normalizeText(current.users_type),
|
||||||
|
users_status: normalizeText(current.users_status),
|
||||||
|
users_rank_level: normalizeText(current.users_rank_level),
|
||||||
|
users_auth_type: normalizeText(current.users_auth_type),
|
||||||
|
company_id: normalizeText(current.company_id),
|
||||||
|
users_tag: normalizeText(current.users_tag),
|
||||||
|
users_parent_id: normalizeText(current.users_parent_id),
|
||||||
|
users_promo_code: normalizeText(current.users_promo_code),
|
||||||
|
usergroups_id: normalizeText(current.usergroups_id),
|
||||||
|
users_id_number: normalizeText(current.users_id_number),
|
||||||
|
users_picture: normalizeText(current.users_picture),
|
||||||
|
users_id_pic_a: normalizeText(current.users_id_pic_a),
|
||||||
|
users_id_pic_b: normalizeText(current.users_id_pic_b),
|
||||||
|
users_title_picture: normalizeText(current.users_title_picture),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasUnsavedChanges(user) {
|
||||||
|
if (!state.isEditMode || !user) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const original = normalizeDraftSnapshot(buildUserDraft(user))
|
||||||
|
const current = normalizeDraftSnapshot(state.userEditDraft || buildUserDraft(user))
|
||||||
|
return JSON.stringify(original) !== JSON.stringify(current)
|
||||||
|
}
|
||||||
|
|
||||||
function syncEditDraftFromDom() {
|
function syncEditDraftFromDom() {
|
||||||
if (!state.isEditMode || !state.userEditDraft) {
|
if (!state.isEditMode || !state.userEditDraft) {
|
||||||
return
|
return
|
||||||
@@ -440,6 +512,39 @@
|
|||||||
+ '</div>'
|
+ '</div>'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderPreviewForm(user) {
|
||||||
|
return '<div class="preview-form">'
|
||||||
|
+ '<div class="preview-form-head">'
|
||||||
|
+ '<div class="preview-form-summary">'
|
||||||
|
+ '<div class="preview-summary-item"><div class="preview-summary-label">用户名称</div><div class="preview-summary-value">' + escapeHtml(user.users_name || '-') + '</div></div>'
|
||||||
|
+ '<div class="preview-summary-item"><div class="preview-summary-label">手机号</div><div class="preview-summary-value">' + escapeHtml(user.users_phone || '-') + '</div></div>'
|
||||||
|
+ '<div class="preview-summary-item"><div class="preview-summary-label">会员等级</div><div class="preview-summary-value">' + escapeHtml(user.users_level_name || user.users_level || '-') + '</div></div>'
|
||||||
|
+ '<div class="preview-summary-item"><div class="preview-summary-label">用户类型</div><div class="preview-summary-value">' + escapeHtml(user.users_type || '-') + '</div></div>'
|
||||||
|
+ '</div>'
|
||||||
|
+ '<button class="btn btn-light preview-toggle" id="togglePreviewBtn" type="button">' + (state.previewExpanded ? '收起详情' : '展开详情') + '</button>'
|
||||||
|
+ '</div>'
|
||||||
|
+ '<div class="preview-form-body' + (state.previewExpanded ? ' open' : '') + '"><div class="preview-form-inner">'
|
||||||
|
+ '<div class="preview-fields">'
|
||||||
|
+ renderPreviewField('用户状态', user.users_status)
|
||||||
|
+ renderPreviewField('用户星级', user.users_rank_level)
|
||||||
|
+ renderPreviewField('账户类型', user.users_auth_type)
|
||||||
|
+ renderPreviewField('公司ID', user.company_id)
|
||||||
|
+ renderPreviewField('用户标签', user.users_tag)
|
||||||
|
+ renderPreviewField('上级用户ID', user.users_parent_id)
|
||||||
|
+ renderPreviewField('推广码', user.users_promo_code)
|
||||||
|
+ renderPreviewField('用户组ID', user.usergroups_id)
|
||||||
|
+ renderPreviewField('证件号', user.users_id_number)
|
||||||
|
+ '</div>'
|
||||||
|
+ '<div class="attachment-grid" style="margin-top:10px; margin-bottom:0;">'
|
||||||
|
+ renderAttachmentCard('userUsersPicture', '头像附件ID', user.users_picture || '', false)
|
||||||
|
+ renderAttachmentCard('userUsersIdPicA', '证件正面附件ID', user.users_id_pic_a || '', false)
|
||||||
|
+ renderAttachmentCard('userUsersIdPicB', '证件反面附件ID', user.users_id_pic_b || '', false)
|
||||||
|
+ renderAttachmentCard('userUsersTitlePicture', '资质附件ID', user.users_title_picture || '', false)
|
||||||
|
+ '</div>'
|
||||||
|
+ '</div></div>'
|
||||||
|
+ '</div>'
|
||||||
|
}
|
||||||
|
|
||||||
function renderUserList() {
|
function renderUserList() {
|
||||||
if (!state.users.length) {
|
if (!state.users.length) {
|
||||||
userListEl.innerHTML = '<div class="empty">暂无匹配用户。</div>'
|
userListEl.innerHTML = '<div class="empty">暂无匹配用户。</div>'
|
||||||
@@ -473,7 +578,7 @@
|
|||||||
+ '</tr></thead><tbody>'
|
+ '</tr></thead><tbody>'
|
||||||
+ items.map(function (item) {
|
+ items.map(function (item) {
|
||||||
return '<tr>'
|
return '<tr>'
|
||||||
+ '<td data-label="商品名称"><div>' + escapeHtml(item.product_name || item.cart_product_id || '-') + '</div><div class="muted">' + escapeHtml(item.cart_product_id || '-') + '</div></td>'
|
+ '<td data-label="商品名称"><div>' + escapeHtml(item.product_name || item.cart_product_business_id || item.cart_product_id || '-') + '</div><div class="muted">recordId:' + escapeHtml(item.cart_product_id || '-') + '</div><div class="muted">业务ID:' + escapeHtml(item.cart_product_business_id || '-') + '</div></td>'
|
||||||
+ '<td data-label="型号">' + escapeHtml(item.product_modelnumber || '-') + '</td>'
|
+ '<td data-label="型号">' + escapeHtml(item.product_modelnumber || '-') + '</td>'
|
||||||
+ '<td data-label="数量">' + escapeHtml(item.cart_product_quantity || 0) + '</td>'
|
+ '<td data-label="数量">' + escapeHtml(item.cart_product_quantity || 0) + '</td>'
|
||||||
+ '<td data-label="单价">¥' + escapeHtml(item.cart_at_price || 0) + '</td>'
|
+ '<td data-label="单价">¥' + escapeHtml(item.cart_at_price || 0) + '</td>'
|
||||||
@@ -503,35 +608,26 @@
|
|||||||
function renderUserProfileForm(user) {
|
function renderUserProfileForm(user) {
|
||||||
const draft = state.isEditMode ? ensureEditDraft(user) : null
|
const draft = state.isEditMode ? ensureEditDraft(user) : null
|
||||||
const source = draft || user
|
const source = draft || user
|
||||||
|
const dirty = hasUnsavedChanges(user)
|
||||||
|
|
||||||
return '<div class="section user-profile-shell"><h3 class="section-title">用户信息维护</h3>'
|
return '<div class="section user-profile-shell"><div class="profile-toolbar"><h3 class="section-title" style="margin:0;">用户信息维护</h3>'
|
||||||
|
+ (state.isEditMode
|
||||||
|
? '<span class="edit-hint ' + (dirty ? 'dirty' : 'clean') + '">' + (dirty ? '● 有未保存变更' : '✓ 暂无未保存变更') + '</span>'
|
||||||
|
: '')
|
||||||
|
+ '</div>'
|
||||||
+ '<div class="detail-actions">'
|
+ '<div class="detail-actions">'
|
||||||
+ (state.isEditMode
|
+ (state.isEditMode
|
||||||
? '<button class="btn btn-primary" id="saveUserBtn" type="button">保存用户信息</button><button class="btn btn-light" id="cancelEditBtn" type="button">取消编辑</button>'
|
? '<button class="btn btn-primary" id="saveUserBtn" type="button">保存用户信息</button><button class="btn btn-light" id="cancelEditBtn" type="button">取消编辑</button>'
|
||||||
: '<button class="btn btn-primary" id="enterEditBtn" type="button">编辑用户信息</button>')
|
: '<button class="btn btn-primary" id="enterEditBtn" type="button">编辑用户信息</button>')
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="preview-grid">'
|
+ (state.isEditMode ? '' : renderPreviewForm(user))
|
||||||
+ renderPreviewField('用户名称', user.users_name)
|
+ '<div class="edit-panel' + (state.isEditMode ? ' open' : '') + '"><div class="edit-panel-inner">'
|
||||||
+ renderPreviewField('手机号', user.users_phone)
|
+ '<div class="attachment-grid editing">'
|
||||||
+ renderPreviewField('会员等级', user.users_level_name || user.users_level)
|
+ renderAttachmentCard('userUsersPicture', '头像附件ID', source.users_picture || '', true)
|
||||||
+ renderPreviewField('用户类型', user.users_type)
|
+ renderAttachmentCard('userUsersIdPicA', '证件正面附件ID', source.users_id_pic_a || '', true)
|
||||||
+ renderPreviewField('用户状态', user.users_status)
|
+ renderAttachmentCard('userUsersIdPicB', '证件反面附件ID', source.users_id_pic_b || '', true)
|
||||||
+ renderPreviewField('用户星级', user.users_rank_level)
|
+ renderAttachmentCard('userUsersTitlePicture', '资质附件ID', source.users_title_picture || '', true)
|
||||||
+ renderPreviewField('账户类型', user.users_auth_type)
|
|
||||||
+ renderPreviewField('公司ID', user.company_id)
|
|
||||||
+ renderPreviewField('用户标签', user.users_tag)
|
|
||||||
+ renderPreviewField('上级用户ID', user.users_parent_id)
|
|
||||||
+ renderPreviewField('推广码', user.users_promo_code)
|
|
||||||
+ renderPreviewField('用户组ID', user.usergroups_id)
|
|
||||||
+ renderPreviewField('证件号', user.users_id_number)
|
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '<div class="attachment-grid">'
|
|
||||||
+ renderAttachmentCard('userUsersPicture', '头像附件ID', source.users_picture || '', state.isEditMode)
|
|
||||||
+ renderAttachmentCard('userUsersIdPicA', '证件正面附件ID', source.users_id_pic_a || '', state.isEditMode)
|
|
||||||
+ renderAttachmentCard('userUsersIdPicB', '证件反面附件ID', source.users_id_pic_b || '', state.isEditMode)
|
|
||||||
+ renderAttachmentCard('userUsersTitlePicture', '资质附件ID', source.users_title_picture || '', state.isEditMode)
|
|
||||||
+ '</div>'
|
|
||||||
+ (!state.isEditMode ? '' : '<div class="edit-panel">'
|
|
||||||
+ '<div class="profile-grid">'
|
+ '<div class="profile-grid">'
|
||||||
+ '<div class="field-block"><label class="field-label" for="userUsersName">用户名称</label><input id="userUsersName" value="' + escapeHtml(source.users_name || '') + '" /></div>'
|
+ '<div class="field-block"><label class="field-label" for="userUsersName">用户名称</label><input id="userUsersName" value="' + escapeHtml(source.users_name || '') + '" /></div>'
|
||||||
+ '<div class="field-block"><label class="field-label" for="userUsersPhone">手机号</label><input id="userUsersPhone" value="' + escapeHtml(source.users_phone || '') + '" /></div>'
|
+ '<div class="field-block"><label class="field-label" for="userUsersPhone">手机号</label><input id="userUsersPhone" value="' + escapeHtml(source.users_phone || '') + '" /></div>'
|
||||||
@@ -547,7 +643,7 @@
|
|||||||
+ '<div class="field-block"><label class="field-label" for="userUsergroupsId">用户组ID</label><input id="userUsergroupsId" value="' + escapeHtml(source.usergroups_id || '') + '" /></div>'
|
+ '<div class="field-block"><label class="field-label" for="userUsergroupsId">用户组ID</label><input id="userUsergroupsId" value="' + escapeHtml(source.usergroups_id || '') + '" /></div>'
|
||||||
+ '<div class="field-block"><label class="field-label" for="userUsersIdNumber">证件号</label><input id="userUsersIdNumber" value="' + escapeHtml(source.users_id_number || '') + '" /></div>'
|
+ '<div class="field-block"><label class="field-label" for="userUsersIdNumber">证件号</label><input id="userUsersIdNumber" value="' + escapeHtml(source.users_id_number || '') + '" /></div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
+ '</div>')
|
+ '</div></div>'
|
||||||
+ '</div>'
|
+ '</div>'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,6 +704,7 @@
|
|||||||
})
|
})
|
||||||
state.users = Array.isArray(data.items) ? data.items : []
|
state.users = Array.isArray(data.items) ? data.items : []
|
||||||
state.userLevelOptions = Array.isArray(data.user_level_options) ? data.user_level_options : []
|
state.userLevelOptions = Array.isArray(data.user_level_options) ? data.user_level_options : []
|
||||||
|
state.previewExpanded = false
|
||||||
state.isEditMode = false
|
state.isEditMode = false
|
||||||
state.userEditDraft = null
|
state.userEditDraft = null
|
||||||
if (!state.users.length) {
|
if (!state.users.length) {
|
||||||
@@ -733,11 +830,18 @@
|
|||||||
if (target && target.id === 'enterEditBtn') {
|
if (target && target.id === 'enterEditBtn') {
|
||||||
const user = getSelectedUser()
|
const user = getSelectedUser()
|
||||||
state.isEditMode = true
|
state.isEditMode = true
|
||||||
|
state.previewExpanded = true
|
||||||
state.userEditDraft = user ? buildUserDraft(user) : null
|
state.userEditDraft = user ? buildUserDraft(user) : null
|
||||||
renderDetail()
|
renderDetail()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target && target.id === 'togglePreviewBtn') {
|
||||||
|
state.previewExpanded = !state.previewExpanded
|
||||||
|
renderDetail()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (target && target.id === 'cancelEditBtn') {
|
if (target && target.id === 'cancelEditBtn') {
|
||||||
state.isEditMode = false
|
state.isEditMode = false
|
||||||
state.userEditDraft = null
|
state.userEditDraft = null
|
||||||
|
|||||||
@@ -50,6 +50,10 @@
|
|||||||
<h2>产品管理</h2>
|
<h2>产品管理</h2>
|
||||||
<a class="btn" href="/pb/manage/product-manage">进入产品管理</a>
|
<a class="btn" href="/pb/manage/product-manage">进入产品管理</a>
|
||||||
</article>
|
</article>
|
||||||
|
<article class="card">
|
||||||
|
<h2>方案预设管理</h2>
|
||||||
|
<a class="btn" href="/pb/manage/scheme">进入方案预设管理</a>
|
||||||
|
</article>
|
||||||
<article class="card">
|
<article class="card">
|
||||||
<h2>SDK 权限管理</h2>
|
<h2>SDK 权限管理</h2>
|
||||||
<a class="btn" href="/pb/manage/sdk-permission-manage">进入权限管理</a>
|
<a class="btn" href="/pb/manage/sdk-permission-manage">进入权限管理</a>
|
||||||
|
|||||||
1126
pocket-base/bai_web_pb_hooks/views/scheme-manage.html
Normal file
1126
pocket-base/bai_web_pb_hooks/views/scheme-manage.html
Normal file
File diff suppressed because it is too large
Load Diff
372
pocket-base/spec/openapi-manage/cart.yaml
Normal file
372
pocket-base/spec/openapi-manage/cart.yaml
Normal file
@@ -0,0 +1,372 @@
|
|||||||
|
paths:
|
||||||
|
cartList:
|
||||||
|
post:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 查询购物车列表
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 查询当前登录用户的购物车列表。
|
||||||
|
请求头请自行携带 `Authorization: Bearer <token>`。
|
||||||
|
requestBody:
|
||||||
|
required: false
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartListRequest'
|
||||||
|
example:
|
||||||
|
keyword: 模糊搜索关键字|string
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 查询成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartListResponse'
|
||||||
|
'400':
|
||||||
|
description: 参数错误
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
cartDetail:
|
||||||
|
post:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 查询购物车详情
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 按 `cart_id` 查询单条购物车记录。
|
||||||
|
请求头请自行携带 `Authorization: Bearer <token>`。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartDetailRequest'
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 查询成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问该购物车记录
|
||||||
|
'404':
|
||||||
|
description: 未找到对应购物车记录
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
cartCreate:
|
||||||
|
post:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 新增购物车记录
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 新增购物车记录。
|
||||||
|
服务端会基于当前 token 自动写入 `cart_owner` 并生成 `cart_id`。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartCreateRequest'
|
||||||
|
example:
|
||||||
|
cart_number: 可选;未传时服务端自动生成|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|integer
|
||||||
|
cart_remark: 备注|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 创建成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或创建失败
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'429':
|
||||||
|
description: 重复请求过于频繁
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
cartUpdate:
|
||||||
|
post:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 修改购物车记录
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 按 `cart_id` 更新购物车记录。
|
||||||
|
请求头请自行携带 `Authorization: Bearer <token>`。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartUpdateRequest'
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|integer
|
||||||
|
cart_remark: 备注|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 更新成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或更新失败
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问该购物车记录
|
||||||
|
'404':
|
||||||
|
description: 未找到待修改记录
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'429':
|
||||||
|
description: 重复请求过于频繁
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
cartDelete:
|
||||||
|
post:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 删除购物车记录
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 按 `cart_id` 删除购物车记录。
|
||||||
|
当前后端实现以实际 hooks 逻辑为准。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartDeleteRequest'
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 删除成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/CartDeleteResponse'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或删除失败
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问该购物车记录
|
||||||
|
'404':
|
||||||
|
description: 未找到待删除记录
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'429':
|
||||||
|
description: 重复请求过于频繁
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
CartRecord:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
pb_id:
|
||||||
|
type: string
|
||||||
|
description: PocketBase记录主键id
|
||||||
|
created:
|
||||||
|
type: string
|
||||||
|
description: PocketBase系统创建时间
|
||||||
|
updated:
|
||||||
|
type: string
|
||||||
|
description: PocketBase系统更新时间
|
||||||
|
cart_id:
|
||||||
|
type: string
|
||||||
|
description: 购物车业务ID
|
||||||
|
cart_number:
|
||||||
|
type: string
|
||||||
|
description: 购物车名称或分组号
|
||||||
|
cart_create:
|
||||||
|
type: string
|
||||||
|
description: 购物车项创建时间
|
||||||
|
cart_owner:
|
||||||
|
type: string
|
||||||
|
description: 购物车所有者openid
|
||||||
|
cart_product_id:
|
||||||
|
type: string
|
||||||
|
description: 关联产品的 PocketBase recordId
|
||||||
|
cart_product_business_id:
|
||||||
|
type: string
|
||||||
|
description: 关联产品的业务 ID(即 prod_list_id),用于展示
|
||||||
|
cart_product_quantity:
|
||||||
|
type: [integer, number]
|
||||||
|
description: 产品数量
|
||||||
|
cart_status:
|
||||||
|
type: string
|
||||||
|
description: 购物车状态
|
||||||
|
cart_at_price:
|
||||||
|
type: [integer, number]
|
||||||
|
description: 加入购物车时价格
|
||||||
|
cart_remark:
|
||||||
|
type: string
|
||||||
|
description: 备注
|
||||||
|
is_delete:
|
||||||
|
type: [integer, number]
|
||||||
|
description: 删除标记
|
||||||
|
product_name:
|
||||||
|
type: string
|
||||||
|
description: 产品名称
|
||||||
|
product_modelnumber:
|
||||||
|
type: string
|
||||||
|
description: 产品型号
|
||||||
|
product_basic_price:
|
||||||
|
type: [integer, number, 'null']
|
||||||
|
description: 产品基础价格
|
||||||
|
example:
|
||||||
|
pb_id: PocketBase记录主键id|string
|
||||||
|
created: PocketBase系统创建时间|string
|
||||||
|
updated: PocketBase系统更新时间|string
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
cart_create: 购物车项创建时间|string
|
||||||
|
cart_owner: 购物车所有者openid|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_business_id: 产品业务ID|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|integer
|
||||||
|
cart_remark: 备注|string
|
||||||
|
is_delete: 删除标记|integer
|
||||||
|
product_name: 产品名称|string
|
||||||
|
product_modelnumber: 产品型号|string
|
||||||
|
product_basic_price: 产品基础价格|integer
|
||||||
|
CartListRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
keyword:
|
||||||
|
type: string
|
||||||
|
description: 模糊搜索关键字
|
||||||
|
cart_status:
|
||||||
|
type: string
|
||||||
|
description: 购物车状态
|
||||||
|
cart_number:
|
||||||
|
type: string
|
||||||
|
description: 购物车名称或分组号
|
||||||
|
example:
|
||||||
|
keyword: 模糊搜索关键字|string
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
CartDetailRequest:
|
||||||
|
type: object
|
||||||
|
required: [cart_id]
|
||||||
|
properties:
|
||||||
|
cart_id:
|
||||||
|
type: string
|
||||||
|
description: 购物车业务ID
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
CartCreateRequest:
|
||||||
|
type: object
|
||||||
|
required: [cart_product_id, cart_product_quantity, cart_at_price]
|
||||||
|
properties:
|
||||||
|
cart_number:
|
||||||
|
type: string
|
||||||
|
cart_product_id:
|
||||||
|
type: string
|
||||||
|
description: tbl_product_list 的 PocketBase recordId
|
||||||
|
cart_product_quantity:
|
||||||
|
type: [integer, number]
|
||||||
|
cart_status:
|
||||||
|
type: string
|
||||||
|
cart_at_price:
|
||||||
|
type: [integer, number]
|
||||||
|
cart_remark:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
cart_number: 可选;未传时服务端自动生成|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|integer
|
||||||
|
cart_remark: 备注|string
|
||||||
|
CartUpdateRequest:
|
||||||
|
type: object
|
||||||
|
required: [cart_id]
|
||||||
|
properties:
|
||||||
|
cart_id:
|
||||||
|
type: string
|
||||||
|
cart_number:
|
||||||
|
type: string
|
||||||
|
cart_product_id:
|
||||||
|
type: string
|
||||||
|
description: tbl_product_list 的 PocketBase recordId
|
||||||
|
cart_product_quantity:
|
||||||
|
type: [integer, number]
|
||||||
|
cart_status:
|
||||||
|
type: string
|
||||||
|
cart_at_price:
|
||||||
|
type: [integer, number]
|
||||||
|
cart_remark:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|integer
|
||||||
|
cart_remark: 备注|string
|
||||||
|
CartDeleteRequest:
|
||||||
|
type: object
|
||||||
|
required: [cart_id]
|
||||||
|
properties:
|
||||||
|
cart_id:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
CartListResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/CartRecord'
|
||||||
|
example:
|
||||||
|
items:
|
||||||
|
- pb_id: PocketBase记录主键id|string
|
||||||
|
created: PocketBase系统创建时间|string
|
||||||
|
updated: PocketBase系统更新时间|string
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
cart_create: 购物车项创建时间|string
|
||||||
|
cart_owner: 购物车所有者openid|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_business_id: 产品业务ID|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|integer
|
||||||
|
cart_remark: 备注|string
|
||||||
|
CartDeleteResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
cart_id:
|
||||||
|
type: string
|
||||||
|
is_delete:
|
||||||
|
type: [integer, number]
|
||||||
|
example:
|
||||||
|
cart_id: 购物车业务ID|string
|
||||||
|
is_delete: 删除标记|integer
|
||||||
103
pocket-base/spec/openapi-manage/openapi.yaml
Normal file
103
pocket-base/spec/openapi-manage/openapi.yaml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: BAI PocketBase Manage Hooks API
|
||||||
|
version: 1.0.0-manage-folder
|
||||||
|
description: |
|
||||||
|
面向管理端与自定义 hooks 的接口文档。
|
||||||
|
本目录仅收敛自定义 hooks API,不包含 PocketBase 原生 records API。
|
||||||
|
|
||||||
|
文档约定:
|
||||||
|
- 不单独配置鉴权组件;如接口需要登录,请直接在说明中关注 `Authorization: Bearer <token>`
|
||||||
|
- 示例字段值统一使用 `<字段说明>|<类型>` 风格
|
||||||
|
- 当前 `tbl_auth_users.openid` 为全平台统一身份锚点
|
||||||
|
servers:
|
||||||
|
- url: https://bai-api.blv-oa.com
|
||||||
|
description: 生产环境
|
||||||
|
- url: http://localhost:8090
|
||||||
|
description: PocketBase 本地环境
|
||||||
|
tags:
|
||||||
|
- name: 系统
|
||||||
|
description: hooks 系统基础接口
|
||||||
|
- name: 微信认证
|
||||||
|
description: hooks 微信认证与资料接口
|
||||||
|
- name: 平台认证
|
||||||
|
description: hooks 平台用户认证接口
|
||||||
|
- name: 字典管理
|
||||||
|
description: hooks 字典管理接口
|
||||||
|
- name: 附件管理
|
||||||
|
description: hooks 附件管理接口
|
||||||
|
- name: 文档管理
|
||||||
|
description: hooks 文档管理接口
|
||||||
|
- name: 文档历史
|
||||||
|
description: hooks 文档历史接口
|
||||||
|
- name: 购物车
|
||||||
|
description: hooks 购物车接口
|
||||||
|
- name: 订单
|
||||||
|
description: hooks 订单接口
|
||||||
|
paths:
|
||||||
|
/pb/api/system/test-helloworld:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1system~1test-helloworld'
|
||||||
|
/pb/api/system/health:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1system~1health'
|
||||||
|
/pb/api/system/users-count:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1system~1users-count'
|
||||||
|
/pb/api/system/refresh-token:
|
||||||
|
$ref: '../openapi.yaml#/paths/~1pb~1api~1system~1refresh-token'
|
||||||
|
/pb/api/wechat/login:
|
||||||
|
$ref: '../openapi.yaml#/paths/~1pb~1api~1wechat~1login'
|
||||||
|
/pb/api/wechat/profile:
|
||||||
|
$ref: '../openapi.yaml#/paths/~1pb~1api~1wechat~1profile'
|
||||||
|
/pb/api/platform/register:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1platform~1register'
|
||||||
|
/pb/api/platform/login:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1platform~1login'
|
||||||
|
/pb/api/dictionary/list:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1dictionary~1list'
|
||||||
|
/pb/api/dictionary/detail:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1dictionary~1detail'
|
||||||
|
/pb/api/dictionary/create:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1dictionary~1create'
|
||||||
|
/pb/api/dictionary/update:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1dictionary~1update'
|
||||||
|
/pb/api/dictionary/delete:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1dictionary~1delete'
|
||||||
|
/pb/api/attachment/list:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1attachment~1list'
|
||||||
|
/pb/api/attachment/detail:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1attachment~1detail'
|
||||||
|
/pb/api/attachment/upload:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1attachment~1upload'
|
||||||
|
/pb/api/attachment/delete:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1attachment~1delete'
|
||||||
|
/pb/api/document/list:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1document~1list'
|
||||||
|
/pb/api/document/detail:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1document~1detail'
|
||||||
|
/pb/api/document/create:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1document~1create'
|
||||||
|
/pb/api/document/update:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1document~1update'
|
||||||
|
/pb/api/document/delete:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1document~1delete'
|
||||||
|
/pb/api/document-history/list:
|
||||||
|
$ref: '../openapi-manage.yaml#/paths/~1pb~1api~1document-history~1list'
|
||||||
|
/pb/api/cart/list:
|
||||||
|
$ref: './cart.yaml#/paths/cartList'
|
||||||
|
/pb/api/cart/detail:
|
||||||
|
$ref: './cart.yaml#/paths/cartDetail'
|
||||||
|
/pb/api/cart/create:
|
||||||
|
$ref: './cart.yaml#/paths/cartCreate'
|
||||||
|
/pb/api/cart/update:
|
||||||
|
$ref: './cart.yaml#/paths/cartUpdate'
|
||||||
|
/pb/api/cart/delete:
|
||||||
|
$ref: './cart.yaml#/paths/cartDelete'
|
||||||
|
/pb/api/order/list:
|
||||||
|
$ref: './order.yaml#/paths/orderList'
|
||||||
|
/pb/api/order/detail:
|
||||||
|
$ref: './order.yaml#/paths/orderDetail'
|
||||||
|
/pb/api/order/create:
|
||||||
|
$ref: './order.yaml#/paths/orderCreate'
|
||||||
|
/pb/api/order/update:
|
||||||
|
$ref: './order.yaml#/paths/orderUpdate'
|
||||||
|
/pb/api/order/delete:
|
||||||
|
$ref: './order.yaml#/paths/orderDelete'
|
||||||
371
pocket-base/spec/openapi-manage/order.yaml
Normal file
371
pocket-base/spec/openapi-manage/order.yaml
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
paths:
|
||||||
|
orderList:
|
||||||
|
post:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 查询订单列表
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 查询当前登录用户的订单列表。
|
||||||
|
请求头请自行携带 `Authorization: Bearer <token>`。
|
||||||
|
requestBody:
|
||||||
|
required: false
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderListRequest'
|
||||||
|
example:
|
||||||
|
keyword: 模糊搜索关键字|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 查询成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderListResponse'
|
||||||
|
'400':
|
||||||
|
description: 参数错误
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
orderDetail:
|
||||||
|
post:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 查询订单详情
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 按 `order_id` 查询单条订单记录。
|
||||||
|
请求头请自行携带 `Authorization: Bearer <token>`。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderDetailRequest'
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 查询成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问该订单记录
|
||||||
|
'404':
|
||||||
|
description: 未找到对应订单记录
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
orderCreate:
|
||||||
|
post:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 新增订单记录
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 新增订单记录。
|
||||||
|
服务端会基于当前 token 自动写入 `order_owner` 并生成 `order_id`。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderCreateRequest'
|
||||||
|
example:
|
||||||
|
order_number: 可选;未传时服务端自动生成|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|integer
|
||||||
|
order_remark: 备注|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 创建成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或创建失败
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'429':
|
||||||
|
description: 重复请求过于频繁
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
orderUpdate:
|
||||||
|
post:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 修改订单记录
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 按 `order_id` 更新订单记录。
|
||||||
|
请求头请自行携带 `Authorization: Bearer <token>`。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderUpdateRequest'
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
order_number: 订单编号|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|integer
|
||||||
|
order_remark: 备注|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 更新成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或更新失败
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问该订单记录
|
||||||
|
'404':
|
||||||
|
description: 未找到待修改记录
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'429':
|
||||||
|
description: 重复请求过于频繁
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
orderDelete:
|
||||||
|
post:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 删除订单记录
|
||||||
|
description: |
|
||||||
|
调用自定义 hooks API 按 `order_id` 删除订单记录。
|
||||||
|
当前后端实现以实际 hooks 逻辑为准。
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderDeleteRequest'
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 删除成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/OrderDeleteResponse'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或删除失败
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问该订单记录
|
||||||
|
'404':
|
||||||
|
description: 未找到待删除记录
|
||||||
|
'415':
|
||||||
|
description: 请求体必须为 application/json
|
||||||
|
'429':
|
||||||
|
description: 重复请求过于频繁
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
components:
|
||||||
|
schemas:
|
||||||
|
OrderRecord:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
pb_id:
|
||||||
|
type: string
|
||||||
|
description: PocketBase记录主键id
|
||||||
|
created:
|
||||||
|
type: string
|
||||||
|
description: PocketBase系统创建时间
|
||||||
|
updated:
|
||||||
|
type: string
|
||||||
|
description: PocketBase系统更新时间
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
description: 订单业务ID
|
||||||
|
order_number:
|
||||||
|
type: string
|
||||||
|
description: 订单编号
|
||||||
|
order_create:
|
||||||
|
type: string
|
||||||
|
description: 订单创建时间
|
||||||
|
order_owner:
|
||||||
|
type: string
|
||||||
|
description: 订单所有者openid
|
||||||
|
order_source:
|
||||||
|
type: string
|
||||||
|
description: 订单来源
|
||||||
|
order_status:
|
||||||
|
type: string
|
||||||
|
description: 订单状态
|
||||||
|
order_source_id:
|
||||||
|
type: string
|
||||||
|
description: 来源关联业务ID
|
||||||
|
order_snap:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
description: 订单快照
|
||||||
|
order_amount:
|
||||||
|
type: [integer, number]
|
||||||
|
description: 订单金额
|
||||||
|
order_remark:
|
||||||
|
type: string
|
||||||
|
description: 备注
|
||||||
|
is_delete:
|
||||||
|
type: [integer, number]
|
||||||
|
description: 删除标记
|
||||||
|
example:
|
||||||
|
pb_id: PocketBase记录主键id|string
|
||||||
|
created: PocketBase系统创建时间|string
|
||||||
|
updated: PocketBase系统更新时间|string
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
order_number: 订单编号|string
|
||||||
|
order_create: 订单创建时间|string
|
||||||
|
order_owner: 订单所有者openid|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|integer
|
||||||
|
order_remark: 备注|string
|
||||||
|
is_delete: 删除标记|integer
|
||||||
|
OrderListRequest:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
keyword:
|
||||||
|
type: string
|
||||||
|
order_status:
|
||||||
|
type: string
|
||||||
|
order_source:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
keyword: 模糊搜索关键字|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
OrderDetailRequest:
|
||||||
|
type: object
|
||||||
|
required: [order_id]
|
||||||
|
properties:
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
OrderCreateRequest:
|
||||||
|
type: object
|
||||||
|
required: [order_source, order_source_id, order_snap, order_amount]
|
||||||
|
properties:
|
||||||
|
order_number:
|
||||||
|
type: string
|
||||||
|
order_source:
|
||||||
|
type: string
|
||||||
|
order_status:
|
||||||
|
type: string
|
||||||
|
order_source_id:
|
||||||
|
type: string
|
||||||
|
order_snap:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
order_amount:
|
||||||
|
type: [integer, number]
|
||||||
|
order_remark:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
order_number: 可选;未传时服务端自动生成|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|integer
|
||||||
|
order_remark: 备注|string
|
||||||
|
OrderUpdateRequest:
|
||||||
|
type: object
|
||||||
|
required: [order_id]
|
||||||
|
properties:
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
order_number:
|
||||||
|
type: string
|
||||||
|
order_source:
|
||||||
|
type: string
|
||||||
|
order_status:
|
||||||
|
type: string
|
||||||
|
order_source_id:
|
||||||
|
type: string
|
||||||
|
order_snap:
|
||||||
|
type: object
|
||||||
|
additionalProperties: true
|
||||||
|
order_amount:
|
||||||
|
type: [integer, number]
|
||||||
|
order_remark:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
order_number: 订单编号|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|integer
|
||||||
|
order_remark: 备注|string
|
||||||
|
OrderDeleteRequest:
|
||||||
|
type: object
|
||||||
|
required: [order_id]
|
||||||
|
properties:
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
OrderListResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
items:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/components/schemas/OrderRecord'
|
||||||
|
example:
|
||||||
|
items:
|
||||||
|
- pb_id: PocketBase记录主键id|string
|
||||||
|
created: PocketBase系统创建时间|string
|
||||||
|
updated: PocketBase系统更新时间|string
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
order_number: 订单编号|string
|
||||||
|
order_create: 订单创建时间|string
|
||||||
|
order_owner: 订单所有者openid|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|integer
|
||||||
|
order_remark: 备注|string
|
||||||
|
OrderDeleteResponse:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
order_id:
|
||||||
|
type: string
|
||||||
|
is_delete:
|
||||||
|
type: [integer, number]
|
||||||
|
example:
|
||||||
|
order_id: 订单业务ID|string
|
||||||
|
is_delete: 删除标记|integer
|
||||||
@@ -1,54 +1,29 @@
|
|||||||
openapi: 3.1.0
|
openapi: 3.1.0
|
||||||
info:
|
info:
|
||||||
title: BAI PocketBase WeChat API
|
title: BAI PocketBase Native API
|
||||||
version: 1.0.0-wx
|
version: 1.0.0-wx
|
||||||
description: |
|
description: |
|
||||||
面向微信端的小程序接口文档。
|
顶层兼容入口。
|
||||||
本文档包含微信登录、微信资料完善,以及微信小程序侧会直接调用的业务接口。
|
当前原生 API 文档已整理到 `spec/openapi-wx/` 目录下;本文件保留为兼容总入口。
|
||||||
|
|
||||||
微信小程序调用适配说明:
|
|
||||||
- 除 `/pb/api/wechat/login` 外,调用购物车 / 订单的 PocketBase 原生 records API 时都需要在请求头中携带 `Authorization: Bearer <token>`
|
|
||||||
- `token` 取自 `/pb/api/wechat/login` 成功返回的认证 token
|
|
||||||
- 小程序端应统一使用 HTTPS,不依赖 Cookie / Session
|
|
||||||
- 购物车与订单同时提供两套调用说明:`/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:
|
servers:
|
||||||
- url: https://bai-api.blv-oa.com
|
- url: https://bai-api.blv-oa.com
|
||||||
description: 生产环境
|
description: 生产环境
|
||||||
- url: http://127.0.0.1:8090
|
- url: http://127.0.0.1:8090
|
||||||
description: PocketBase 本地环境
|
description: PocketBase 本地环境
|
||||||
tags:
|
tags:
|
||||||
- name: 系统
|
|
||||||
description: 微信端共用系统接口
|
|
||||||
- name: 微信认证
|
|
||||||
description: 微信登录、资料完善与 token 刷新接口
|
|
||||||
- name: 企业信息
|
- name: 企业信息
|
||||||
description: 通过 PocketBase 原生 records API 访问 `tbl_company`
|
description: PocketBase 原生公司记录接口
|
||||||
- name: 附件信息
|
- name: 附件信息
|
||||||
description: 通过 PocketBase 原生 records API 访问 `tbl_attachments`
|
description: PocketBase 原生附件记录接口
|
||||||
- name: 产品信息
|
- name: 产品信息
|
||||||
description: 通过 PocketBase 原生 records API 访问 `tbl_product_list`
|
description: PocketBase 原生产品记录接口
|
||||||
- name: 文档信息
|
- name: 文档信息
|
||||||
description: 通过 PocketBase 原生 records API 访问 `tbl_document`
|
description: PocketBase 原生文档记录接口
|
||||||
- name: 购物车
|
- name: 购物车
|
||||||
description: 微信小程序侧购物车 CRUD 接口
|
description: PocketBase 原生购物车记录接口
|
||||||
- name: 订单
|
- name: 订单
|
||||||
description: 微信小程序侧订单 CRUD 接口
|
description: PocketBase 原生订单记录接口
|
||||||
security: []
|
|
||||||
paths:
|
paths:
|
||||||
/pb/api/system/users-count:
|
|
||||||
$ref: './openapi-wx/system.yaml#/paths/usersCount'
|
|
||||||
/pb/api/system/refresh-token:
|
|
||||||
$ref: './openapi-wx/system.yaml#/paths/refreshToken'
|
|
||||||
/pb/api/wechat/login:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/paths/wechatLogin'
|
|
||||||
/pb/api/wechat/profile:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/paths/wechatProfile'
|
|
||||||
/pb/api/collections/tbl_company/records:
|
/pb/api/collections/tbl_company/records:
|
||||||
$ref: './openapi-wx/company.yaml#/paths/companyRecords'
|
$ref: './openapi-wx/company.yaml#/paths/companyRecords'
|
||||||
/pb/api/collections/tbl_company/records/{recordId}:
|
/pb/api/collections/tbl_company/records/{recordId}:
|
||||||
@@ -59,150 +34,11 @@ paths:
|
|||||||
$ref: './openapi-wx/products.yaml#/paths/productRecords'
|
$ref: './openapi-wx/products.yaml#/paths/productRecords'
|
||||||
/pb/api/collections/tbl_document/records:
|
/pb/api/collections/tbl_document/records:
|
||||||
$ref: './openapi-wx/documents.yaml#/paths/documentRecords'
|
$ref: './openapi-wx/documents.yaml#/paths/documentRecords'
|
||||||
/pb/api/cart/list:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartList'
|
|
||||||
/pb/api/cart/detail:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartDetail'
|
|
||||||
/pb/api/cart/create:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartCreate'
|
|
||||||
/pb/api/cart/update:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartUpdate'
|
|
||||||
/pb/api/cart/delete:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartDelete'
|
|
||||||
/pb/api/collections/tbl_cart/records:
|
/pb/api/collections/tbl_cart/records:
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartRecords'
|
$ref: './openapi-wx/cart.yaml#/paths/cartRecords'
|
||||||
/pb/api/collections/tbl_cart/records/{recordId}:
|
/pb/api/collections/tbl_cart/records/{recordId}:
|
||||||
$ref: './openapi-wx/cart.yaml#/paths/cartRecordById'
|
$ref: './openapi-wx/cart.yaml#/paths/cartRecordById'
|
||||||
/pb/api/order/list:
|
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderList'
|
|
||||||
/pb/api/order/detail:
|
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderDetail'
|
|
||||||
/pb/api/order/create:
|
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderCreate'
|
|
||||||
/pb/api/order/update:
|
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderUpdate'
|
|
||||||
/pb/api/order/delete:
|
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderDelete'
|
|
||||||
/pb/api/collections/tbl_order/records:
|
/pb/api/collections/tbl_order/records:
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderRecords'
|
$ref: './openapi-wx/order.yaml#/paths/orderRecords'
|
||||||
/pb/api/collections/tbl_order/records/{recordId}:
|
/pb/api/collections/tbl_order/records/{recordId}:
|
||||||
$ref: './openapi-wx/order.yaml#/paths/orderRecordById'
|
$ref: './openapi-wx/order.yaml#/paths/orderRecordById'
|
||||||
components:
|
|
||||||
securitySchemes:
|
|
||||||
BearerAuth:
|
|
||||||
type: http
|
|
||||||
scheme: bearer
|
|
||||||
bearerFormat: JWT
|
|
||||||
schemas:
|
|
||||||
ApiResponseBase:
|
|
||||||
$ref: './openapi-wx/system.yaml#/components/schemas/ApiResponseBase'
|
|
||||||
ErrorResponse:
|
|
||||||
$ref: './openapi-wx/system.yaml#/components/schemas/ErrorResponse'
|
|
||||||
SystemRefreshTokenRequest:
|
|
||||||
$ref: './openapi-wx/system.yaml#/components/schemas/SystemRefreshTokenRequest'
|
|
||||||
RefreshTokenResponse:
|
|
||||||
$ref: './openapi-wx/system.yaml#/components/schemas/RefreshTokenResponse'
|
|
||||||
UsersCountData:
|
|
||||||
$ref: './openapi-wx/system.yaml#/components/schemas/UsersCountData'
|
|
||||||
UsersCountResponse:
|
|
||||||
$ref: './openapi-wx/system.yaml#/components/schemas/UsersCountResponse'
|
|
||||||
UserInfo:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/UserInfo'
|
|
||||||
WechatLoginRequest:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/WechatLoginRequest'
|
|
||||||
WechatProfileRequest:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/WechatProfileRequest'
|
|
||||||
AuthSuccessData:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/AuthSuccessData'
|
|
||||||
AuthSuccessResponse:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/AuthSuccessResponse'
|
|
||||||
WechatProfileResponseData:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/WechatProfileResponseData'
|
|
||||||
WechatProfileResponse:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/WechatProfileResponse'
|
|
||||||
CompanyInfo:
|
|
||||||
$ref: './openapi-wx/wechat-auth.yaml#/components/schemas/CompanyInfo'
|
|
||||||
PocketBaseNativeError:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseNativeError'
|
|
||||||
PocketBaseRecordBase:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseRecordBase'
|
|
||||||
PocketBaseCompanyFields:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseCompanyFields'
|
|
||||||
PocketBaseCompanyRecord:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseCompanyRecord'
|
|
||||||
PocketBaseCompanyCreateRequest:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseCompanyCreateRequest'
|
|
||||||
PocketBaseCompanyUpdateRequest:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseCompanyUpdateRequest'
|
|
||||||
PocketBaseCompanyListResponse:
|
|
||||||
$ref: './openapi-wx/company.yaml#/components/schemas/PocketBaseCompanyListResponse'
|
|
||||||
PocketBaseAttachmentRecord:
|
|
||||||
$ref: './openapi-wx/attachments.yaml#/components/schemas/PocketBaseAttachmentRecord'
|
|
||||||
PocketBaseAttachmentListResponse:
|
|
||||||
$ref: './openapi-wx/attachments.yaml#/components/schemas/PocketBaseAttachmentListResponse'
|
|
||||||
PocketBaseProductListFields:
|
|
||||||
$ref: './openapi-wx/products.yaml#/components/schemas/PocketBaseProductListFields'
|
|
||||||
PocketBaseProductListRecord:
|
|
||||||
$ref: './openapi-wx/products.yaml#/components/schemas/PocketBaseProductListRecord'
|
|
||||||
PocketBaseProductListListResponse:
|
|
||||||
$ref: './openapi-wx/products.yaml#/components/schemas/PocketBaseProductListListResponse'
|
|
||||||
PocketBaseDocumentFields:
|
|
||||||
$ref: './openapi-wx/documents.yaml#/components/schemas/PocketBaseDocumentFields'
|
|
||||||
PocketBaseDocumentRecord:
|
|
||||||
$ref: './openapi-wx/documents.yaml#/components/schemas/PocketBaseDocumentRecord'
|
|
||||||
PocketBaseDocumentListResponse:
|
|
||||||
$ref: './openapi-wx/documents.yaml#/components/schemas/PocketBaseDocumentListResponse'
|
|
||||||
HookRecordBase:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/HookRecordBase'
|
|
||||||
CartRecord:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartRecord'
|
|
||||||
CartListRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartListRequest'
|
|
||||||
CartDetailRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartDetailRequest'
|
|
||||||
CartCreateRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartCreateRequest'
|
|
||||||
CartUpdateRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartUpdateRequest'
|
|
||||||
CartDeleteRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartDeleteRequest'
|
|
||||||
CartListResponse:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartListResponse'
|
|
||||||
CartDeleteResponse:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/CartDeleteResponse'
|
|
||||||
PocketBaseCartFields:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/PocketBaseCartFields'
|
|
||||||
PocketBaseCartRecord:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/PocketBaseCartRecord'
|
|
||||||
PocketBaseCartCreateRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/PocketBaseCartCreateRequest'
|
|
||||||
PocketBaseCartUpdateRequest:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/PocketBaseCartUpdateRequest'
|
|
||||||
PocketBaseCartListResponse:
|
|
||||||
$ref: './openapi-wx/cart.yaml#/components/schemas/PocketBaseCartListResponse'
|
|
||||||
OrderRecord:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderRecord'
|
|
||||||
OrderListRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderListRequest'
|
|
||||||
OrderDetailRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderDetailRequest'
|
|
||||||
OrderCreateRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderCreateRequest'
|
|
||||||
OrderUpdateRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderUpdateRequest'
|
|
||||||
OrderDeleteRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderDeleteRequest'
|
|
||||||
OrderListResponse:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderListResponse'
|
|
||||||
OrderDeleteResponse:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/OrderDeleteResponse'
|
|
||||||
PocketBaseOrderFields:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/PocketBaseOrderFields'
|
|
||||||
PocketBaseOrderRecord:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/PocketBaseOrderRecord'
|
|
||||||
PocketBaseOrderCreateRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/PocketBaseOrderCreateRequest'
|
|
||||||
PocketBaseOrderUpdateRequest:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/PocketBaseOrderUpdateRequest'
|
|
||||||
PocketBaseOrderListResponse:
|
|
||||||
$ref: './openapi-wx/order.yaml#/components/schemas/PocketBaseOrderListResponse'
|
|
||||||
|
|||||||
@@ -1,157 +1,155 @@
|
|||||||
paths:
|
paths:
|
||||||
cartList:
|
cartRecords:
|
||||||
post:
|
get:
|
||||||
operationId: postCartList
|
tags: [购物车]
|
||||||
tags:
|
summary: 查询购物车记录列表
|
||||||
- 购物车
|
|
||||||
summary: 按索引字段模糊查询购物车列表
|
|
||||||
description: |
|
description: |
|
||||||
调用自定义 hooks API 查询当前登录用户的购物车列表。
|
使用 PocketBase 原生 records list 接口查询 `tbl_cart`。
|
||||||
|
如需鉴权,请自行在请求头中携带 `Authorization: Bearer <token>`。
|
||||||
查询规则:
|
parameters:
|
||||||
- 仅允许查询当前 `Authorization` 对应用户自己的购物车记录
|
- name: filter
|
||||||
- 支持通过 `keyword` 对多个索引相关字段做统一模糊搜索,当前实现覆盖:
|
in: query
|
||||||
- `cart_id`
|
|
||||||
- `cart_number`
|
|
||||||
- `cart_product_id`
|
|
||||||
- `product_name`
|
|
||||||
- 支持按 `cart_status` 精确过滤
|
|
||||||
- 支持按 `cart_number` 精确过滤
|
|
||||||
|
|
||||||
目标软删除契约:
|
|
||||||
- 目标行为应仅返回 `is_delete = 0` 的记录
|
|
||||||
- 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
requestBody:
|
|
||||||
required: false
|
required: false
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/CartListRequest
|
type: string
|
||||||
example:
|
description: PocketBase 原生过滤表达式
|
||||||
keyword: 对 cart_id、cart_number、cart_product_id 等索引相关字段的统一模糊搜索关键字|string
|
example: cart_id="购物车业务ID|string"
|
||||||
cart_status: 购物车状态精确过滤|string
|
- name: page
|
||||||
cart_number: 购物车名称或分组号精确过滤|string
|
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
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: -cart_create
|
||||||
responses:
|
responses:
|
||||||
"200":
|
'200':
|
||||||
description: 查询成功
|
description: 查询成功
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/CartListResponse
|
$ref: '#/components/schemas/PocketBaseCartListResponse'
|
||||||
example:
|
'400':
|
||||||
items:
|
description: 查询参数错误
|
||||||
- pb_id: PocketBase 记录主键 id|string
|
'401':
|
||||||
created: PocketBase 系统创建时间|string
|
description: token 无效或已过期
|
||||||
updated: PocketBase 系统更新时间|string
|
'403':
|
||||||
cart_id: 购物车业务 ID|string
|
description: 无权访问
|
||||||
cart_number: 购物车名称或分组号|string
|
'500':
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
|
||||||
cart_owner: 购物车所有者 openid|string
|
|
||||||
cart_product_id: 产品业务 ID|string
|
|
||||||
cart_product_quantity: 产品数量|integer
|
|
||||||
cart_status: 购物车状态|string
|
|
||||||
cart_at_price: 加入购物车时价格|integer
|
|
||||||
cart_remark: 备注|string
|
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
|
||||||
product_name: 产品名称(服务端联动补充)|string
|
|
||||||
product_modelnumber: 产品型号(服务端联动补充)|string
|
|
||||||
product_basic_price: 产品基础价格(服务端联动补充)|integer
|
|
||||||
"400":
|
|
||||||
description: 参数错误
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"401":
|
|
||||||
description: token 缺失、无效或已过期
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"415":
|
|
||||||
description: 请求体必须为 application/json
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"500":
|
|
||||||
description: 服务端错误
|
description: 服务端错误
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
cartDetail:
|
|
||||||
post:
|
post:
|
||||||
operationId: postCartDetail
|
tags: [购物车]
|
||||||
tags:
|
summary: 创建购物车记录
|
||||||
- 购物车
|
|
||||||
summary: 按 cart_id 精确查询购物车详情
|
|
||||||
description: |
|
description: |
|
||||||
调用自定义 hooks API 按 `cart_id` 查询单条购物车记录。
|
使用 PocketBase 原生 records create 接口向 `tbl_cart` 新增记录。
|
||||||
|
如集合规则要求 `cart_owner` 与当前用户一致,请客户端显式提交。
|
||||||
查询规则:
|
|
||||||
- 仅允许访问当前 `Authorization` 对应用户自己的购物车记录
|
|
||||||
- 查询键为业务 ID `cart_id`,不是 PocketBase 原生 `recordId`
|
|
||||||
|
|
||||||
目标软删除契约:
|
|
||||||
- 目标行为应仅允许查询 `is_delete = 0` 的记录
|
|
||||||
- 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/CartDetailRequest
|
$ref: '#/components/schemas/PocketBaseCartCreateRequest'
|
||||||
example:
|
example:
|
||||||
cart_id: 购物车业务 ID|string
|
cart_id: 购物车业务ID|string
|
||||||
|
cart_number: 购物车名称或分组号|string
|
||||||
|
cart_owner: 购物车所有者openid|string
|
||||||
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_quantity: 产品数量|integer
|
||||||
|
cart_status: 购物车状态|string
|
||||||
|
cart_at_price: 加入购物车时价格|number
|
||||||
|
cart_remark: 备注|string
|
||||||
responses:
|
responses:
|
||||||
"200":
|
'200':
|
||||||
description: 查询成功
|
description: 创建成功
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/CartRecord
|
$ref: '#/components/schemas/PocketBaseCartRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或违反集合规则
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
cartRecordById:
|
||||||
|
patch:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 更新购物车记录
|
||||||
|
description: |
|
||||||
|
使用 PocketBase 原生 records update 接口更新 `tbl_cart`。
|
||||||
|
路径参数使用 PocketBase 原生 `recordId`。
|
||||||
|
parameters:
|
||||||
|
- name: recordId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: PocketBase记录主键|string
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PocketBaseCartUpdateRequest'
|
||||||
example:
|
example:
|
||||||
pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
cart_number: 购物车名称或分组号|string
|
cart_number: 购物车名称或分组号|string
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
cart_owner: 购物车所有者openid|string
|
||||||
cart_owner: 购物车所有者 openid|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
cart_product_id: 产品业务 ID|string
|
|
||||||
cart_product_quantity: 产品数量|integer
|
cart_product_quantity: 产品数量|integer
|
||||||
cart_status: 购物车状态|string
|
cart_status: 购物车状态|string
|
||||||
cart_at_price: 加入购物车时价格|integer
|
cart_at_price: 加入购物车时价格|number
|
||||||
cart_remark: 备注|string
|
cart_remark: 备注|string
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
responses:
|
||||||
product_name: 产品名称(服务端联动补充)|string
|
'200':
|
||||||
product_modelnumber: 产品型号(服务端联动补充)|string
|
description: 更新成功
|
||||||
product_basic_price: 产品基础价格(服务端联动补充)|integer
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PocketBaseCartRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'404':
|
||||||
|
description: 记录不存在
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
delete:
|
||||||
|
tags: [购物车]
|
||||||
|
summary: 删除购物车记录
|
||||||
|
description: |
|
||||||
|
使用 PocketBase 原生 records delete 接口删除 `tbl_cart`。
|
||||||
|
路径参数使用 PocketBase 原生 `recordId`。
|
||||||
|
parameters:
|
||||||
|
- name: recordId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: PocketBase记录主键|string
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: 删除成功
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'404':
|
||||||
|
description: 记录不存在
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
"400":
|
"400":
|
||||||
description: 参数错误
|
description: 参数错误
|
||||||
content:
|
content:
|
||||||
@@ -231,7 +229,8 @@ paths:
|
|||||||
- 服务端自动根据当前 token 写入 `cart_owner`
|
- 服务端自动根据当前 token 写入 `cart_owner`
|
||||||
- 服务端自动生成 `cart_id`
|
- 服务端自动生成 `cart_id`
|
||||||
- 若未传 `cart_number`,服务端会自动生成展示编号
|
- 若未传 `cart_number`,服务端会自动生成展示编号
|
||||||
- `cart_product_id`、`cart_product_quantity`、`cart_at_price` 为必填
|
- `cart_product_id` 为必填;当前 hooks 与库结构保持一致,必须传 `tbl_product_list` 的 `recordId`
|
||||||
|
- `cart_product_quantity`、`cart_at_price` 为必填
|
||||||
|
|
||||||
目标软删除契约:
|
目标软删除契约:
|
||||||
- 新建记录应默认为 `is_delete = 0`
|
- 新建记录应默认为 `is_delete = 0`
|
||||||
@@ -246,7 +245,7 @@ paths:
|
|||||||
$ref: ../openapi-wx.yaml#/components/schemas/CartCreateRequest
|
$ref: ../openapi-wx.yaml#/components/schemas/CartCreateRequest
|
||||||
example:
|
example:
|
||||||
cart_number: 可选;未传时服务端自动生成|string
|
cart_number: 可选;未传时服务端自动生成|string
|
||||||
cart_product_id: 产品业务 ID|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
cart_product_quantity: 产品数量,需为正整数|integer
|
cart_product_quantity: 产品数量,需为正整数|integer
|
||||||
cart_status: 可选;未传时默认 有效|string
|
cart_status: 可选;未传时默认 有效|string
|
||||||
cart_at_price: 加入购物车时价格|integer
|
cart_at_price: 加入购物车时价格|integer
|
||||||
@@ -266,7 +265,8 @@ paths:
|
|||||||
cart_number: 购物车名称或分组号|string
|
cart_number: 购物车名称或分组号|string
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
cart_create: 购物车项创建时间,由数据库自动生成|string
|
||||||
cart_owner: 购物车所有者 openid|string
|
cart_owner: 购物车所有者 openid|string
|
||||||
cart_product_id: 产品业务 ID|string
|
cart_product_id: 关联产品的 PocketBase recordId|string
|
||||||
|
cart_product_business_id: 产品业务 ID|string
|
||||||
cart_product_quantity: 产品数量|integer
|
cart_product_quantity: 产品数量|integer
|
||||||
cart_status: 购物车状态|string
|
cart_status: 购物车状态|string
|
||||||
cart_at_price: 加入购物车时价格|integer
|
cart_at_price: 加入购物车时价格|integer
|
||||||
@@ -354,7 +354,7 @@ paths:
|
|||||||
example:
|
example:
|
||||||
cart_id: 购物车业务 ID|string
|
cart_id: 购物车业务 ID|string
|
||||||
cart_number: cart_number|string
|
cart_number: cart_number|string
|
||||||
cart_product_id: cart_product_id|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
cart_product_quantity: cart_product_quantity|integer
|
cart_product_quantity: cart_product_quantity|integer
|
||||||
cart_status: cart_status|string
|
cart_status: cart_status|string
|
||||||
cart_at_price: cart_at_price|integer
|
cart_at_price: cart_at_price|integer
|
||||||
@@ -374,7 +374,8 @@ paths:
|
|||||||
cart_number: 购物车名称或分组号|string
|
cart_number: 购物车名称或分组号|string
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
cart_create: 购物车项创建时间,由数据库自动生成|string
|
||||||
cart_owner: 购物车所有者 openid|string
|
cart_owner: 购物车所有者 openid|string
|
||||||
cart_product_id: 产品业务 ID|string
|
cart_product_id: 关联产品的 PocketBase recordId|string
|
||||||
|
cart_product_business_id: 产品业务 ID|string
|
||||||
cart_product_quantity: 产品数量|integer
|
cart_product_quantity: 产品数量|integer
|
||||||
cart_status: 购物车状态|string
|
cart_status: 购物车状态|string
|
||||||
cart_at_price: 加入购物车时价格|integer
|
cart_at_price: 加入购物车时价格|integer
|
||||||
@@ -710,8 +711,9 @@ paths:
|
|||||||
使用 PocketBase 原生 records create 接口向 `tbl_cart` 新增记录。
|
使用 PocketBase 原生 records create 接口向 `tbl_cart` 新增记录。
|
||||||
|
|
||||||
当前线上权限规则:
|
当前线上权限规则:
|
||||||
- `createRule = @request.auth.id != "" && @request.body.cart_owner = @request.auth.openid`
|
- `createRule = @request.body.cart_owner != ""`
|
||||||
- 因此客户端创建时必须显式提交 `cart_owner`,并且值必须等于当前 token 对应的 `openid`
|
- 因此客户端创建时必须显式提交非空 `cart_owner`
|
||||||
|
- `cart_product_id` 当前为 relation 字段,必须传 `tbl_product_list` 的 PocketBase `recordId`
|
||||||
|
|
||||||
这意味着:
|
这意味着:
|
||||||
- 不能依赖服务端自动补 owner
|
- 不能依赖服务端自动补 owner
|
||||||
@@ -728,11 +730,11 @@ paths:
|
|||||||
example:
|
example:
|
||||||
cart_id: cart_id|string
|
cart_id: cart_id|string
|
||||||
cart_number: cart_number|string
|
cart_number: cart_number|string
|
||||||
cart_owner: 必须显式提交,且值必须等于当前 token 对应 openid|string
|
cart_owner: 必须显式提交非空 owner|string
|
||||||
cart_product_id: cart_product_id|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
cart_product_quantity: cart_product_quantity|integer
|
cart_product_quantity: cart_product_quantity|integer,可为空
|
||||||
cart_status: cart_status|string
|
cart_status: cart_status|string,可为空
|
||||||
cart_at_price: cart_at_price|number
|
cart_at_price: cart_at_price|number,可为空
|
||||||
cart_remark: cart_remark|string
|
cart_remark: cart_remark|string
|
||||||
responses:
|
responses:
|
||||||
"200":
|
"200":
|
||||||
@@ -751,7 +753,7 @@ paths:
|
|||||||
cart_number: 购物车名称或分组号|string
|
cart_number: 购物车名称或分组号|string
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
cart_create: 购物车项创建时间,由数据库自动生成|string
|
||||||
cart_owner: 购物车所有者 openid|string
|
cart_owner: 购物车所有者 openid|string
|
||||||
cart_product_id: 产品业务 ID|string
|
cart_product_id: 关联产品的 PocketBase recordId|string
|
||||||
cart_product_quantity: 产品数量|integer
|
cart_product_quantity: 产品数量|integer
|
||||||
cart_status: 购物车状态|string
|
cart_status: 购物车状态|string
|
||||||
cart_at_price: 加入购物车时价格|number
|
cart_at_price: 加入购物车时价格|number
|
||||||
@@ -963,77 +965,6 @@ paths:
|
|||||||
业务响应数据字段|string: 业务响应数据值|string
|
业务响应数据字段|string: 业务响应数据值|string
|
||||||
components:
|
components:
|
||||||
schemas:
|
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:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
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:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
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
|
|
||||||
example:
|
|
||||||
pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
PocketBaseNativeError:
|
PocketBaseNativeError:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -1041,13 +972,14 @@ components:
|
|||||||
type:
|
type:
|
||||||
- integer
|
- integer
|
||||||
- string
|
- string
|
||||||
description: 业务状态码
|
description: PocketBase错误码
|
||||||
example: 错误状态码 | integer
|
example: 错误状态码 | integer
|
||||||
message:
|
message:
|
||||||
type: string
|
type: string
|
||||||
|
description: PocketBase错误信息
|
||||||
example: PocketBase原生错误信息 | string
|
example: PocketBase原生错误信息 | string
|
||||||
data:
|
data:
|
||||||
description: 业务响应数据
|
description: PocketBase错误数据
|
||||||
type: object
|
type: object
|
||||||
additionalProperties: true
|
additionalProperties: true
|
||||||
example:
|
example:
|
||||||
@@ -1088,245 +1020,6 @@ components:
|
|||||||
collectionName: collectionName|string
|
collectionName: collectionName|string
|
||||||
created: 记录创建时间|string
|
created: 记录创建时间|string
|
||||||
updated: 记录更新时间|string
|
updated: 记录更新时间|string
|
||||||
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
|
|
||||||
example:
|
|
||||||
pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
cart_number: 购物车名称或分组号|string
|
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
|
||||||
cart_owner: 购物车所有者 openid|string
|
|
||||||
cart_product_id: 产品业务 ID|string
|
|
||||||
cart_product_quantity: 产品数量|integer
|
|
||||||
cart_status: 购物车状态|string
|
|
||||||
cart_at_price: 加入购物车时价格|integer
|
|
||||||
cart_remark: 备注|string
|
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
|
||||||
product_name: 产品名称(服务端联动补充)|string
|
|
||||||
product_modelnumber: 产品型号(服务端联动补充)|string
|
|
||||||
product_basic_price: 产品基础价格(服务端联动补充)|integer
|
|
||||||
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
|
|
||||||
example:
|
|
||||||
keyword: 对 cart_id、cart_number、cart_product_id 等索引相关字段的统一模糊搜索关键字|string
|
|
||||||
cart_status: 购物车状态精确过滤|string
|
|
||||||
cart_number: 购物车名称或分组号精确过滤|string
|
|
||||||
CartDetailRequest:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- cart_id
|
|
||||||
properties:
|
|
||||||
cart_id:
|
|
||||||
type: string
|
|
||||||
description: 购物车业务 ID
|
|
||||||
example: CART-1770000000000-abc123
|
|
||||||
example:
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
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: 小程序加入购物车示例
|
|
||||||
example:
|
|
||||||
cart_number: 可选;未传时服务端自动生成|string
|
|
||||||
cart_product_id: 产品业务 ID|string
|
|
||||||
cart_product_quantity: 产品数量,需为正整数|integer
|
|
||||||
cart_status: 可选;未传时默认 有效|string
|
|
||||||
cart_at_price: 加入购物车时价格|integer
|
|
||||||
cart_remark: 备注|string
|
|
||||||
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
|
|
||||||
example:
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
cart_number: cart_number|string
|
|
||||||
cart_product_id: cart_product_id|string
|
|
||||||
cart_product_quantity: cart_product_quantity|integer
|
|
||||||
cart_status: cart_status|string
|
|
||||||
cart_at_price: cart_at_price|integer
|
|
||||||
cart_remark: cart_remark|string
|
|
||||||
CartDeleteRequest:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- cart_id
|
|
||||||
properties:
|
|
||||||
cart_id:
|
|
||||||
type: string
|
|
||||||
description: 购物车业务 ID
|
|
||||||
example: CART-1770000000000-abc123
|
|
||||||
example:
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
CartListResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
items:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/CartRecord
|
|
||||||
example:
|
|
||||||
items:
|
|
||||||
- pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
cart_number: 购物车名称或分组号|string
|
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
|
||||||
cart_owner: 购物车所有者 openid|string
|
|
||||||
cart_product_id: 产品业务 ID|string
|
|
||||||
cart_product_quantity: 产品数量|integer
|
|
||||||
cart_status: 购物车状态|string
|
|
||||||
cart_at_price: 加入购物车时价格|integer
|
|
||||||
cart_remark: 备注|string
|
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
|
||||||
product_name: 产品名称(服务端联动补充)|string
|
|
||||||
product_modelnumber: 产品型号(服务端联动补充)|string
|
|
||||||
product_basic_price: 产品基础价格(服务端联动补充)|integer
|
|
||||||
CartDeleteResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
cart_id:
|
|
||||||
type: string
|
|
||||||
description: 购物车业务 ID
|
|
||||||
example: CART-1770000000000-abc123
|
|
||||||
is_delete:
|
|
||||||
type:
|
|
||||||
- integer
|
|
||||||
- number
|
|
||||||
description: 目标软删除标记值;当前实现可能仍返回物理删除结果
|
|
||||||
example: 1
|
|
||||||
example:
|
|
||||||
cart_id: 购物车业务 ID|string
|
|
||||||
is_delete: 目标软删除标记值;当前实现可能仍返回物理删除结果|integer
|
|
||||||
PocketBaseCartFields:
|
PocketBaseCartFields:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
@@ -1348,7 +1041,11 @@ components:
|
|||||||
example: wx-openid-user-001
|
example: wx-openid-user-001
|
||||||
cart_product_id:
|
cart_product_id:
|
||||||
type: string
|
type: string
|
||||||
description: 产品业务 ID
|
description: 关联产品的 PocketBase recordId
|
||||||
|
example: pbc_product_record_id|string
|
||||||
|
cart_product_business_id:
|
||||||
|
type: string
|
||||||
|
description: 关联产品的业务 ID(prod_list_id),用于展示
|
||||||
example: PROD-1770000000000-abcd12
|
example: PROD-1770000000000-abcd12
|
||||||
cart_product_quantity:
|
cart_product_quantity:
|
||||||
type:
|
type:
|
||||||
@@ -1375,7 +1072,8 @@ components:
|
|||||||
cart_number: 购物车名称或分组号|string
|
cart_number: 购物车名称或分组号|string
|
||||||
cart_create: 购物车项创建时间,由数据库自动生成|string
|
cart_create: 购物车项创建时间,由数据库自动生成|string
|
||||||
cart_owner: 购物车所有者 openid|string
|
cart_owner: 购物车所有者 openid|string
|
||||||
cart_product_id: 产品业务 ID|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
|
cart_product_business_id: 产品业务 ID|string
|
||||||
cart_product_quantity: 产品数量|integer
|
cart_product_quantity: 产品数量|integer
|
||||||
cart_status: 购物车状态|string
|
cart_status: 购物车状态|string
|
||||||
cart_at_price: 加入购物车时价格|number
|
cart_at_price: 加入购物车时价格|number
|
||||||
@@ -1402,13 +1100,8 @@ components:
|
|||||||
PocketBaseCartCreateRequest:
|
PocketBaseCartCreateRequest:
|
||||||
type: object
|
type: object
|
||||||
required:
|
required:
|
||||||
- cart_id
|
|
||||||
- cart_number
|
|
||||||
- cart_owner
|
- cart_owner
|
||||||
- cart_product_id
|
- cart_product_id
|
||||||
- cart_product_quantity
|
|
||||||
- cart_status
|
|
||||||
- cart_at_price
|
|
||||||
properties:
|
properties:
|
||||||
cart_id:
|
cart_id:
|
||||||
type: string
|
type: string
|
||||||
@@ -1416,9 +1109,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
cart_owner:
|
cart_owner:
|
||||||
type: string
|
type: string
|
||||||
description: 必须显式提交,且值必须等于当前 token 对应 openid
|
description: 必须显式提交非空 owner
|
||||||
cart_product_id:
|
cart_product_id:
|
||||||
type: string
|
type: string
|
||||||
|
description: tbl_product_list 的 PocketBase recordId
|
||||||
cart_product_quantity:
|
cart_product_quantity:
|
||||||
type:
|
type:
|
||||||
- integer
|
- integer
|
||||||
@@ -1432,13 +1126,11 @@ components:
|
|||||||
cart_remark:
|
cart_remark:
|
||||||
type: string
|
type: string
|
||||||
example:
|
example:
|
||||||
cart_id: cart_id|string
|
cart_owner: 必须显式提交非空 owner|string
|
||||||
cart_number: cart_number|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
cart_owner: 必须显式提交,且值必须等于当前 token 对应 openid|string
|
cart_product_quantity: cart_product_quantity|integer,可为空
|
||||||
cart_product_id: cart_product_id|string
|
cart_status: cart_status|string,可为空
|
||||||
cart_product_quantity: cart_product_quantity|integer
|
cart_at_price: cart_at_price|number,可为空
|
||||||
cart_status: cart_status|string
|
|
||||||
cart_at_price: cart_at_price|number
|
|
||||||
cart_remark: cart_remark|string
|
cart_remark: cart_remark|string
|
||||||
PocketBaseCartUpdateRequest:
|
PocketBaseCartUpdateRequest:
|
||||||
type: object
|
type: object
|
||||||
@@ -1447,9 +1139,10 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
cart_owner:
|
cart_owner:
|
||||||
type: string
|
type: string
|
||||||
description: 若提交,必须仍等于当前 token 对应 openid
|
description: 若提交,必须仍为非空 owner
|
||||||
cart_product_id:
|
cart_product_id:
|
||||||
type: string
|
type: string
|
||||||
|
description: tbl_product_list 的 PocketBase recordId
|
||||||
cart_product_quantity:
|
cart_product_quantity:
|
||||||
type:
|
type:
|
||||||
- integer
|
- integer
|
||||||
@@ -1464,8 +1157,8 @@ components:
|
|||||||
type: string
|
type: string
|
||||||
example:
|
example:
|
||||||
cart_number: cart_number|string
|
cart_number: cart_number|string
|
||||||
cart_owner: 若提交,必须仍等于当前 token 对应 openid|string
|
cart_owner: 若提交,必须仍为非空 owner|string
|
||||||
cart_product_id: cart_product_id|string
|
cart_product_id: tbl_product_list 的 PocketBase recordId|string
|
||||||
cart_product_quantity: cart_product_quantity|integer
|
cart_product_quantity: cart_product_quantity|integer
|
||||||
cart_status: cart_status|string
|
cart_status: cart_status|string
|
||||||
cart_at_price: cart_at_price|number
|
cart_at_price: cart_at_price|number
|
||||||
|
|||||||
48
pocket-base/spec/openapi-wx/openapi.yaml
Normal file
48
pocket-base/spec/openapi-wx/openapi.yaml
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
openapi: 3.1.0
|
||||||
|
info:
|
||||||
|
title: BAI PocketBase Native API
|
||||||
|
version: 1.0.0-wx-folder
|
||||||
|
description: |
|
||||||
|
本目录仅收敛 PocketBase 原生 API 文档,不包含自定义 hooks API。
|
||||||
|
|
||||||
|
文档约定:
|
||||||
|
- 不单独配置鉴权组件;如接口需要登录,请直接在说明中关注 `Authorization: Bearer <token>`
|
||||||
|
- 示例字段值统一使用 `<字段说明>|<类型>` 风格
|
||||||
|
- 原生接口返回 PocketBase 标准 records 结构,不使用 hooks 统一响应信封
|
||||||
|
servers:
|
||||||
|
- url: https://bai-api.blv-oa.com
|
||||||
|
description: 生产环境
|
||||||
|
- url: http://127.0.0.1:8090
|
||||||
|
description: PocketBase 本地环境
|
||||||
|
tags:
|
||||||
|
- name: 企业信息
|
||||||
|
description: PocketBase 原生公司记录接口
|
||||||
|
- name: 附件信息
|
||||||
|
description: PocketBase 原生附件记录接口
|
||||||
|
- name: 产品信息
|
||||||
|
description: PocketBase 原生产品记录接口
|
||||||
|
- name: 文档信息
|
||||||
|
description: PocketBase 原生文档记录接口
|
||||||
|
- name: 购物车
|
||||||
|
description: PocketBase 原生购物车记录接口
|
||||||
|
- name: 订单
|
||||||
|
description: PocketBase 原生订单记录接口
|
||||||
|
paths:
|
||||||
|
/pb/api/collections/tbl_company/records:
|
||||||
|
$ref: './company.yaml#/paths/companyRecords'
|
||||||
|
/pb/api/collections/tbl_company/records/{recordId}:
|
||||||
|
$ref: './company.yaml#/paths/companyRecordById'
|
||||||
|
/pb/api/collections/tbl_attachments/records:
|
||||||
|
$ref: './attachments.yaml#/paths/attachmentRecords'
|
||||||
|
/pb/api/collections/tbl_product_list/records:
|
||||||
|
$ref: './products.yaml#/paths/productRecords'
|
||||||
|
/pb/api/collections/tbl_document/records:
|
||||||
|
$ref: './documents.yaml#/paths/documentRecords'
|
||||||
|
/pb/api/collections/tbl_cart/records:
|
||||||
|
$ref: './cart.yaml#/paths/cartRecords'
|
||||||
|
/pb/api/collections/tbl_cart/records/{recordId}:
|
||||||
|
$ref: './cart.yaml#/paths/cartRecordById'
|
||||||
|
/pb/api/collections/tbl_order/records:
|
||||||
|
$ref: './order.yaml#/paths/orderRecords'
|
||||||
|
/pb/api/collections/tbl_order/records/{recordId}:
|
||||||
|
$ref: './order.yaml#/paths/orderRecordById'
|
||||||
@@ -1,229 +1,163 @@
|
|||||||
paths:
|
paths:
|
||||||
orderList:
|
orderRecords:
|
||||||
post:
|
get:
|
||||||
operationId: postOrderList
|
tags: [订单]
|
||||||
tags:
|
summary: 查询订单记录列表
|
||||||
- 订单
|
|
||||||
summary: 按索引字段模糊查询订单列表
|
|
||||||
description: |
|
description: |
|
||||||
调用自定义 hooks API 查询当前登录用户的订单列表。
|
使用 PocketBase 原生 records list 接口查询 `tbl_order`。
|
||||||
|
如需鉴权,请自行在请求头中携带 `Authorization: Bearer <token>`。
|
||||||
查询规则:
|
parameters:
|
||||||
- 仅允许查询当前 `Authorization` 对应用户自己的订单记录
|
- name: filter
|
||||||
- 支持通过 `keyword` 对多个索引相关字段做统一模糊搜索,当前实现覆盖:
|
in: query
|
||||||
- `order_id`
|
|
||||||
- `order_number`
|
|
||||||
- `order_source_id`
|
|
||||||
- 支持按 `order_status` 精确过滤
|
|
||||||
- 支持按 `order_source` 精确过滤
|
|
||||||
|
|
||||||
目标软删除契约:
|
|
||||||
- 目标行为应仅返回 `is_delete = 0` 的记录
|
|
||||||
- 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
requestBody:
|
|
||||||
required: false
|
required: false
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/OrderListRequest
|
type: string
|
||||||
example:
|
description: PocketBase 原生过滤表达式
|
||||||
keyword: 对 order_id、order_number、order_source_id 等索引相关字段的统一模糊搜索关键字|string
|
example: order_id="订单业务ID|string"
|
||||||
order_status: 订单状态精确过滤|string
|
- name: page
|
||||||
order_source: 订单来源精确过滤|string
|
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
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: -order_create
|
||||||
responses:
|
responses:
|
||||||
"200":
|
'200':
|
||||||
description: 查询成功
|
description: 查询成功
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/OrderListResponse
|
$ref: '#/components/schemas/PocketBaseOrderListResponse'
|
||||||
example:
|
'400':
|
||||||
items:
|
description: 查询参数错误
|
||||||
- pb_id: PocketBase 记录主键 id|string
|
'401':
|
||||||
created: PocketBase 系统创建时间|string
|
description: token 无效或已过期
|
||||||
updated: PocketBase 系统更新时间|string
|
'403':
|
||||||
order_id: 订单业务 ID|string
|
description: 无权访问
|
||||||
order_number: 订单编号|string
|
'500':
|
||||||
order_create: 订单创建时间,由数据库自动生成|string
|
|
||||||
order_owner: 订单所有者 openid|string
|
|
||||||
order_source: 订单来源|string
|
|
||||||
order_status: 订单状态|string
|
|
||||||
order_source_id: 来源关联业务 ID|string
|
|
||||||
order_snap:
|
|
||||||
order_snap字段|string: order_snap值|string
|
|
||||||
order_amount: 订单金额|integer
|
|
||||||
order_remark: 备注|string
|
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
|
||||||
"400":
|
|
||||||
description: 参数错误
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"401":
|
|
||||||
description: token 缺失、无效或已过期
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"415":
|
|
||||||
description: 请求体必须为 application/json
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"500":
|
|
||||||
description: 服务端错误
|
description: 服务端错误
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
orderDetail:
|
|
||||||
post:
|
post:
|
||||||
operationId: postOrderDetail
|
tags: [订单]
|
||||||
tags:
|
summary: 创建订单记录
|
||||||
- 订单
|
|
||||||
summary: 按 order_id 精确查询订单详情
|
|
||||||
description: |
|
description: |
|
||||||
调用自定义 hooks API 按 `order_id` 查询单条订单记录。
|
使用 PocketBase 原生 records create 接口向 `tbl_order` 新增记录。
|
||||||
|
如集合规则要求 `order_owner` 与当前用户一致,请客户端显式提交。
|
||||||
查询规则:
|
|
||||||
- 仅允许访问当前 `Authorization` 对应用户自己的订单记录
|
|
||||||
- 查询键为业务 ID `order_id`,不是 PocketBase 原生 `recordId`
|
|
||||||
|
|
||||||
目标软删除契约:
|
|
||||||
- 目标行为应仅允许查询 `is_delete = 0` 的记录
|
|
||||||
- 当前仓库实现尚未显式追加 `is_delete = 0` 过滤,请以实际后端代码为准
|
|
||||||
security:
|
|
||||||
- BearerAuth: []
|
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
required: true
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/OrderDetailRequest
|
$ref: '#/components/schemas/PocketBaseOrderCreateRequest'
|
||||||
example:
|
example:
|
||||||
order_id: 订单业务 ID|string
|
order_id: 订单业务ID|string
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: 查询成功
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/OrderRecord
|
|
||||||
example:
|
|
||||||
pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
order_number: 订单编号|string
|
order_number: 订单编号|string
|
||||||
order_create: 订单创建时间,由数据库自动生成|string
|
order_owner: 订单所有者openid|string
|
||||||
order_owner: 订单所有者 openid|string
|
|
||||||
order_source: 订单来源|string
|
order_source: 订单来源|string
|
||||||
order_status: 订单状态|string
|
order_status: 订单状态|string
|
||||||
order_source_id: 来源关联业务 ID|string
|
order_source_id: 来源关联业务ID|string
|
||||||
order_snap:
|
order_snap:
|
||||||
order_snap字段|string: order_snap值|string
|
字段名|string: 字段值|string
|
||||||
order_amount: 订单金额|integer
|
order_amount: 订单金额|number
|
||||||
order_remark: 备注|string
|
order_remark: 备注|string
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 创建成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PocketBaseOrderRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误或违反集合规则
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'403':
|
||||||
|
description: 无权访问
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
orderRecordById:
|
||||||
|
patch:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 更新订单记录
|
||||||
|
description: |
|
||||||
|
使用 PocketBase 原生 records update 接口更新 `tbl_order`。
|
||||||
|
路径参数使用 PocketBase 原生 `recordId`。
|
||||||
|
parameters:
|
||||||
|
- name: recordId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: PocketBase记录主键|string
|
||||||
|
requestBody:
|
||||||
|
required: true
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PocketBaseOrderUpdateRequest'
|
||||||
|
example:
|
||||||
|
order_number: 订单编号|string
|
||||||
|
order_owner: 订单所有者openid|string
|
||||||
|
order_source: 订单来源|string
|
||||||
|
order_status: 订单状态|string
|
||||||
|
order_source_id: 来源关联业务ID|string
|
||||||
|
order_snap:
|
||||||
|
字段名|string: 字段值|string
|
||||||
|
order_amount: 订单金额|number
|
||||||
|
order_remark: 备注|string
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: 更新成功
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/PocketBaseOrderRecord'
|
||||||
|
'400':
|
||||||
|
description: 参数错误
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'404':
|
||||||
|
description: 记录不存在
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
|
delete:
|
||||||
|
tags: [订单]
|
||||||
|
summary: 删除订单记录
|
||||||
|
description: |
|
||||||
|
使用 PocketBase 原生 records delete 接口删除 `tbl_order`。
|
||||||
|
路径参数使用 PocketBase 原生 `recordId`。
|
||||||
|
parameters:
|
||||||
|
- name: recordId
|
||||||
|
in: path
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
example: PocketBase记录主键|string
|
||||||
|
responses:
|
||||||
|
'204':
|
||||||
|
description: 删除成功
|
||||||
|
'401':
|
||||||
|
description: token 无效或已过期
|
||||||
|
'404':
|
||||||
|
description: 记录不存在
|
||||||
|
'500':
|
||||||
|
description: 服务端错误
|
||||||
"400":
|
"400":
|
||||||
description: 参数错误
|
description: 参数错误
|
||||||
content:
|
components:
|
||||||
application/json:
|
schemas:
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"401":
|
|
||||||
description: token 缺失、无效或已过期
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"403":
|
|
||||||
description: 无权访问该订单记录
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"404":
|
|
||||||
description: 未找到对应订单记录
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"415":
|
|
||||||
description: 请求体必须为 application/json
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"500":
|
|
||||||
description: 服务端错误
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
orderCreate:
|
|
||||||
post:
|
|
||||||
operationId: postOrderCreate
|
|
||||||
tags:
|
|
||||||
- 订单
|
|
||||||
summary: 新增订单记录
|
|
||||||
description: |
|
|
||||||
调用自定义 hooks API 新增一条订单记录。
|
|
||||||
|
|
||||||
创建规则:
|
创建规则:
|
||||||
- 服务端自动根据当前 token 写入 `order_owner`
|
- 服务端自动根据当前 token 写入 `order_owner`
|
||||||
- 服务端自动生成 `order_id`
|
- 服务端自动生成 `order_id`
|
||||||
@@ -231,12 +165,13 @@ paths:
|
|||||||
- `order_source`、`order_source_id`、`order_snap`、`order_amount` 为必填
|
- `order_source`、`order_source_id`、`order_snap`、`order_amount` 为必填
|
||||||
|
|
||||||
目标软删除契约:
|
目标软删除契约:
|
||||||
- 新建记录应默认为 `is_delete = 0`
|
description: PocketBase错误码
|
||||||
- 当前仓库导出响应中尚未显式返回 `is_delete` 字段
|
- 当前仓库导出响应中尚未显式返回 `is_delete` 字段
|
||||||
security:
|
security:
|
||||||
- BearerAuth: []
|
description: PocketBase错误信息
|
||||||
|
example: PocketBase原生错误信息 | string
|
||||||
requestBody:
|
requestBody:
|
||||||
required: true
|
description: PocketBase错误数据
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
@@ -1088,253 +1023,6 @@ components:
|
|||||||
collectionName: collectionName|string
|
collectionName: collectionName|string
|
||||||
created: 记录创建时间|string
|
created: 记录创建时间|string
|
||||||
updated: 记录更新时间|string
|
updated: 记录更新时间|string
|
||||||
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
|
|
||||||
example:
|
|
||||||
pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
order_number: 订单编号|string
|
|
||||||
order_create: 订单创建时间,由数据库自动生成|string
|
|
||||||
order_owner: 订单所有者 openid|string
|
|
||||||
order_source: 订单来源|string
|
|
||||||
order_status: 订单状态|string
|
|
||||||
order_source_id: 来源关联业务 ID|string
|
|
||||||
order_snap:
|
|
||||||
order_snap字段|string: order_snap值|string
|
|
||||||
order_amount: 订单金额|integer
|
|
||||||
order_remark: 备注|string
|
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
|
||||||
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: 购物车
|
|
||||||
example:
|
|
||||||
keyword: 对 order_id、order_number、order_source_id 等索引相关字段的统一模糊搜索关键字|string
|
|
||||||
order_status: 订单状态精确过滤|string
|
|
||||||
order_source: 订单来源精确过滤|string
|
|
||||||
OrderDetailRequest:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- order_id
|
|
||||||
properties:
|
|
||||||
order_id:
|
|
||||||
type: string
|
|
||||||
description: 订单业务 ID
|
|
||||||
example: ORDER-1770000000000-abc123
|
|
||||||
example:
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
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: 小程序订单示例
|
|
||||||
example:
|
|
||||||
order_number: 可选;未传时服务端自动生成|string
|
|
||||||
order_source: 订单来源|string
|
|
||||||
order_status: 可选;未传时默认 订单已生成|string
|
|
||||||
order_source_id: 来源关联业务 ID|string
|
|
||||||
order_snap:
|
|
||||||
order_snap字段|string: order_snap值|string
|
|
||||||
order_amount: 订单金额|integer
|
|
||||||
order_remark: 备注|string
|
|
||||||
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
|
|
||||||
example:
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
order_number: order_number|string
|
|
||||||
order_source: order_source|string
|
|
||||||
order_status: order_status|string
|
|
||||||
order_source_id: order_source_id|string
|
|
||||||
order_snap:
|
|
||||||
order_snap字段|string: order_snap值|string
|
|
||||||
order_amount: order_amount|integer
|
|
||||||
order_remark: order_remark|string
|
|
||||||
OrderDeleteRequest:
|
|
||||||
type: object
|
|
||||||
required:
|
|
||||||
- order_id
|
|
||||||
properties:
|
|
||||||
order_id:
|
|
||||||
type: string
|
|
||||||
description: 订单业务 ID
|
|
||||||
example: ORDER-1770000000000-abc123
|
|
||||||
example:
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
OrderListResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
items:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/OrderRecord
|
|
||||||
example:
|
|
||||||
items:
|
|
||||||
- pb_id: PocketBase 记录主键 id|string
|
|
||||||
created: PocketBase 系统创建时间|string
|
|
||||||
updated: PocketBase 系统更新时间|string
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
order_number: 订单编号|string
|
|
||||||
order_create: 订单创建时间,由数据库自动生成|string
|
|
||||||
order_owner: 订单所有者 openid|string
|
|
||||||
order_source: 订单来源|string
|
|
||||||
order_status: 订单状态|string
|
|
||||||
order_source_id: 来源关联业务 ID|string
|
|
||||||
order_snap:
|
|
||||||
order_snap字段|string: order_snap值|string
|
|
||||||
order_amount: 订单金额|integer
|
|
||||||
order_remark: 备注|string
|
|
||||||
is_delete: 软删除标记;目标契约字段,当前 hooks 响应可能尚未显式透出|integer
|
|
||||||
OrderDeleteResponse:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
order_id:
|
|
||||||
type: string
|
|
||||||
description: 订单业务 ID
|
|
||||||
example: ORDER-1770000000000-abc123
|
|
||||||
is_delete:
|
|
||||||
type:
|
|
||||||
- integer
|
|
||||||
- number
|
|
||||||
description: 目标软删除标记值;当前实现可能仍返回物理删除结果
|
|
||||||
example: 1
|
|
||||||
example:
|
|
||||||
order_id: 订单业务 ID|string
|
|
||||||
is_delete: 目标软删除标记值;当前实现可能仍返回物理删除结果|integer
|
|
||||||
PocketBaseOrderFields:
|
PocketBaseOrderFields:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
|
|||||||
@@ -1,253 +0,0 @@
|
|||||||
paths:
|
|
||||||
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
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
total_users: total_users|integer
|
|
||||||
"400":
|
|
||||||
description: 请求参数错误
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"500":
|
|
||||||
description: 服务端错误
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
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
|
|
||||||
example:
|
|
||||||
users_wx_code: 当前 token 失效时,可通过该 code 重新签发 token。|string
|
|
||||||
responses:
|
|
||||||
"200":
|
|
||||||
description: 刷新成功
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/RefreshTokenResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
token: 新签发的 PocketBase 原生 auth token|string
|
|
||||||
"400":
|
|
||||||
description: 参数错误或微信 code 换取失败
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"401":
|
|
||||||
description: token 无效,且未提供有效的 `users_wx_code`
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"404":
|
|
||||||
description: 当前用户不存在
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"415":
|
|
||||||
description: 请求体不是 JSON
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"429":
|
|
||||||
description: 请求过于频繁
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
"500":
|
|
||||||
description: 服务端错误
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: ../openapi-wx.yaml#/components/schemas/ErrorResponse
|
|
||||||
example:
|
|
||||||
statusCode: 业务状态码|integer
|
|
||||||
errMsg: 业务提示信息|string
|
|
||||||
data:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
components:
|
|
||||||
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:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
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:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
SystemRefreshTokenRequest:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
users_wx_code:
|
|
||||||
type:
|
|
||||||
- string
|
|
||||||
- "null"
|
|
||||||
description: |
|
|
||||||
可选。
|
|
||||||
当前 token 失效时,可通过该 code 重新签发 token。
|
|
||||||
example: 0a1b2c3d4e5f6g
|
|
||||||
example:
|
|
||||||
users_wx_code: 当前 token 失效时,可通过该 code 重新签发 token。|string
|
|
||||||
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:
|
|
||||||
业务响应数据字段|string: 业务响应数据值|string
|
|
||||||
token: 新签发的 PocketBase 原生 auth token|string
|
|
||||||
UsersCountData:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
total_users:
|
|
||||||
type:
|
|
||||||
- integer
|
|
||||||
- string
|
|
||||||
example: 用户总数 | integer
|
|
||||||
example:
|
|
||||||
total_users: total_users|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: total_users|integer
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -9,6 +9,7 @@
|
|||||||
"init:documents": "node pocketbase.documents.js",
|
"init:documents": "node pocketbase.documents.js",
|
||||||
"init:product-list": "node pocketbase.product-list.js",
|
"init:product-list": "node pocketbase.product-list.js",
|
||||||
"init:dictionary": "node pocketbase.dictionary.js",
|
"init:dictionary": "node pocketbase.dictionary.js",
|
||||||
|
"init:scheme": "node pocketbase.scheme.js",
|
||||||
"migrate:file-fields": "node pocketbase.file-fields-to-attachments.js",
|
"migrate:file-fields": "node pocketbase.file-fields-to-attachments.js",
|
||||||
"migrate:add-is-delete-field": "node pocketbase.add-is-delete-field.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:apply-soft-delete-rules": "node pocketbase.apply-soft-delete-rules.js",
|
||||||
|
|||||||
@@ -24,24 +24,38 @@ if (!AUTH_TOKEN) {
|
|||||||
|
|
||||||
const pb = new PocketBase(PB_URL);
|
const pb = new PocketBase(PB_URL);
|
||||||
|
|
||||||
const collections = [
|
async function getCollectionIdByName(collectionName) {
|
||||||
|
const list = await pb.collections.getFullList({
|
||||||
|
sort: '-created',
|
||||||
|
});
|
||||||
|
const target = list.find((item) => item.name === collectionName);
|
||||||
|
if (!target) {
|
||||||
|
throw new Error(`未找到集合:${collectionName}`);
|
||||||
|
}
|
||||||
|
return target.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function buildCollections() {
|
||||||
|
const productCollectionId = await getCollectionIdByName('tbl_product_list');
|
||||||
|
|
||||||
|
return [
|
||||||
{
|
{
|
||||||
name: 'tbl_cart',
|
name: 'tbl_cart',
|
||||||
type: 'base',
|
type: 'base',
|
||||||
listRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE} && ${SOFT_DELETE_RULE}`,
|
listRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE} && ${SOFT_DELETE_RULE}`,
|
||||||
viewRule: `${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`,
|
createRule: '@request.body.cart_owner != ""',
|
||||||
updateRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
|
updateRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
|
||||||
deleteRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
|
deleteRule: `${OWNER_AUTH_RULE} && ${CART_OWNER_MATCH_RULE}`,
|
||||||
fields: [
|
fields: [
|
||||||
{ name: 'cart_id', type: 'text', required: true, autogeneratePattern: 'CART-[0-9]{13}-[A-Za-z0-9]{6}' },
|
{ 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_number', type: 'text', required: false },
|
||||||
{ name: 'cart_create', type: 'autodate', onCreate: true, onUpdate: false },
|
{ name: 'cart_create', type: 'autodate', onCreate: true, onUpdate: false },
|
||||||
{ name: 'cart_owner', type: 'text', required: true },
|
{ name: 'cart_owner', type: 'text', required: true },
|
||||||
{ name: 'cart_product_id', type: 'text', required: true },
|
{ name: 'cart_product_id', type: 'relation', required: true, collectionId: productCollectionId, maxSelect: 1, cascadeDelete: false },
|
||||||
{ name: 'cart_product_quantity', type: 'number', required: true },
|
{ name: 'cart_product_quantity', type: 'number', required: false },
|
||||||
{ name: 'cart_status', type: 'text', required: true },
|
{ name: 'cart_status', type: 'text', required: false },
|
||||||
{ name: 'cart_at_price', type: 'number', required: true },
|
{ name: 'cart_at_price', type: 'number', required: false },
|
||||||
{ name: 'cart_remark', type: 'text' },
|
{ name: 'cart_remark', type: 'text' },
|
||||||
{ name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
|
{ name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
|
||||||
],
|
],
|
||||||
@@ -88,7 +102,8 @@ const collections = [
|
|||||||
'CREATE INDEX idx_tbl_order_owner_status ON tbl_order (order_owner, order_status)',
|
'CREATE INDEX idx_tbl_order_owner_status ON tbl_order (order_owner, order_status)',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeFieldPayload(field, existingField) {
|
function normalizeFieldPayload(field, existingField) {
|
||||||
const payload = existingField
|
const payload = existingField
|
||||||
@@ -114,6 +129,12 @@ function normalizeFieldPayload(field, existingField) {
|
|||||||
payload.onUpdate = typeof field.onUpdate === 'boolean' ? field.onUpdate : false;
|
payload.onUpdate = typeof field.onUpdate === 'boolean' ? field.onUpdate : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (field.type === 'relation') {
|
||||||
|
payload.collectionId = field.collectionId;
|
||||||
|
payload.maxSelect = typeof field.maxSelect === 'number' ? field.maxSelect : 1;
|
||||||
|
payload.cascadeDelete = typeof field.cascadeDelete === 'boolean' ? field.cascadeDelete : false;
|
||||||
|
}
|
||||||
|
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,6 +291,8 @@ async function init() {
|
|||||||
pb.authStore.save(AUTH_TOKEN, null);
|
pb.authStore.save(AUTH_TOKEN, null);
|
||||||
console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。');
|
console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。');
|
||||||
|
|
||||||
|
const collections = await buildCollections();
|
||||||
|
|
||||||
for (const collectionData of collections) {
|
for (const collectionData of collections) {
|
||||||
await createOrUpdateCollection(collectionData);
|
await createOrUpdateCollection(collectionData);
|
||||||
}
|
}
|
||||||
|
|||||||
328
script/pocketbase.scheme.js
Normal file
328
script/pocketbase.scheme.js
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
import { createRequire } from 'module';
|
||||||
|
import PocketBase from 'pocketbase';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
|
let runtimeConfig = {};
|
||||||
|
try {
|
||||||
|
runtimeConfig = require('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js');
|
||||||
|
} catch (_error) {
|
||||||
|
runtimeConfig = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
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 OWNER_AUTH_RULE = '@request.auth.id != ""';
|
||||||
|
const SOFT_DELETE_RULE = 'is_delete = 0';
|
||||||
|
|
||||||
|
if (!AUTH_TOKEN) {
|
||||||
|
console.error('❌ 缺少 POCKETBASE_AUTH_TOKEN,无法执行方案相关建表。');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pb = new PocketBase(PB_URL);
|
||||||
|
|
||||||
|
const collections = [
|
||||||
|
{
|
||||||
|
name: 'tbl_scheme_template',
|
||||||
|
type: 'base',
|
||||||
|
listRule: `${OWNER_AUTH_RULE} && scheme_template_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
viewRule: `${OWNER_AUTH_RULE} && scheme_template_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
createRule: `${OWNER_AUTH_RULE} && @request.body.scheme_template_owner = @request.auth.openid`,
|
||||||
|
updateRule: `${OWNER_AUTH_RULE} && scheme_template_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
deleteRule: `${OWNER_AUTH_RULE} && scheme_template_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
fields: [
|
||||||
|
{ name: 'scheme_template_id', type: 'text', required: true, autogeneratePattern: 'SCHTPL-[0-9]{13}-[A-Za-z0-9]{6}' },
|
||||||
|
{ name: 'scheme_template_icon', type: 'text' },
|
||||||
|
{ name: 'scheme_template_label', type: 'text' },
|
||||||
|
{ name: 'scheme_template_name', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_template_owner', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_template_status', type: 'text' },
|
||||||
|
{ name: 'scheme_template_solution_type', type: 'text' },
|
||||||
|
{ name: 'scheme_template_solution_feature', type: 'text' },
|
||||||
|
{ name: 'scheme_template_product_list', type: 'json' },
|
||||||
|
{ name: 'scheme_template_description', type: 'text' },
|
||||||
|
{ name: 'scheme_template_remark', type: 'text' },
|
||||||
|
{ name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
|
||||||
|
],
|
||||||
|
indexes: [
|
||||||
|
'CREATE UNIQUE INDEX idx_tbl_scheme_template_scheme_template_id ON tbl_scheme_template (scheme_template_id)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_scheme_template_owner ON tbl_scheme_template (scheme_template_owner)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_scheme_template_name ON tbl_scheme_template (scheme_template_name)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_scheme_template_label ON tbl_scheme_template (scheme_template_label)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_scheme_template_status ON tbl_scheme_template (scheme_template_status)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_solution_type ON tbl_scheme_template (scheme_template_solution_type)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_solution_feature ON tbl_scheme_template (scheme_template_solution_feature)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_template_owner_status ON tbl_scheme_template (scheme_template_owner, scheme_template_status)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tbl_scheme',
|
||||||
|
type: 'base',
|
||||||
|
listRule: `${OWNER_AUTH_RULE} && scheme_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
viewRule: `${OWNER_AUTH_RULE} && scheme_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
createRule: `${OWNER_AUTH_RULE} && @request.body.scheme_owner = @request.auth.openid`,
|
||||||
|
updateRule: `${OWNER_AUTH_RULE} && scheme_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
deleteRule: `${OWNER_AUTH_RULE} && scheme_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
fields: [
|
||||||
|
{ name: 'scheme_id', type: 'text', required: true, autogeneratePattern: 'SCHEME-[0-9]{13}-[A-Za-z0-9]{6}' },
|
||||||
|
{ name: 'scheme_name', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_owner', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_share_status', type: 'text' },
|
||||||
|
{ name: 'scheme_expires_at', type: 'date' },
|
||||||
|
{ name: 'scheme_hotel_type', type: 'text' },
|
||||||
|
{ name: 'scheme_solution_type', type: 'text' },
|
||||||
|
{ name: 'scheme_solution_feature', type: 'text' },
|
||||||
|
{ name: 'scheme_room_type', type: 'json' },
|
||||||
|
{ name: 'scheme_curtains', type: 'text' },
|
||||||
|
{ name: 'scheme_voice_device', type: 'text' },
|
||||||
|
{ name: 'scheme_ac_type', type: 'text' },
|
||||||
|
{ name: 'scheme_template_highend', type: 'text' },
|
||||||
|
{ name: 'scheme_template_midend', type: 'text' },
|
||||||
|
{ name: 'scheme_template_lowend', type: 'text' },
|
||||||
|
{ name: 'scheme_status', type: 'text' },
|
||||||
|
{ name: 'scheme_remark', type: 'text' },
|
||||||
|
{ name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
|
||||||
|
],
|
||||||
|
indexes: [
|
||||||
|
'CREATE UNIQUE INDEX idx_tbl_scheme_scheme_id ON tbl_scheme (scheme_id)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_owner ON tbl_scheme (scheme_owner)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_name ON tbl_scheme (scheme_name)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_share_status ON tbl_scheme (scheme_share_status)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_expires_at ON tbl_scheme (scheme_expires_at)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_hotel_type ON tbl_scheme (scheme_hotel_type)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_solution_type ON tbl_scheme (scheme_solution_type)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_solution_feature ON tbl_scheme (scheme_solution_feature)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_scheme_status ON tbl_scheme (scheme_status)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_owner_status ON tbl_scheme (scheme_owner, scheme_status)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'tbl_scheme_share',
|
||||||
|
type: 'base',
|
||||||
|
listRule: `${OWNER_AUTH_RULE} && scheme_share_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
viewRule: `${OWNER_AUTH_RULE} && scheme_share_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
createRule: `${OWNER_AUTH_RULE} && @request.body.scheme_share_owner = @request.auth.openid`,
|
||||||
|
updateRule: `${OWNER_AUTH_RULE} && scheme_share_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
deleteRule: `${OWNER_AUTH_RULE} && scheme_share_owner = @request.auth.openid && ${SOFT_DELETE_RULE}`,
|
||||||
|
fields: [
|
||||||
|
{ name: 'scheme_share_id', type: 'text', required: true, autogeneratePattern: 'SCHSHARE-[0-9]{13}-[A-Za-z0-9]{6}' },
|
||||||
|
{ name: 'scheme_id', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_share_owner', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_share_to', type: 'text', required: true },
|
||||||
|
{ name: 'scheme_share_acceptance_status', type: 'text' },
|
||||||
|
{ name: 'scheme_share_expires_at', type: 'date' },
|
||||||
|
{ name: 'scheme_share_permission', type: 'text' },
|
||||||
|
{ name: 'scheme_share_remark', type: 'text' },
|
||||||
|
{ name: 'is_delete', type: 'number', default: 0, min: 0, max: 1, onlyInt: true },
|
||||||
|
],
|
||||||
|
indexes: [
|
||||||
|
'CREATE UNIQUE INDEX idx_tbl_scheme_share_scheme_share_id ON tbl_scheme_share (scheme_share_id)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_share_scheme_id ON tbl_scheme_share (scheme_id)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_share_scheme_share_owner ON tbl_scheme_share (scheme_share_owner)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_share_scheme_share_to ON tbl_scheme_share (scheme_share_to)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_share_acceptance_status ON tbl_scheme_share (scheme_share_acceptance_status)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_share_expires_at ON tbl_scheme_share (scheme_share_expires_at)',
|
||||||
|
'CREATE INDEX idx_tbl_scheme_share_owner_to ON tbl_scheme_share (scheme_share_owner, scheme_share_to)',
|
||||||
|
'CREATE UNIQUE INDEX idx_tbl_scheme_share_unique_map ON tbl_scheme_share (scheme_share_owner, scheme_id, scheme_share_to)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function normalizeFieldPayload(field, existingField) {
|
||||||
|
const payload = existingField
|
||||||
|
? Object.assign({}, existingField)
|
||||||
|
: {
|
||||||
|
name: field.name,
|
||||||
|
type: field.type,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (existingField && existingField.id) {
|
||||||
|
payload.id = existingField.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.name = field.name;
|
||||||
|
payload.type = field.type;
|
||||||
|
|
||||||
|
if (typeof field.required !== 'undefined') {
|
||||||
|
payload.required = field.required;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 === 'date') {
|
||||||
|
payload.required = !!field.required;
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCollectionPayload(collectionData, existingCollection) {
|
||||||
|
if (!existingCollection) {
|
||||||
|
return {
|
||||||
|
name: collectionData.name,
|
||||||
|
type: collectionData.type,
|
||||||
|
listRule: Object.prototype.hasOwnProperty.call(collectionData, 'listRule') ? collectionData.listRule : null,
|
||||||
|
viewRule: Object.prototype.hasOwnProperty.call(collectionData, 'viewRule') ? collectionData.viewRule : null,
|
||||||
|
createRule: Object.prototype.hasOwnProperty.call(collectionData, 'createRule') ? collectionData.createRule : null,
|
||||||
|
updateRule: Object.prototype.hasOwnProperty.call(collectionData, 'updateRule') ? collectionData.updateRule : null,
|
||||||
|
deleteRule: Object.prototype.hasOwnProperty.call(collectionData, 'deleteRule') ? collectionData.deleteRule : null,
|
||||||
|
fields: collectionData.fields.map((field) => normalizeFieldPayload(field, null)),
|
||||||
|
indexes: collectionData.indexes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetFieldMap = new Map(collectionData.fields.map((field) => [field.name, field]));
|
||||||
|
const fields = (existingCollection.fields || []).map((existingField) => {
|
||||||
|
const targetField = targetFieldMap.get(existingField.name);
|
||||||
|
if (!targetField) {
|
||||||
|
return existingField;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetFieldMap.delete(existingField.name);
|
||||||
|
return normalizeFieldPayload(targetField, existingField);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const field of targetFieldMap.values()) {
|
||||||
|
fields.push(normalizeFieldPayload(field, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: collectionData.name,
|
||||||
|
type: collectionData.type,
|
||||||
|
listRule: Object.prototype.hasOwnProperty.call(collectionData, 'listRule') ? collectionData.listRule : existingCollection.listRule,
|
||||||
|
viewRule: Object.prototype.hasOwnProperty.call(collectionData, 'viewRule') ? collectionData.viewRule : existingCollection.viewRule,
|
||||||
|
createRule: Object.prototype.hasOwnProperty.call(collectionData, 'createRule') ? collectionData.createRule : existingCollection.createRule,
|
||||||
|
updateRule: Object.prototype.hasOwnProperty.call(collectionData, 'updateRule') ? collectionData.updateRule : existingCollection.updateRule,
|
||||||
|
deleteRule: Object.prototype.hasOwnProperty.call(collectionData, 'deleteRule') ? collectionData.deleteRule : existingCollection.deleteRule,
|
||||||
|
fields: fields,
|
||||||
|
indexes: collectionData.indexes,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeFieldList(fields) {
|
||||||
|
return (fields || []).map((field) => ({
|
||||||
|
name: field.name,
|
||||||
|
type: field.type,
|
||||||
|
required: !!field.required,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createOrUpdateCollection(collectionData) {
|
||||||
|
console.log(`🔄 正在处理表: ${collectionData.name} ...`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const list = await pb.collections.getFullList({
|
||||||
|
sort: '-created',
|
||||||
|
});
|
||||||
|
const existing = list.find((item) => item.name === collectionData.name);
|
||||||
|
|
||||||
|
if (existing) {
|
||||||
|
await pb.collections.update(existing.id, buildCollectionPayload(collectionData, existing));
|
||||||
|
console.log(`♻️ ${collectionData.name} 已存在,已按最新结构更新。`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await pb.collections.create(buildCollectionPayload(collectionData, null));
|
||||||
|
console.log(`✅ ${collectionData.name} 创建完成。`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`❌ 处理集合 ${collectionData.name} 失败:`, {
|
||||||
|
status: error.status,
|
||||||
|
message: error.message,
|
||||||
|
response: error.response,
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCollectionByName(collectionName) {
|
||||||
|
const list = await pb.collections.getFullList({
|
||||||
|
sort: '-created',
|
||||||
|
});
|
||||||
|
return list.find((item) => item.name === collectionName) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function verifyCollections(targetCollections) {
|
||||||
|
console.log('\n🔍 开始校验方案相关表结构与索引...');
|
||||||
|
|
||||||
|
for (const target of targetCollections) {
|
||||||
|
const remote = await getCollectionByName(target.name);
|
||||||
|
if (!remote) {
|
||||||
|
throw new Error(`${target.name} 不存在`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteFields = normalizeFieldList(remote.fields);
|
||||||
|
const targetFields = normalizeFieldList(target.fields);
|
||||||
|
const remoteFieldMap = new Map(remoteFields.map((field) => [field.name, field.type]));
|
||||||
|
const remoteRequiredMap = new Map(remoteFields.map((field) => [field.name, field.required]));
|
||||||
|
const missingFields = [];
|
||||||
|
const mismatchedTypes = [];
|
||||||
|
const mismatchedRequired = [];
|
||||||
|
|
||||||
|
for (const field of targetFields) {
|
||||||
|
if (!remoteFieldMap.has(field.name)) {
|
||||||
|
missingFields.push(field.name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteFieldMap.get(field.name) !== field.type) {
|
||||||
|
mismatchedTypes.push(`${field.name}:${remoteFieldMap.get(field.name)}!=${field.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (remoteRequiredMap.get(field.name) !== !!field.required) {
|
||||||
|
mismatchedRequired.push(`${field.name}:${remoteRequiredMap.get(field.name)}!=${!!field.required}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const remoteIndexes = new Set(remote.indexes || []);
|
||||||
|
const missingIndexes = target.indexes.filter((indexSql) => !remoteIndexes.has(indexSql));
|
||||||
|
|
||||||
|
if (remote.type !== target.type) {
|
||||||
|
throw new Error(`${target.name} 类型不匹配,期望 ${target.type},实际 ${remote.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!missingFields.length && !mismatchedTypes.length && !mismatchedRequired.length && !missingIndexes.length) {
|
||||||
|
console.log(`✅ ${target.name} 校验通过。`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`❌ ${target.name} 校验失败:`);
|
||||||
|
if (missingFields.length) {
|
||||||
|
console.log(` - 缺失字段: ${missingFields.join(', ')}`);
|
||||||
|
}
|
||||||
|
if (mismatchedTypes.length) {
|
||||||
|
console.log(` - 字段类型不匹配: ${mismatchedTypes.join(', ')}`);
|
||||||
|
}
|
||||||
|
if (mismatchedRequired.length) {
|
||||||
|
console.log(` - 字段必填属性不匹配: ${mismatchedRequired.join(', ')}`);
|
||||||
|
}
|
||||||
|
if (missingIndexes.length) {
|
||||||
|
console.log(` - 缺失索引: ${missingIndexes.join(' | ')}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`${target.name} 结构与预期不一致`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init() {
|
||||||
|
try {
|
||||||
|
console.log(`🔄 正在连接 PocketBase: ${PB_URL}`);
|
||||||
|
pb.authStore.save(AUTH_TOKEN, null);
|
||||||
|
console.log('✅ 已使用 POCKETBASE_AUTH_TOKEN 载入认证状态。');
|
||||||
|
|
||||||
|
for (const collectionData of collections) {
|
||||||
|
await createOrUpdateCollection(collectionData);
|
||||||
|
}
|
||||||
|
|
||||||
|
await verifyCollections(collections);
|
||||||
|
console.log('\n🎉 方案相关表结构初始化并校验完成!');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ 初始化失败:', error.response?.data || error.message);
|
||||||
|
process.exitCode = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
init();
|
||||||
Reference in New Issue
Block a user