feat: 更新 PocketBase API 路由,增强认证安全性

- 修改登录路由为 `/pb/api/wechat/login`,新增局部 try/catch 以保留业务状态码并返回统一结构 `{ code, msg, data }`。
- 更新所有相关 API 路由前缀为 `/pb`,包括系统、平台、字典、附件和文档接口。
- 新增文档接口支持多个附件 ID,更新 OpenAPI 文档以反映这些变化。
- 在数据库模式中添加对字典名称的唯一索引,确保全局唯一性。
- 更新文档字段,设置 `document_title` 和 `document_type` 为必填项,并在验证过程中检查字段的必填属性。
This commit is contained in:
2026-03-27 13:55:06 +08:00
parent 9feb0bb3a0
commit c43aae783f
9 changed files with 749 additions and 206 deletions

View File

@@ -20,6 +20,7 @@
**索引规划 (Indexes):**
* `CREATE UNIQUE INDEX` 针对 `system_dict_id` (确保业务主键唯一)
* `CREATE UNIQUE INDEX` 针对 `dict_name` (确保词典名称全局唯一,并支持按名称唯一索引查询)
* `CREATE INDEX` 针对 `dict_word_parent_id` (加速父子级联查询)
---

View File

@@ -49,8 +49,8 @@ const collections = [
{ name: 'document_id', type: 'text', required: true },
{ name: 'document_effect_date', type: 'date' },
{ name: 'document_expiry_date', type: 'date' },
{ name: 'document_type', type: 'text' },
{ name: 'document_title', type: 'text' },
{ name: 'document_type', type: 'text', required: true },
{ name: 'document_title', type: 'text', required: true },
{ name: 'document_subtitle', type: 'text' },
{ name: 'document_summary', type: 'text' },
{ name: 'document_content', type: 'text' },
@@ -167,6 +167,7 @@ function normalizeFieldList(fields) {
return (fields || []).map((field) => ({
name: field.name,
type: field.type,
required: !!field.required,
}));
}
@@ -210,8 +211,10 @@ async function verifyCollections(targetCollections) {
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)) {
@@ -222,6 +225,10 @@ async function verifyCollections(targetCollections) {
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 || []);
@@ -231,7 +238,7 @@ async function verifyCollections(targetCollections) {
throw new Error(`${target.name} 类型不匹配,期望 ${target.type},实际 ${remote.type}`);
}
if (!missingFields.length && !mismatchedTypes.length && !missingIndexes.length) {
if (!missingFields.length && !mismatchedTypes.length && !mismatchedRequired.length && !missingIndexes.length) {
console.log(`${target.name} 校验通过。`);
continue;
}
@@ -243,6 +250,9 @@ async function verifyCollections(targetCollections) {
if (mismatchedTypes.length) {
console.log(` - 字段类型不匹配: ${mismatchedTypes.join(', ')}`);
}
if (mismatchedRequired.length) {
console.log(` - 字段必填属性不匹配: ${mismatchedRequired.join(', ')}`);
}
if (missingIndexes.length) {
console.log(` - 缺失索引: ${missingIndexes.join(' | ')}`);
}