Commit 6d0eb5bb authored by cwuyiqing's avatar cwuyiqing
Browse files

fix: fix circle connect cause service timeout

parent 3c7dace2
Showing with 106 additions and 44 deletions
+106 -44
......@@ -116,8 +116,8 @@ const Login: React.FC<{}> = () => {
if (data?.from !== 'lowcode') return
window?.parent.postMessage(
JSON.stringify({
ack: 1,
from: 'cms',
status: 'success',
}),
'*'
)
......@@ -130,7 +130,6 @@ const Login: React.FC<{}> = () => {
// 响应低码平台
window?.parent.postMessage(
JSON.stringify({
ack: 2,
from: 'cms',
status: 'success',
}),
......@@ -141,7 +140,6 @@ const Login: React.FC<{}> = () => {
// 响应低码平台
window?.parent.postMessage(
JSON.stringify({
ack: 2,
from: 'cms',
status: 'fail',
message: error.message,
......
......@@ -101,7 +101,7 @@ export class ApiController {
@UseGuards(ActionGuard('read'))
@Get(':collectionName')
async getDocuments(@Param('collectionName') collectionName: string, @Query() query: IQuery) {
const apiQuery = await this.apiService.getMergedQuery(collectionName, query)
const apiQuery = await this.apiService.getMergedQuery(query)
// 查询数据
let findRes = await this.apiService.callOpenApi({
......@@ -132,7 +132,7 @@ export class ApiController {
@Query() query,
@Body() payload
) {
const apiQuery = await this.apiService.getMergedQuery(collectionName, query)
const apiQuery = await this.apiService.getMergedQuery(query)
const { total } = await this.apiService.callOpenApi({
collectionName,
......
......@@ -28,13 +28,13 @@ export class ApiService {
/**
* 合并传入的 query 与 Schema 内置的条件,获取最终查询 query
*/
async getMergedQuery(collectionName: string, query: IQuery) {
async getMergedQuery(query: IQuery) {
// 调用 api 传入的 query
let { limit, skip, fields, sort } = query
sort = sort ? JSON.parse(sort) : {}
fields = fields ? JSON.parse(fields) : {}
// 获取 doc 模型信息
// 从缓存中获取 doc 模型信息
const docSchema = this.cacheService.get('currentSchema')
if (!docSchema) {
......@@ -85,13 +85,26 @@ export class ApiService {
let formatData = resData
// 获取数据模型
const docSchema = this.cacheService.get('currentSchema')
// 获取数据模型,优先使用 currentSchema 缓存
// 如果 currentSchema 与 collectionName 不相等,再查找 schema
const currentSchema = this.cacheService.get('currentSchema')
let docSchema = currentSchema
if (docSchema.collectionName !== collectionName) {
// 获取所有 Schema 数据
const schemas = await this.getAndCacheSchemas()
docSchema = schemas.find((_) => _.collectionName === collectionName)
}
// 如果文档模型定义中存在关联字段
// 则把返回结果中的所有关联字段 id 转换为对应的数据
const connectFields = docSchema.fields.filter((field) => field.type === 'Connect')
if (connectFields?.length) {
// 存储 connect schema,以确认是否发生循环关联
const connectTraverseCollections = this.cacheService.get('connectTraverseCollections') || []
this.cacheService.set(
'connectTraverseCollections',
connectTraverseCollections.concat([docSchema.collectionName])
)
formatData = await this.transformConnectField(resData, connectFields)
}
......@@ -125,15 +138,7 @@ export class ApiService {
const $ = this.cloudbaseService.db.command
// 获取所有 Schema 数据
let schemas = []
// 缓存的 Schema 数据
const cachedSchemas = this.cacheService.get('schemas')
if (cachedSchemas?.length) {
schemas = cachedSchemas
} else {
schemas = await getCollectionSchema()
this.cacheService.set('schemas', schemas)
}
const schemas = await this.getAndCacheSchemas()
// 转换 data 中的关联 field
const transformDataByField = async (field: SchemaField) => {
......@@ -157,14 +162,28 @@ export class ApiService {
const collectionName = schemas.find((schema) => schema._id === field.connectResource)
.collectionName
// 获取关联的数据,分页最大条数 50
let { data: connectData } = await this.cloudbaseService
// 获取关联 id 对应的 Doc
// 使用 getMany 获取数据,自动转换 Connect 字段
let connectData = []
// 是否发生循环关联
const existCircle = this.cacheService
.get('connectTraverseCollections')
.includes(collectionName)
// 拉取数据
const { data } = await this.cloudbaseService
.collection(collectionName)
.where({ _id: $.in(ids) })
.limit(1000)
.get()
connectData = await this.parseResData(connectData, collectionName)
// 存在环且会在当前的节点发生循环,直接使用底层数据
if (existCircle) {
connectData = data
} else {
// 不发生循环,获取关联转换的数据
connectData = await this.parseResData(data, collectionName)
}
// 修改 resData 中的关联字段
resData = resData.map((record) => {
......@@ -172,7 +191,7 @@ export class ApiService {
let connectRecord
// 关联的数据被删除
if (!connectData) {
if (!connectData?.length) {
return {
...record,
[fieldName]: null,
......@@ -268,6 +287,22 @@ export class ApiService {
return queryRes
}
// 获取并使用 localCache 缓存 Schemas
private async getAndCacheSchemas() {
// 获取所有 Schema 数据
let schemas: Schema[] = []
// 缓存的 Schema 数据
const cachedSchemas = this.cacheService.get('schemas')
if (cachedSchemas?.length) {
schemas = cachedSchemas
} else {
schemas = await getCollectionSchema()
this.cacheService.set('schemas', schemas)
}
return schemas
}
/**
* 计算 Open API 请求签名
*/
......
......@@ -6,6 +6,8 @@ interface CacheMap {
project: Project
schemas: Schema[]
connectTraverseCollections: string[]
}
// 针对单个请求的缓存
......
......@@ -22,16 +22,20 @@ export class ContentsService {
async getMany(
resource: string,
options: {
// 过滤
filter?: {
_id?: string
ids?: string[]
[key: string]: any
}
// 模糊查询
fuzzyFilter?: {
[key: string]: any
}
// 分页
pageSize?: number
page?: number
// 排序
sort?: {
[key: string]: 'ascend' | 'ascend'
}
......@@ -48,7 +52,10 @@ export class ContentsService {
where._id = db.command.in(filter.ids)
}
const schema = await getCollectionSchema(resource)
// 获取所有 Schema 数据
const schemas = await getCollectionSchema()
// 当前 Schema 配置
const schema = schemas.find((_) => _.collectionName === resource)
// 模糊搜索
if (fuzzyFilter && schema) {
......@@ -111,6 +118,13 @@ export class ContentsService {
// 存在关联类型字段
const connectFields = schema.fields.filter((field) => field.type === 'Connect')
if (!R.isEmpty(connectFields)) {
// 存储 connect schema,以确认是否发生循环关联
const connectTraverseCollections = this.cacheService.get('connectTraverseCollections') || []
this.cacheService.set(
'connectTraverseCollections',
connectTraverseCollections.concat([schema.collectionName])
)
// 获取 connect 数据
res.data = await this.transformConnectField(res.data, connectFields)
}
}
......@@ -351,17 +365,10 @@ export class ContentsService {
*/
private async transformConnectField(docs: any[], connectFields: SchemaField[]) {
let resData: any[] = docs
const $ = this.cloudbaseService.db.command
// 获取所有 Schema 数据
let schemas = []
// 缓存的 Schema 数据
const cachedSchemas = this.cacheService.get('schemas')
if (cachedSchemas?.length) {
schemas = cachedSchemas
} else {
schemas = await getCollectionSchema()
this.cacheService.set('schemas', schemas)
}
const schemas = await getCollectionSchema()
// 转换 data 中的关联 field
const transformDataByField = async (field: SchemaField) => {
......@@ -385,17 +392,36 @@ export class ContentsService {
}
// 关联的 Schema
const connectSchema = schemas.find((schema) => schema._id === field.connectResource)
const collectionName = schemas.find((schema) => schema._id === field.connectResource)
?.collectionName
// 获取关联 id 对应的 Doc
// 使用 getMany 获取数据,自动转换 Connect 字段
const { data: connectData } = await this.getMany(connectSchema.collectionName, {
page: 1,
pageSize: 1000,
filter: {
ids,
},
})
let connectData = []
// 是否发生循环关联
const existCircle = this.cacheService
.get('connectTraverseCollections')
.includes(collectionName)
// 存在环且会在当前的节点发生循环,直接获取底层数据
if (existCircle) {
const { data } = await this.cloudbaseService
.collection(collectionName)
.where({ _id: $.in(ids) })
.limit(1000)
.get()
connectData = data
} else {
// 不发生循环,获取关联转换的数据
const { data } = await this.getMany(collectionName, {
page: 1,
pageSize: 1000,
filter: {
ids,
},
})
connectData = data
}
// 修改 resData 中的关联字段
resData = resData.map((record) => {
......
import { Injectable, Scope } from '@nestjs/common'
interface CacheMap {
// currentSchema: Schema
// project: Project
// 模型数据
schemas: Schema[]
// 关联遍历的集合名
connectTraverseCollections: string[]
}
// 针对单个请求的缓存
......
......@@ -120,6 +120,7 @@ export async function getCollectionSchema(collection: string): Promise<Schema>
export async function getCollectionSchema(): Promise<Schema[]>
export async function getCollectionSchema(collection?: string) {
// 全部 schemas 使用 SCHEMAS 作为 key 缓存
const cacheSchema = collection ? schemaCache.get(collection) : schemaCache.get('SCHEMAS')
if (cacheSchema) return cacheSchema
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment