feat: 更新 API 响应结构,统一使用 statusCode 和 errMsg 替代 code 和 msg;新增用户类型和公司 ID 字段;优化数据库索引;添加公司所有者同步测试脚本

This commit is contained in:
2026-03-29 19:57:04 +08:00
parent e9fe1165e3
commit 50c09d855b
50 changed files with 851 additions and 601 deletions

View File

@@ -171,7 +171,7 @@ hooks 查询时统一联查 `tbl_attachments`,并补充:
处理方式:
- 登录路由本地 try/catch 显式返回 `{ code, msg, data }`
- 登录路由本地 try/catch 显式返回 `{ statusCode, errMsg, data }`
- 全局错误包装提前注册
- 保存 auth 用户时透传原始错误信息
@@ -409,3 +409,5 @@ hooks 查询时统一联查 `tbl_attachments`,并补充:
1. `refresh-token` 当前仅依赖 `users_wx_openid`,未校验旧 token
2. 微信手机号能力依赖微信官方 `access_token``users_phone_code`
3. 当前后端为 JavaScript + Express 架构,未引入 TypeScript 编译链

View File

@@ -8,7 +8,7 @@
- 基础路径(生产):`https://bai-api.blv-oa.com/pb/api`
- 基础路径(本地):`http://localhost:8090/pb/api`
- 响应格式JSON
- 业务响应结构统一为:`code``msg``data`
- 业务响应结构统一为:`statusCode``errMsg``data`
- 当前公开接口统一使用 **POST** 方法
- 微信写接口统一要求 `Content-Type: application/json`
@@ -20,8 +20,8 @@
```json
{
"code": 200,
"msg": "操作成功",
"statusCode": 200,
"errMsg": "操作成功",
"data": {}
}
```
@@ -30,8 +30,8 @@
```json
{
"code": 400,
"msg": "错误信息",
"statusCode": 400,
"errMsg": "错误信息",
"data": {}
}
```
@@ -51,8 +51,8 @@
```json
{
"code": 200,
"msg": "请求成功",
"statusCode": 200,
"errMsg": "请求成功",
"data": {
"message": "Hello, World!",
"timestamp": "2026-03-20T00:00:00.000Z",
@@ -72,8 +72,8 @@
```json
{
"code": 200,
"msg": "服务运行正常",
"statusCode": 200,
"errMsg": "服务运行正常",
"data": {
"status": "healthy",
"timestamp": "2026-03-20T00:00:00.000Z"
@@ -122,8 +122,8 @@
```json
{
"code": 200,
"msg": "登录成功",
"statusCode": 200,
"errMsg": "登录成功",
"data": {
"status": "login_success",
"is_info_complete": true,
@@ -203,8 +203,8 @@
```json
{
"code": 200,
"msg": "信息更新成功",
"statusCode": 200,
"errMsg": "信息更新成功",
"data": {
"status": "update_success",
"user": {
@@ -258,8 +258,8 @@
```json
{
"code": 200,
"msg": "刷新成功",
"statusCode": 200,
"errMsg": "刷新成功",
"data": {
"token": "new-jwt-token"
}
@@ -305,3 +305,5 @@ Authorization: Bearer <token>
```
不需要旧 `Authorization`

View File

@@ -108,7 +108,7 @@ npm run test
```json
{
"code": 200,
"statusCode": 200,
"message": "success",
"data": {}
}
@@ -118,7 +118,7 @@ npm run test
```json
{
"code": 400,
"statusCode": 400,
"message": "错误信息",
"data": null
}
@@ -151,3 +151,4 @@ npm run test
## 部署说明
详见 [部署文档](deployment.md)。

View File

@@ -2,7 +2,7 @@
> 来源:线上 PocketBase collection 回读、`script/pocketbase.newpb.js`
> 类型:`auth`
> 读写规则:仅管理员 / 管理角色允许
> 读写规则:公开允许新增与修改;列表 / 详情 / 删除仍仅管理员 / 管理角色允许
## 表用途

View File

@@ -39,7 +39,7 @@
| :--- | :--- | :--- |
| `idx_company_id` | `UNIQUE INDEX` | 保证 `company_id` 唯一 |
| `idx_company_usci` | `INDEX` | 加速按统一社会信用代码查询 |
| `idx_company_owner_openid` | `INDEX` | 加速按公司所有者查询 |
| `idx_company_owner_openid` | `UNIQUE INDEX` | 限制同一公司所有者仅能绑定一个公司 |
## 补充约定

View File

@@ -1,5 +1,7 @@
/// <reference path="../pb_data/types.d.ts" />
const { fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
routerUse(function (e) {
try {
return e.next()
@@ -9,11 +11,7 @@ routerUse(function (e) {
|| (err && typeof err.status === 'number' && err.status)
|| 500
return e.json(status, {
code: status,
msg: (err && err.message) || '服务器内部错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '服务器内部错误', (err && err.data) || {}, status)
}
})
@@ -47,3 +45,5 @@ require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/sdk-permission/collection-sa
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/sdk-permission/manageplatform-sync.js`)
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/wechat/login.js`)
require(`${__hooks}/bai_api_pb_hooks/bai_api_routes/wechat/profile.js`)

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/attachment/delete', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,14 @@ routerAdd('POST', '/api/attachment/delete', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('删除附件失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '删除附件失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/attachment/detail', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -16,13 +16,14 @@ routerAdd('POST', '/api/attachment/detail', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询附件详情失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询附件详情失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/attachment/list', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -18,13 +18,14 @@ routerAdd('POST', '/api/attachment/list', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询附件列表失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询附件列表失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/attachment/upload', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
const authState = guards.requireManagePlatformUser(e)
@@ -16,13 +16,14 @@ routerAdd('POST', '/api/attachment/upload', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('上传附件失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '上传附件失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
}, $apis.bodyLimit(536870912))

View File

@@ -1,17 +1,7 @@
onRecordAfterCreateSuccess((e) => {
try {
const authRecord = e && e.auth ? e.auth : null
if (!authRecord || authRecord.collection().name !== 'tbl_auth_users') {
return
}
if (authRecord.getString('users_type') === '服务商') {
return
}
authRecord.set('users_type', '服务商')
$app.save(authRecord)
} catch (_error) {
// Keep company create flow unchanged even if user type sync fails.
}
onRecordCreateRequest((e) => {
return e.next()
}, 'tbl_company')
onRecordUpdateRequest((e) => {
return e.next()
}, 'tbl_company')

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/dictionary/create', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const dictionaryService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/dictionaryService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,13 @@ routerAdd('POST', '/api/dictionary/create', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('新增字典失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '新增字典失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/dictionary/delete', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const dictionaryService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/dictionaryService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,13 @@ routerAdd('POST', '/api/dictionary/delete', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('删除字典失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '删除字典失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/dictionary/detail', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const dictionaryService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/dictionaryService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -15,13 +15,14 @@ routerAdd('POST', '/api/dictionary/detail', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询字典详情失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询字典详情失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/dictionary/list', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const dictionaryService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/dictionaryService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,14 @@ routerAdd('POST', '/api/dictionary/list', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询字典列表失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询字典列表失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/dictionary/update', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const dictionaryService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/dictionaryService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,13 @@ routerAdd('POST', '/api/dictionary/update', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('修改字典失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '修改字典失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/document-history/list', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -18,13 +18,14 @@ routerAdd('POST', '/api/document-history/list', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询文档操作历史失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询文档操作历史失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/document/create', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,14 @@ routerAdd('POST', '/api/document/create', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('新增文档失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '新增文档失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/document/delete', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,14 @@ routerAdd('POST', '/api/document/delete', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('删除文档失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '删除文档失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/document/detail', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -16,13 +16,14 @@ routerAdd('POST', '/api/document/detail', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询文档详情失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询文档详情失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/document/list', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -18,13 +18,14 @@ routerAdd('POST', '/api/document/list', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('查询文档列表失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '查询文档列表失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/document/update', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const documentService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/documentService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { success } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { success, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -17,13 +17,14 @@ routerAdd('POST', '/api/document/update', function (e) {
const status = (err && err.statusCode) || (err && err.status) || 400
logger.error('修改文档失败', {
status: status,
message: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '修改文档失败',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/platform/login', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const userService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/userService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { successWithToken } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { successWithToken, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -20,14 +20,14 @@ routerAdd('POST', '/api/platform/login', function (e) {
logger.error('平台登录失败', {
status: status,
message: (err && err.message) || '未知错误',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '平台登录失败',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/platform/register', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const userService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/userService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { successWithToken } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { successWithToken, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -21,14 +21,14 @@ routerAdd('POST', '/api/platform/register', function (e) {
logger.error('平台注册失败', {
status: status,
message: (err && err.message) || '未知错误',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '平台注册失败',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -12,3 +12,5 @@ routerAdd('POST', '/api/sdk-permission/collection-save', function (e) {
return success(e, '保存集合权限成功', data)
})

View File

@@ -11,3 +11,5 @@ routerAdd('POST', '/api/sdk-permission/context', function (e) {
return success(e, '查询权限管理上下文成功', data)
})

View File

@@ -11,3 +11,5 @@ routerAdd('POST', '/api/sdk-permission/manageplatform-sync', function (e) {
return success(e, '已同步 ManagePlatform 业务全权限', data)
})

View File

@@ -12,3 +12,5 @@ routerAdd('POST', '/api/sdk-permission/role-delete', function (e) {
return success(e, '删除角色成功', data)
})

View File

@@ -12,3 +12,5 @@ routerAdd('POST', '/api/sdk-permission/role-save', function (e) {
return success(e, '保存角色成功', data)
})

View File

@@ -12,3 +12,5 @@ routerAdd('POST', '/api/sdk-permission/user-role-update', function (e) {
return success(e, '更新用户角色成功', data)
})

View File

@@ -8,3 +8,5 @@ routerAdd('POST', '/api/system/health', function (e) {
timestamp: new Date().toISOString(),
})
})

View File

@@ -9,3 +9,5 @@ routerAdd('POST', '/api/system/test-helloworld', function (e) {
build_time: env.buildTime,
})
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/system/refresh-token', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const userService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/userService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { successWithToken } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { successWithToken, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -19,11 +19,7 @@ routerAdd('POST', '/api/system/refresh-token', function (e) {
}
if (!payload.users_wx_code) {
return e.json(401, {
code: 401,
msg: 'token已过期请上传users_wx_code',
data: {},
})
return fail(e, 'token已过期请上传users_wx_code', {}, 401)
}
const authData = userService.authenticateWechatUser(payload)
@@ -38,14 +34,14 @@ routerAdd('POST', '/api/system/refresh-token', function (e) {
logger.error('系统刷新令牌失败', {
status: status,
message: (err && err.message) || '未知错误',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '刷新失败',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -28,3 +28,4 @@ routerAdd('POST', '/api/system/users-count', function (e) {
total_users: total,
})
})

View File

@@ -2,7 +2,7 @@ routerAdd('POST', '/api/wechat/login', function (e) {
const guards = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/middlewares/requestGuards.js`)
const userService = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/services/userService.js`)
const logger = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/logger.js`)
const { successWithToken } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
const { successWithToken, fail } = require(`${__hooks}/bai_api_pb_hooks/bai_api_shared/utils/response.js`)
try {
guards.requireJson(e)
@@ -21,14 +21,15 @@ routerAdd('POST', '/api/wechat/login', function (e) {
logger.error('微信登录失败', {
status: status,
message: (err && err.message) || '未知错误',
errMsg: (err && err.message) || '未知错误',
data: (err && err.data) || {},
})
return e.json(status, {
code: status,
msg: (err && err.message) || '微信登录失败',
data: (err && err.data) || {},
})
return fail(e, (err && err.message) || '操作失败', (err && err.data) || {}, status)
}
})

View File

@@ -12,3 +12,5 @@ routerAdd('POST', '/api/wechat/profile', function (e) {
return success(e, '信息更新成功', data)
})

View File

@@ -29,6 +29,8 @@ function validateProfileBody(e) {
users_name: Object.prototype.hasOwnProperty.call(payload, 'users_name') ? String(payload.users_name || '').trim() : undefined,
users_phone_code: Object.prototype.hasOwnProperty.call(payload, 'users_phone_code') ? String(payload.users_phone_code || '').trim() : undefined,
users_phone: Object.prototype.hasOwnProperty.call(payload, 'users_phone') ? String(payload.users_phone || '').trim() : undefined,
users_type: Object.prototype.hasOwnProperty.call(payload, 'users_type') ? String(payload.users_type || '').trim() : undefined,
company_id: Object.prototype.hasOwnProperty.call(payload, 'company_id') ? String(payload.company_id || '').trim() : undefined,
users_tag: Object.prototype.hasOwnProperty.call(payload, 'users_tag') ? String(payload.users_tag || '').trim() : undefined,
users_picture: Object.prototype.hasOwnProperty.call(payload, 'users_picture') ? String(payload.users_picture || '').trim() : undefined,
users_id_pic_a: Object.prototype.hasOwnProperty.call(payload, 'users_id_pic_a') ? payload.users_id_pic_a : undefined,
@@ -74,14 +76,15 @@ function validatePlatformRegisterBody(e) {
function validatePlatformLoginBody(e) {
const payload = parseBody(e)
const loginAccount = payload.login_account || payload.users_phone || payload.email || ''
const loginAccount = String(payload.login_account || payload.users_phone || payload.email || '').trim()
const password = String(payload.password || '')
if (!loginAccount) throw createAppError(400, 'login_account 为必填项,支持邮箱或手机号')
if (!payload.password) throw createAppError(400, 'password 为必填项')
if (!password) throw createAppError(400, 'password 为必填项')
return {
login_account: loginAccount,
password: payload.password,
password: password,
}
}

View File

@@ -112,6 +112,32 @@ function parseHttpJsonResponse(response) {
})
}
function decodeHttpBodyText(body) {
if (typeof body === 'string') {
return body
}
if (Array.isArray(body)) {
return body.map(function (code) {
return String.fromCharCode(Number(code) || 0)
}).join('')
}
if (body && typeof body.length === 'number' && typeof body !== 'function') {
try {
let text = ''
for (let i = 0; i < body.length; i += 1) {
text += String.fromCharCode(Number(body[i]) || 0)
}
return text
} catch (_err) {
return String(body || '')
}
}
return String(body || '')
}
function getCompanyByCompanyId(companyId) {
if (!companyId) return null
const records = $app.findRecordsByFilter('tbl_company', 'company_id = {:companyId}', '', 1, 0, {
@@ -120,6 +146,14 @@ function getCompanyByCompanyId(companyId) {
return records.length ? records[0] : null
}
function getCompanyByOwnerOpenid(openid) {
if (!openid) return null
const records = $app.findRecordsByFilter('tbl_company', 'company_owner_openid = {:openid}', '', 1, 0, {
openid: openid,
})
return records.length ? records[0] : null
}
function exportCompany(companyRecord) {
if (!companyRecord) return null
return {
@@ -211,9 +245,17 @@ function applyUserAttachmentFields(record, payload) {
}
function enrichUser(userRecord) {
const companyId = userRecord.getString('company_id')
const companyRecord = getCompanyByCompanyId(companyId)
const openid = userRecord.getString('openid')
let companyId = userRecord.getString('company_id')
let companyRecord = getCompanyByCompanyId(companyId)
if (!companyRecord && openid) {
companyRecord = getCompanyByOwnerOpenid(openid)
if (companyRecord) {
companyId = companyRecord.getString('company_id')
}
}
const userPicture = resolveUserAttachment(userRecord.getString('users_picture'))
const userIdPicA = resolveUserAttachment(userRecord.getString('users_id_pic_a'))
const userIdPicB = resolveUserAttachment(userRecord.getString('users_id_pic_b'))
@@ -259,8 +301,8 @@ function enrichUser(userRecord) {
function buildAuthMeta(user) {
return {
code: 200,
msg: '操作成功',
statusCode: 200,
errMsg: '操作成功',
data: {
status: user.status,
is_info_complete: user.is_info_complete,
@@ -472,9 +514,25 @@ function authenticatePlatformUser(payload) {
})
if (!authResponse || authResponse.statusCode < 200 || authResponse.statusCode >= 300) {
throw createAppError(400, '平台登录失败', {
const responseBodyText = authResponse ? decodeHttpBodyText(authResponse.body) : ''
let responseBody = {}
try {
responseBody = responseBodyText ? JSON.parse(responseBodyText) : {}
} catch (_err) {
responseBody = {}
}
const originalMessage = responseBody.message || ''
const normalizedMessage = originalMessage === 'Something went wrong while processing your request.'
? '账号或密码错误'
: (originalMessage || '平台登录失败')
throw createAppError(400, normalizedMessage, {
statusCode: authResponse ? authResponse.statusCode : 0,
body: authResponse ? String(authResponse.body || '') : '',
originalMessage: originalMessage,
originalData: responseBody.data || {},
body: responseBodyText,
})
}
@@ -538,12 +596,19 @@ function updateWechatUserProfile(usersWxOpenid, payload) {
if (usersPhone) {
currentUser.set('users_phone', usersPhone)
}
if (typeof payload.users_type !== 'undefined' && payload.users_type) {
currentUser.set('users_type', payload.users_type)
}
if (typeof payload.company_id !== 'undefined' && payload.company_id) {
currentUser.set('company_id', payload.company_id)
}
if (typeof payload.users_tag !== 'undefined' && payload.users_tag) {
currentUser.set('users_tag', payload.users_tag)
}
applyUserAttachmentFields(currentUser, payload)
const shouldPromote = ((currentUser.getString('users_type') || GUEST_USER_TYPE) === GUEST_USER_TYPE)
&& typeof payload.users_type === 'undefined'
&& isInfoComplete(currentUser)
if (shouldPromote) {
currentUser.set('users_type', REGISTERED_USER_TYPE)
@@ -582,8 +647,8 @@ function refreshAuthToken(openid) {
authRecord: userRecord,
authMethod: '',
meta: {
code: 200,
msg: '刷新成功',
statusCode: 200,
errMsg: '刷新成功',
data: {},
},
}
@@ -623,3 +688,4 @@ module.exports = {
issueAuthToken,
registerPlatformUser,
}

View File

@@ -1,27 +1,64 @@
function applyHttpMeta(e, code, msg) {
const statusCode = code || 200
const errMsg = msg || '操作成功'
try {
const res = e && typeof e.response === 'function' ? e.response() : null
const headers = res && typeof res.header === 'function' ? res.header() : null
if (headers && typeof headers.set === 'function') {
headers.set('X-Status-Code', String(statusCode))
headers.set('X-Err-Msg', encodeURIComponent(errMsg))
headers.set('Access-Control-Expose-Headers', 'X-Status-Code, X-Err-Msg')
}
} catch (_err) {
// Header injection failures should not break API responses.
}
return {
statusCode,
errMsg,
}
}
function normalizePayloadData(data) {
if (typeof data === 'undefined' || data === null) {
return {}
}
if (data && typeof data === 'object' && !Array.isArray(data)) {
return data
}
return {
data: data,
}
}
function success(e, msg, data, code) {
return e.json(code || 200, {
code: code || 200,
msg: msg || '操作成功',
data: data || {},
})
const meta = applyHttpMeta(e, code || 200, msg || '操作成功')
return e.json(meta.statusCode, normalizePayloadData(data))
}
function successWithToken(e, msg, data, token, code) {
const statusCode = code || 200
const payload = {
code: statusCode,
msg: msg || '操作成功',
data: data || {},
}
const meta = applyHttpMeta(e, code || 200, msg || '操作成功')
const payload = normalizePayloadData(data)
if (token) {
payload.token = token
}
return e.json(statusCode, payload)
return e.json(meta.statusCode, payload)
}
function fail(e, msg, data, code) {
const meta = applyHttpMeta(e, code || 400, msg || '操作失败')
return e.json(meta.statusCode, normalizePayloadData(data))
}
module.exports = {
success,
successWithToken,
fail,
}

View File

@@ -257,6 +257,33 @@ routerAdd('GET', '/manage/dictionary-manage', function (e) {
return localStorage.getItem(tokenKey) || ''
}
function decodeErrMsg(value) {
if (!value) {
return ''
}
try {
return decodeURIComponent(value)
} catch (_err) {
return String(value)
}
}
function unwrapApiResponse(data, res) {
const safe = data && typeof data === 'object' ? data : {}
const headerCode = Number(res && res.headers ? (res.headers.get('X-Status-Code') || 0) : 0)
const headerErrMsg = decodeErrMsg(res && res.headers ? res.headers.get('X-Err-Msg') : '')
const code = Number(safe.statusCode || safe.code || headerCode || 0)
const errMsg = safe.errMsg || safe.msg || headerErrMsg || ''
const hasLegacyEnvelope = Object.prototype.hasOwnProperty.call(safe, 'data')
&& (Object.prototype.hasOwnProperty.call(safe, 'code')
|| Object.prototype.hasOwnProperty.call(safe, 'statusCode')
|| Object.prototype.hasOwnProperty.call(safe, 'msg')
|| Object.prototype.hasOwnProperty.call(safe, 'errMsg'))
const payload = hasLegacyEnvelope ? (safe.data || {}) : safe
return { code: code, errMsg: errMsg, payload: payload }
}
async function request(url, payload) {
const token = getToken()
if (!token) {
@@ -275,18 +302,19 @@ routerAdd('GET', '/manage/dictionary-manage', function (e) {
})
const data = await res.json()
if (!res.ok || data.code >= 400) {
if (res.status === 401 || res.status === 403 || data.code === 401 || data.code === 403) {
const unwrapped = unwrapApiResponse(data, res)
if (!res.ok || unwrapped.code >= 400) {
if (res.status === 401 || res.status === 403 || unwrapped.code === 401 || unwrapped.code === 403) {
localStorage.removeItem('pb_manage_token')
localStorage.removeItem('pb_manage_logged_in')
localStorage.removeItem('pb_manage_login_account')
localStorage.removeItem('pb_manage_login_time')
window.location.replace('/pb/manage/login')
}
throw new Error(data.msg || '请求失败')
throw new Error(unwrapped.errMsg || '请求失败')
}
return data.data
return unwrapped.payload
}
async function parseJsonSafe(res) {
@@ -345,17 +373,18 @@ routerAdd('GET', '/manage/dictionary-manage', function (e) {
const parsed = await parseJsonSafe(res)
const data = parsed.json
if (!res.ok || !data || data.code >= 400) {
const unwrapped = unwrapApiResponse(data, res)
if (!res.ok || !data || unwrapped.code >= 400) {
if (res.status === 413) {
throw new Error('上传图片失败:文件已超过当前网关允许的请求体大小,或线上服务仍在运行旧版 hooks。')
}
if (!parsed.isJson && parsed.text) {
throw new Error('上传图片失败:服务端返回了非 JSON 响应,通常表示网关或反向代理提前拦截了上传请求。')
}
throw new Error((data && data.msg) || '上传图片失败')
throw new Error(unwrapped.errMsg || '上传图片失败')
}
return data.data
return unwrapped.payload
}
function escapeHtml(value) {

View File

@@ -708,6 +708,33 @@ routerAdd('GET', '/manage/document-manage', function (e) {
}
}
function decodeErrMsg(value) {
if (!value) {
return ''
}
try {
return decodeURIComponent(value)
} catch (_err) {
return String(value)
}
}
function unwrapApiResponse(data, res) {
const safe = data && typeof data === 'object' ? data : {}
const headerCode = Number(res && res.headers ? (res.headers.get('X-Status-Code') || 0) : 0)
const headerErrMsg = decodeErrMsg(res && res.headers ? res.headers.get('X-Err-Msg') : '')
const code = Number(safe.statusCode || safe.code || headerCode || 0)
const errMsg = safe.errMsg || safe.msg || headerErrMsg || ''
const hasLegacyEnvelope = Object.prototype.hasOwnProperty.call(safe, 'data')
&& (Object.prototype.hasOwnProperty.call(safe, 'code')
|| Object.prototype.hasOwnProperty.call(safe, 'statusCode')
|| Object.prototype.hasOwnProperty.call(safe, 'msg')
|| Object.prototype.hasOwnProperty.call(safe, 'errMsg'))
const payload = hasLegacyEnvelope ? (safe.data || {}) : safe
return { code: code, errMsg: errMsg, payload: payload }
}
async function requestJson(path, payload) {
const token = getToken()
if (!token) {
@@ -725,21 +752,22 @@ routerAdd('GET', '/manage/document-manage', function (e) {
})
const data = await res.json()
if (!res.ok || !data || data.code >= 400) {
if (res.status === 401 || res.status === 403 || data.code === 401 || data.code === 403) {
const unwrapped = unwrapApiResponse(data, res)
if (!res.ok || !data || unwrapped.code >= 400) {
if (res.status === 401 || res.status === 403 || unwrapped.code === 401 || unwrapped.code === 403) {
localStorage.removeItem('pb_manage_token')
localStorage.removeItem('pb_manage_logged_in')
window.location.replace('/pb/manage/login')
}
const details = data && data.data ? data.data : {}
const details = unwrapped.payload && typeof unwrapped.payload === 'object' ? unwrapped.payload : {}
const detailMessage = details.originalMessage || details.body || ''
const finalMessage = [(data && data.msg) || '请求失败', detailMessage].filter(function (item, index, arr) {
const finalMessage = [unwrapped.errMsg || '请求失败', detailMessage].filter(function (item, index, arr) {
return item && arr.indexOf(item) === index
}).join('')
throw new Error(finalMessage || '请求失败')
}
return data.data
return unwrapped.payload
}
async function parseJsonSafe(res) {
@@ -797,7 +825,8 @@ routerAdd('GET', '/manage/document-manage', function (e) {
const parsed = await parseJsonSafe(res)
const data = parsed.json
if (!res.ok || !data || data.code >= 400) {
const unwrapped = unwrapApiResponse(data, res)
if (!res.ok || !data || unwrapped.code >= 400) {
if (res.status === 413) {
throw new Error('上传' + label + '失败:文件已超过当前网关允许的请求体大小,或线上服务仍在运行旧版 hooks。')
}
@@ -806,10 +835,10 @@ routerAdd('GET', '/manage/document-manage', function (e) {
throw new Error('上传' + label + '失败:服务端返回了非 JSON 响应,通常表示网关或反向代理提前拦截了上传请求。')
}
throw new Error((data && data.msg) || ('上传' + label + '失败'))
throw new Error(unwrapped.errMsg || ('上传' + label + '失败'))
}
return data.data
return unwrapped.payload
}
async function uploadPendingFiles(fileItems, label) {

View File

@@ -245,6 +245,33 @@ routerAdd('GET', '/manage/sdk-permission-manage', function (e) {
}
}
function decodeErrMsg(value) {
if (!value) {
return ''
}
try {
return decodeURIComponent(value)
} catch (_err) {
return String(value)
}
}
function unwrapApiResponse(data, res) {
const safe = data && typeof data === 'object' ? data : {}
const headerCode = Number(res && res.headers ? (res.headers.get('X-Status-Code') || 0) : 0)
const headerErrMsg = decodeErrMsg(res && res.headers ? res.headers.get('X-Err-Msg') : '')
const code = Number(safe.statusCode || safe.code || headerCode || 0)
const errMsg = safe.errMsg || safe.msg || headerErrMsg || ''
const hasLegacyEnvelope = Object.prototype.hasOwnProperty.call(safe, 'data')
&& (Object.prototype.hasOwnProperty.call(safe, 'code')
|| Object.prototype.hasOwnProperty.call(safe, 'statusCode')
|| Object.prototype.hasOwnProperty.call(safe, 'msg')
|| Object.prototype.hasOwnProperty.call(safe, 'errMsg'))
const payload = hasLegacyEnvelope ? (safe.data || {}) : safe
return { code: code, errMsg: errMsg, payload: payload }
}
async function requestJson(path, payload) {
const token = getToken()
if (!token) {
@@ -263,18 +290,19 @@ routerAdd('GET', '/manage/sdk-permission-manage', function (e) {
})
const data = await res.json()
if (!res.ok || !data || data.code >= 400) {
if (res.status === 401 || res.status === 403 || data.code === 401 || data.code === 403) {
const unwrapped = unwrapApiResponse(data, res)
if (!res.ok || !data || unwrapped.code >= 400) {
if (res.status === 401 || res.status === 403 || unwrapped.code === 401 || unwrapped.code === 403) {
localStorage.removeItem('pb_manage_token')
localStorage.removeItem('pb_manage_logged_in')
localStorage.removeItem('pb_manage_login_account')
localStorage.removeItem('pb_manage_login_time')
window.location.replace('/pb/manage/login')
}
throw new Error((data && data.msg) || '请求失败')
throw new Error(unwrapped.errMsg || '请求失败')
}
return data.data
return unwrapped.payload
}
function roleOptionsHtml(selectedRoleId) {

View File

@@ -40,13 +40,13 @@ components:
schemas:
ApiResponse:
type: object
required: [code, msg, data]
required: [statusCode, errMsg, data]
properties:
code:
statusCode:
type: integer
description: "业务状态码"
example: 200
msg:
errMsg:
type: string
description: "业务提示信息"
example: 操作成功
@@ -343,13 +343,13 @@ components:
type: object
description: |
项目统一认证响应。
所有对外接口统一返回 `code`、`msg`、`data`,认证成功时额外返回顶层 `token`。
所有对外接口统一返回 `statusCode`、`errMsg`、`data`,认证成功时额外返回顶层 `token`。
properties:
code:
statusCode:
type: integer
description: "业务状态码"
example: 200
msg:
errMsg:
type: string
description: "业务提示信息"
example: 登录成功
@@ -370,8 +370,8 @@ components:
type: string
description: "PocketBase 原生 auth token仅认证类接口在成功时额外返回"
example:
code: 200
msg: 登录成功
statusCode: 200
errMsg: 登录成功
data:
status: 登录或注册状态 | string
is_info_complete: 资料是否完整 | boolean
@@ -450,6 +450,14 @@ components:
type: string
description: "可选。未传 `users_phone_code` 时,可直接写入手机号"
example: '13800138000'
users_type:
type: string
description: "可选。用户类型;仅在传入非空值时更新"
example: 服务商
company_id:
type: string
description: "可选。公司业务 id仅在传入非空值时更新"
example: WX-COMPANY-10001
users_tag:
type: string
description: "可选。用户标签;非空时才更新"
@@ -1194,11 +1202,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/HelloWorldData'
/pb/api/system/health:
@@ -1211,11 +1214,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/HealthData'
/pb/api/system/users-count:
@@ -1229,11 +1227,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/UsersCountData'
/pb/api/platform/register:
@@ -1292,8 +1285,8 @@ paths:
schema:
$ref: '#/components/schemas/PocketBaseAuthResponse'
example:
code: 200
msg: 登录成功
statusCode: 200
errMsg: 登录成功
data:
status: login_success
is_info_complete: false
@@ -1348,11 +1341,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1382,11 +1370,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DictionaryRecord'
'400':
@@ -1415,11 +1398,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DictionaryRecord'
'400':
@@ -1451,11 +1429,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DictionaryRecord'
'400':
@@ -1489,11 +1462,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1531,11 +1499,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1568,11 +1531,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/AttachmentRecord'
'400':
@@ -1605,11 +1563,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/AttachmentRecord'
'400':
@@ -1637,11 +1590,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1681,11 +1629,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1718,11 +1661,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DocumentRecord'
'400':
@@ -1757,11 +1695,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DocumentRecord'
'400':
@@ -1796,11 +1729,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DocumentRecord'
'400':
@@ -1834,11 +1762,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1876,11 +1799,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1895,3 +1813,5 @@ paths:
'415':
description: "请求体必须为 application/json"

View File

@@ -241,7 +241,7 @@ paths:
- 其他原生操作中,`update/delete/view` 仅管理员或管理后台用户允许
注意:
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ code, msg, data }` 包装
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- `company_id` 由数据库自动生成,客户端创建时不需要传
- `company_id` 仍带唯一索引,可用于后续按业务 id 查询
requestBody:
@@ -320,7 +320,7 @@ paths:
- 按需传 `page`、`perPage`
注意:
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ code, msg, data }` 包装
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- PocketBase 原生标准接口里,“按 `company_id` 查询单条”和“查询整个列表”共用同一个 `GET /records` 路径,因此文档以同一个 GET operation 展示两种调用模式
parameters:
- name: filter
@@ -539,7 +539,7 @@ paths:
- `page=1`
注意:
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ code, msg, data }` 包装
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- `attachments_link` 返回的是 PocketBase 文件字段值,不是完整下载地址
- 若需文件流地址,可按 PocketBase 标准文件路径自行拼接:`/pb/api/files/{collectionId}/{recordId}/{attachments_link}`
parameters:
@@ -660,7 +660,7 @@ paths:
- 若要按最新上传倒序,传 `sort=-document_create`
注意:
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ code, msg, data }` 包装
- 这是 PocketBase 原生返回结构,不是 hooks 统一 `{ statusCode, errMsg, data }` 包装
- 如果需要更复杂的条件组合,可继续使用 PocketBase 原生 `filter` 语法自行扩展
parameters:
- name: filter
@@ -786,17 +786,17 @@ components:
ApiResponseBase:
type: object
required:
- code
- msg
- statusCode
- errMsg
- data
properties:
code:
statusCode:
type:
- integer
- string
description: "业务状态码"
example: 业务状态码 | integer
msg:
errMsg:
type: string
description: "业务提示信息"
example: 业务提示信息 | string
@@ -805,24 +805,24 @@ components:
type: object
additionalProperties: true
example:
code: 业务状态码 | integer
msg: 业务提示信息 | string
statusCode: 业务状态码 | integer
errMsg: 业务提示信息 | string
data:
任意业务字段: 业务响应数据 | object
ErrorResponse:
type: object
required:
- code
- msg
- statusCode
- errMsg
- data
properties:
code:
statusCode:
type:
- integer
- string
description: "业务状态码"
example: 业务状态码 | integer
msg:
errMsg:
type: string
description: "业务提示信息"
example: 失败原因提示 | string
@@ -831,8 +831,8 @@ components:
type: object
additionalProperties: true
example:
code: 业务状态码 | integer
msg: 失败原因提示 | string
statusCode: 业务状态码 | integer
errMsg: 失败原因提示 | string
data:
任意错误字段: 错误附加信息 | object
CompanyInfo:
@@ -1698,6 +1698,14 @@ components:
type: string
description: 可选。未传 `users_phone_code` 时,可直接写入手机号
example: '13800138000'
users_type:
type: string
description: 可选。用户类型;仅在传入非空值时更新
example: 服务商
company_id:
type: string
description: 可选。公司业务 id仅在传入非空值时更新
example: WX-COMPANY-10001
users_tag:
type: string
description: 可选。用户标签;非空时才更新
@@ -1756,8 +1764,8 @@ components:
type: string
description: PocketBase 原生 auth token
example:
code: 业务状态码 | integer
msg: 业务提示信息 | string
statusCode: 业务状态码 | integer
errMsg: 业务提示信息 | string
data:
status: 登录或注册状态 | string
is_info_complete: 资料是否完整 | boolean
@@ -1835,8 +1843,8 @@ components:
description: "业务响应数据"
$ref: '#/components/schemas/WechatProfileResponseData'
example:
code: 业务状态码 | integer
msg: 业务提示信息 | string
statusCode: 业务状态码 | integer
errMsg: 业务提示信息 | string
data:
status: 资料更新状态 | string
user:
@@ -1908,8 +1916,8 @@ components:
type: string
description: 新签发的 PocketBase 原生 auth token
example:
code: 业务状态码 | integer
msg: 刷新成功 | string
statusCode: 业务状态码 | integer
errMsg: 刷新成功 | string
data: {}
token: 新签发的PocketBase原生auth token | string
UsersCountData:
@@ -1929,7 +1937,8 @@ components:
description: "业务响应数据"
$ref: '#/components/schemas/UsersCountData'
example:
code: 业务状态码 | integer
msg: 业务提示信息 | string
statusCode: 业务状态码 | integer
errMsg: 业务提示信息 | string
data:
total_users: 用户总数 | integer

View File

@@ -33,13 +33,13 @@ components:
schemas:
ApiResponse:
type: object
required: [code, msg, data]
required: [statusCode, errMsg, data]
properties:
code:
statusCode:
type: integer
description: "业务状态码"
example: 200
msg:
errMsg:
type: string
description: "业务提示信息"
example: 操作成功
@@ -313,13 +313,13 @@ components:
type: object
description: |
项目统一认证响应。
所有对外接口统一返回 `code`、`msg`、`data`,认证成功时额外返回顶层 `token`。
所有对外接口统一返回 `statusCode`、`errMsg`、`data`,认证成功时额外返回顶层 `token`。
properties:
code:
statusCode:
type: integer
description: "业务状态码"
example: 200
msg:
errMsg:
type: string
description: "业务提示信息"
example: 登录成功
@@ -340,8 +340,8 @@ components:
type: string
description: PocketBase 原生 auth token仅认证类接口在成功时额外返回
example:
code: 200
msg: 登录成功
statusCode: 200
errMsg: 登录成功
data:
status: 登录或注册状态 | string
is_info_complete: 资料是否完整 | boolean
@@ -420,6 +420,14 @@ components:
type: string
description: 可选。未传 `users_phone_code` 时,可直接写入手机号
example: '13800138000'
users_type:
type: string
description: 可选。用户类型;仅在传入非空值时更新
example: 服务商
company_id:
type: string
description: 可选。公司业务 id仅在传入非空值时更新
example: WX-COMPANY-10001
users_tag:
type: string
description: 可选。用户标签;非空时才更新
@@ -1164,11 +1172,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/HelloWorldData'
/pb/api/system/health:
@@ -1181,11 +1184,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/HealthData'
/pb/api/system/users-count:
@@ -1199,11 +1197,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/UsersCountData'
/pb/api/system/refresh-token:
@@ -1235,8 +1228,8 @@ paths:
type: string
description: 新签发的 PocketBase 原生 auth token
example:
code: 200
msg: 刷新成功
statusCode: 200
errMsg: 刷新成功
data: {}
token: eyJhbGciOi...
'400':
@@ -1337,8 +1330,8 @@ paths:
schema:
$ref: '#/components/schemas/PocketBaseAuthResponse'
example:
code: 200
msg: 登录成功
statusCode: 200
errMsg: 登录成功
data:
status: login_success
is_info_complete: false
@@ -1395,11 +1388,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/WechatProfileResponseData'
'401':
@@ -1426,11 +1414,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1460,11 +1443,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DictionaryRecord'
'400':
@@ -1493,11 +1471,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DictionaryRecord'
'400':
@@ -1529,11 +1502,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DictionaryRecord'
'400':
@@ -1567,11 +1535,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1609,11 +1572,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1646,11 +1604,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/AttachmentRecord'
'400':
@@ -1683,11 +1636,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/AttachmentRecord'
'400':
@@ -1715,11 +1663,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1759,11 +1702,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1796,11 +1734,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DocumentRecord'
'400':
@@ -1835,11 +1768,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DocumentRecord'
'400':
@@ -1874,11 +1802,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
$ref: '#/components/schemas/DocumentRecord'
'400':
@@ -1912,11 +1835,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1954,11 +1872,6 @@ paths:
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
description: "业务响应数据"
type: object
properties:
@@ -1972,3 +1885,5 @@ paths:
description: 非 ManagePlatform 用户无权访问
'415':
description: 请求体必须为 application/json

View File

@@ -54,7 +54,7 @@
**索引规划 (Indexes):**
* `CREATE UNIQUE INDEX` 针对 `company_id` (确保业务主键唯一)
* `CREATE INDEX` 针对 `company_usci` (加速信用代码检索)
* `CREATE INDEX` 针对 `company_owner_openid` (加速按公司所有者 openid 查询)
* `CREATE UNIQUE INDEX` 针对 `company_owner_openid` (限制同一公司所有者仅能绑定一个公司)
---

View File

@@ -9,7 +9,8 @@
"init:documents": "node pocketbase.documents.js",
"init:dictionary": "node pocketbase.dictionary.js",
"migrate:file-fields": "node pocketbase.file-fields-to-attachments.js",
"test:company-native-api": "node test-tbl-company-native-api.js"
"test:company-native-api": "node test-tbl-company-native-api.js",
"test:company-owner-sync": "node test-company-owner-sync.js"
},
"keywords": [],
"author": "",

View File

@@ -61,7 +61,7 @@ const collections = [
indexes: [
'CREATE UNIQUE INDEX idx_company_id ON tbl_company (company_id)',
'CREATE INDEX idx_company_usci ON tbl_company (company_usci)',
'CREATE INDEX idx_company_owner_openid ON tbl_company (company_owner_openid)'
'CREATE UNIQUE INDEX idx_company_owner_openid ON tbl_company (company_owner_openid)'
]
},
{

View File

@@ -37,6 +37,11 @@ const collections = [
{
name: 'tbl_auth_users',
type: 'auth',
listRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB"',
viewRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB"',
createRule: '',
updateRule: '',
deleteRule: '@request.auth.usergroups_id = "ROLE-1774666070666-9dDrTB"',
fields: [
{ name: 'users_convers_id', type: 'text' },
{ name: 'openid', type: 'text', required: true },
@@ -196,6 +201,11 @@ async function createOrUpdateCollection(collectionData) {
const payload = {
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,
indexes: collectionData.indexes,
};

View File

@@ -0,0 +1,180 @@
import fs from 'node:fs/promises'
const runtimePath = new URL('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js', import.meta.url)
const runtimeText = await fs.readFile(runtimePath, 'utf8')
function readRuntimeValue(name) {
const match = runtimeText.match(new RegExp(`${name}:\\s*'([^']*)'`))
if (!match) {
throw new Error(`未在 runtime.js 中找到 ${name}`)
}
return match[1]
}
const baseUrl = readRuntimeValue('APP_BASE_URL')
const adminToken = readRuntimeValue('POCKETBASE_AUTH_TOKEN')
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
async function requestJson(path, options = {}) {
const res = await fetch(`${baseUrl}${path}`, options)
const text = await res.text()
let json = null
try {
json = text ? JSON.parse(text) : null
} catch {
throw new Error(`接口 ${path} 返回了非 JSON 响应:${text}`)
}
return { res, json, text }
}
async function findUserByOpenid(openid) {
const filter = encodeURIComponent(`openid="${openid}"`)
const result = await requestJson(`/pb/api/collections/tbl_auth_users/records?filter=${filter}&perPage=1&page=1&fields=id,openid,users_type,company_id`, {
headers: {
Authorization: `Bearer ${adminToken}`,
},
})
if (!result.res.ok) {
throw new Error(`查询用户失败:${result.text}`)
}
return result.json?.items?.[0] || null
}
async function patchUser(recordId, payload) {
const result = await requestJson(`/pb/api/collections/tbl_auth_users/records/${recordId}`, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${adminToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
})
if (!result.res.ok) {
throw new Error(`恢复用户失败:${result.text}`)
}
return result.json
}
async function deleteCompany(recordId) {
const result = await requestJson(`/pb/api/collections/tbl_company/records/${recordId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${adminToken}`,
},
})
if (!result.res.ok) {
throw new Error(`删除测试公司失败:${result.text}`)
}
}
async function loginPlatform(loginAccount, password) {
const result = await requestJson('/pb/api/platform/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
login_account: loginAccount,
password,
}),
})
if (!result.res.ok || !result.json?.token) {
throw new Error(`平台登录失败:${result.text}`)
}
return result.json.token
}
async function createCompany(token, ownerOpenid) {
const ts = Date.now()
const body = {
company_name: `同步测试公司-${ts}`,
company_type: '渠道商',
company_entity: '测试人',
company_usci: `91310000${String(ts).slice(-10)}`,
company_nationality: '中国',
company_nationality_code: 'CN',
company_province: '上海',
company_province_code: '310000',
company_city: '上海',
company_city_code: '310100',
company_district: '浦东新区',
company_district_code: '310115',
company_postalcode: '200000',
company_add: '同步测试地址',
company_status: '有效',
company_level: 'A',
company_owner_openid: ownerOpenid,
company_remark: 'codex-sync-test',
}
const result = await requestJson('/pb/api/collections/tbl_company/records', {
method: 'POST',
headers: {
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
})
if (!result.res.ok || !result.json?.id) {
throw new Error(`创建公司失败:${result.text}`)
}
return result.json
}
async function main() {
const openid = 'su13106859882'
const password = '13106859882'
const before = await findUserByOpenid(openid)
if (!before?.id) {
throw new Error(`未找到测试用户:${openid}`)
}
const token = await loginPlatform(password, password)
const createdCompany = await createCompany(token, openid)
await sleep(1500)
const after = await findUserByOpenid(openid)
const result = {
openid,
before_company_id: before.company_id || '',
before_users_type: before.users_type || '',
created_company_id: createdCompany.company_id || '',
created_company_record_id: createdCompany.id || '',
after_company_id: after?.company_id || '',
after_users_type: after?.users_type || '',
synced_company_id: after?.company_id === createdCompany.company_id,
synced_users_type: after?.users_type === '服务商',
}
try {
await patchUser(before.id, {
company_id: before.company_id || '',
users_type: before.users_type || '',
})
} finally {
await deleteCompany(createdCompany.id)
}
console.log(JSON.stringify(result, null, 2))
if (!result.synced_company_id || !result.synced_users_type) {
process.exitCode = 1
}
}
await main()