@rxdrag/website-lib-core 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rxdrag/website-lib-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./index.ts"
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"@types/react-dom": "^19.1.0",
|
|
24
24
|
"eslint": "^9.39.2",
|
|
25
25
|
"typescript": "^5",
|
|
26
|
-
"@rxdrag/
|
|
26
|
+
"@rxdrag/tiptap-preview": "0.1.0",
|
|
27
27
|
"@rxdrag/eslint-config-custom": "0.3.0",
|
|
28
|
-
"@rxdrag/
|
|
28
|
+
"@rxdrag/tsconfig": "0.3.0"
|
|
29
29
|
},
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@iconify/utils": "^3.0.2",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"gsap": "^3.12.7",
|
|
34
34
|
"hls.js": "^1.6.13",
|
|
35
35
|
"lodash-es": "^4.17.21",
|
|
36
|
-
"@rxdrag/entify-lib": "0.1.
|
|
36
|
+
"@rxdrag/entify-lib": "0.1.1",
|
|
37
37
|
"@rxdrag/rxcms-models": "0.4.1"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
package/src/entify/Entify.ts
CHANGED
|
@@ -15,6 +15,9 @@ import {
|
|
|
15
15
|
queryPosts,
|
|
16
16
|
queryProductCategories,
|
|
17
17
|
queryProducts,
|
|
18
|
+
queryProductsBatch,
|
|
19
|
+
queryPostsBatch,
|
|
20
|
+
PostsBatchOptions,
|
|
18
21
|
queryTagCategories,
|
|
19
22
|
queryTags,
|
|
20
23
|
queryUserPosts,
|
|
@@ -298,6 +301,45 @@ export class Entify implements IEntify {
|
|
|
298
301
|
)?.items;
|
|
299
302
|
}
|
|
300
303
|
|
|
304
|
+
/**
|
|
305
|
+
* 批量获取多组产品列表(不同分类/分页),底层折叠为最少的 HTTP 请求,
|
|
306
|
+
* 用于静态构建时显著减少网络往返。返回顺序与入参顺序严格一致。
|
|
307
|
+
*/
|
|
308
|
+
public async getProductsBatch(
|
|
309
|
+
conditionsList: ListConditions[],
|
|
310
|
+
imageSize?: ImageSize,
|
|
311
|
+
addonFields?: (keyof Product)[],
|
|
312
|
+
) {
|
|
313
|
+
const langAbbr = this.ensureLangAbbr();
|
|
314
|
+
const size = imageSize ?? this.imageSizes.productThumbnial;
|
|
315
|
+
const results = await queryProductsBatch(
|
|
316
|
+
conditionsList,
|
|
317
|
+
size,
|
|
318
|
+
this.envVariables,
|
|
319
|
+
addonFields,
|
|
320
|
+
langAbbr,
|
|
321
|
+
);
|
|
322
|
+
return results.map((r) => r?.items);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* 批量获取多组文章列表(不同分类/分页),底层折叠为最少的 HTTP 请求。
|
|
327
|
+
*/
|
|
328
|
+
public async getPostsBatch(
|
|
329
|
+
conditionsList: ListConditions[],
|
|
330
|
+
options?: PostsBatchOptions,
|
|
331
|
+
) {
|
|
332
|
+
const langAbbr = this.ensureLangAbbr();
|
|
333
|
+
const coverSize = options?.coverSize ?? this.imageSizes.postThumbnial;
|
|
334
|
+
const results = await queryPostsBatch(
|
|
335
|
+
conditionsList,
|
|
336
|
+
{ coverSize, addonFields: options?.addonFields },
|
|
337
|
+
this.envVariables,
|
|
338
|
+
langAbbr,
|
|
339
|
+
);
|
|
340
|
+
return results.map((r) => r?.items);
|
|
341
|
+
}
|
|
342
|
+
|
|
301
343
|
public async getProductListPaths(options: {
|
|
302
344
|
category?: string;
|
|
303
345
|
pageSize: number;
|
package/src/entify/IEntify.ts
CHANGED
|
@@ -120,6 +120,19 @@ export interface IEntify {
|
|
|
120
120
|
addonFields?: (keyof Product)[] | undefined
|
|
121
121
|
): Promise<TProduct[] | undefined>;
|
|
122
122
|
|
|
123
|
+
/** 批量获取多组产品列表(折叠为最少 HTTP 请求) */
|
|
124
|
+
getProductsBatch(
|
|
125
|
+
conditionsList: ListConditions[],
|
|
126
|
+
imageSize?: ImageSize,
|
|
127
|
+
addonFields?: (keyof Product)[]
|
|
128
|
+
): Promise<(TProduct[] | undefined)[]>;
|
|
129
|
+
|
|
130
|
+
/** 批量获取多组文章列表(折叠为最少 HTTP 请求) */
|
|
131
|
+
getPostsBatch(
|
|
132
|
+
conditionsList: ListConditions[],
|
|
133
|
+
options?: { coverSize?: ImageSize; addonFields?: (keyof import("@rxdrag/rxcms-models").Post)[] }
|
|
134
|
+
): Promise<(TPost[] | undefined)[]>;
|
|
135
|
+
|
|
123
136
|
getProductListPaths(options: {
|
|
124
137
|
category?: string;
|
|
125
138
|
pageSize: number;
|
package/src/entify/lib/index.ts
CHANGED
|
@@ -6,6 +6,9 @@ export * from "./newQueryPostOptions";
|
|
|
6
6
|
export * from "./newQueryProductOptions";
|
|
7
7
|
export * from "./newQueryProductsMediaOptions";
|
|
8
8
|
export * from "./queryEntityList";
|
|
9
|
+
export * from "./queryEntityListBatch";
|
|
10
|
+
export * from "./queryProductsBatch";
|
|
11
|
+
export * from "./queryPostsBatch";
|
|
9
12
|
export * from "./queryFeaturedProducts";
|
|
10
13
|
export * from "./queryLangs";
|
|
11
14
|
export * from "./queryLatestPosts";
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { IQueryOptions, ListResult } from "@rxdrag/entify-lib";
|
|
2
|
+
import { createEntifyClient } from "./createEntifyClient";
|
|
3
|
+
import { EnvVariables } from "../types";
|
|
4
|
+
import {
|
|
5
|
+
Post,
|
|
6
|
+
PostCategoryEntityName,
|
|
7
|
+
PostEntityName,
|
|
8
|
+
Product,
|
|
9
|
+
ProductCategoryEntityName,
|
|
10
|
+
ProductEntityName,
|
|
11
|
+
UserEntityName,
|
|
12
|
+
} from "@rxdrag/rxcms-models";
|
|
13
|
+
import {
|
|
14
|
+
postCategoriesToViewModel,
|
|
15
|
+
postListToViewModel,
|
|
16
|
+
productCategoriesToViewModel,
|
|
17
|
+
productListToViewModel,
|
|
18
|
+
userListToViewModel,
|
|
19
|
+
} from "../view-model";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* queryEntityList 的批量版本:把 N 个 list 查询通过 GraphQL 别名打包成一次 HTTP 请求。
|
|
23
|
+
*
|
|
24
|
+
* 与 queryEntityList 行为对齐:
|
|
25
|
+
* - 当 envVariables.websiteId 存在时,统一向每个子查询注入 website.id 过滤
|
|
26
|
+
* - 按子查询的 entityName 走对应 viewmodel 转换
|
|
27
|
+
* - 返回顺序与入参顺序严格一致
|
|
28
|
+
*/
|
|
29
|
+
export async function queryEntityListBatch<
|
|
30
|
+
T,
|
|
31
|
+
WhereExp = unknown,
|
|
32
|
+
OrderBy = unknown,
|
|
33
|
+
DistinctExp = unknown
|
|
34
|
+
>(
|
|
35
|
+
optionsList: IQueryOptions<T, WhereExp, OrderBy, DistinctExp>[],
|
|
36
|
+
envVariables: EnvVariables,
|
|
37
|
+
) {
|
|
38
|
+
if (!optionsList?.length) return [];
|
|
39
|
+
const client = createEntifyClient(envVariables);
|
|
40
|
+
|
|
41
|
+
const scoped = optionsList.map((options) =>
|
|
42
|
+
!envVariables.websiteId
|
|
43
|
+
? options
|
|
44
|
+
: options.setQueryArgs({
|
|
45
|
+
...options.getQueryArgs(),
|
|
46
|
+
where: {
|
|
47
|
+
website: { id: { _eq: envVariables.websiteId } },
|
|
48
|
+
...options.getQueryArgs()?.where,
|
|
49
|
+
} as WhereExp,
|
|
50
|
+
}),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const results = await client.batchEntityList<T, WhereExp, OrderBy, DistinctExp>(scoped);
|
|
54
|
+
|
|
55
|
+
return scoped.map((options, i) => {
|
|
56
|
+
const result = results[i];
|
|
57
|
+
switch (options.entityName) {
|
|
58
|
+
case ProductEntityName:
|
|
59
|
+
return productListToViewModel(result as ListResult<Product> | undefined);
|
|
60
|
+
case PostEntityName:
|
|
61
|
+
return postListToViewModel(result as ListResult<Post> | undefined);
|
|
62
|
+
case ProductCategoryEntityName:
|
|
63
|
+
return productCategoriesToViewModel(result as ListResult<Product> | undefined);
|
|
64
|
+
case PostCategoryEntityName:
|
|
65
|
+
return postCategoriesToViewModel(result as ListResult<Post> | undefined);
|
|
66
|
+
case UserEntityName:
|
|
67
|
+
return userListToViewModel(result as ListResult<Post> | undefined);
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Post,
|
|
3
|
+
PostBoolExp,
|
|
4
|
+
PostOrderBy,
|
|
5
|
+
PostDistinctExp,
|
|
6
|
+
PostFields,
|
|
7
|
+
PublishableStatus,
|
|
8
|
+
PostAssciations,
|
|
9
|
+
PostQueryOptions,
|
|
10
|
+
MediaQueryOptions,
|
|
11
|
+
UserQueryOptions,
|
|
12
|
+
UserFields,
|
|
13
|
+
ImageSize,
|
|
14
|
+
PostCategoryFields,
|
|
15
|
+
MediaFields,
|
|
16
|
+
PostCategory,
|
|
17
|
+
PostCategoryBoolExp,
|
|
18
|
+
PostCategoryOrderBy,
|
|
19
|
+
PostCategoryDistinctExp,
|
|
20
|
+
PostCategoryQueryOptions,
|
|
21
|
+
TagFields,
|
|
22
|
+
} from "@rxdrag/rxcms-models";
|
|
23
|
+
import { ListResult } from "@rxdrag/entify-lib";
|
|
24
|
+
import { ListConditions } from "./queryPosts";
|
|
25
|
+
import { queryEntityListBatch } from "./queryEntityListBatch";
|
|
26
|
+
import { createEntifyClient } from "./createEntifyClient";
|
|
27
|
+
import { EnvVariables } from "../types";
|
|
28
|
+
import { TPost } from "../view-model";
|
|
29
|
+
import { CategoryNode, collectCategoryIds } from "./collectCategoryIds";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 批量文章列表查询:把同一语言下多个分类/分页的 posts 查询折叠成最少的 HTTP 请求。
|
|
33
|
+
*
|
|
34
|
+
* 与 queryPosts 行为对齐:分类树展开(三级)、website 注入、viewmodel 转换、author/cover 字段。
|
|
35
|
+
*/
|
|
36
|
+
export interface PostsBatchOptions {
|
|
37
|
+
coverSize?: ImageSize;
|
|
38
|
+
addonFields?: (keyof Post)[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function queryPostsBatch(
|
|
42
|
+
conditionsList: ListConditions[],
|
|
43
|
+
options: PostsBatchOptions | undefined,
|
|
44
|
+
envVariables: EnvVariables,
|
|
45
|
+
langAbbr: string,
|
|
46
|
+
): Promise<(ListResult<TPost> | undefined)[]> {
|
|
47
|
+
if (!conditionsList?.length) return [];
|
|
48
|
+
|
|
49
|
+
const categoryIdsBySlug = await resolvePostCategoryIds(
|
|
50
|
+
conditionsList.map((c) => c.category).filter((s): s is string => !!s),
|
|
51
|
+
envVariables,
|
|
52
|
+
langAbbr,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
const postOptions = conditionsList.map((c) =>
|
|
56
|
+
buildPostQueryOptions(c, options, langAbbr, categoryIdsBySlug),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
const results = await queryEntityListBatch<
|
|
60
|
+
Post,
|
|
61
|
+
PostBoolExp,
|
|
62
|
+
PostOrderBy,
|
|
63
|
+
PostDistinctExp
|
|
64
|
+
>(postOptions, envVariables);
|
|
65
|
+
|
|
66
|
+
return results.map((r) => r as ListResult<TPost> | undefined);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function resolvePostCategoryIds(
|
|
70
|
+
slugs: string[],
|
|
71
|
+
envVariables: EnvVariables,
|
|
72
|
+
langAbbr: string,
|
|
73
|
+
): Promise<Map<string, string[]>> {
|
|
74
|
+
const map = new Map<string, string[]>();
|
|
75
|
+
const uniq = Array.from(new Set(slugs));
|
|
76
|
+
if (!uniq.length) return map;
|
|
77
|
+
|
|
78
|
+
const client = createEntifyClient(envVariables);
|
|
79
|
+
const categoryOptions = uniq.map((slug) =>
|
|
80
|
+
new PostCategoryQueryOptions(
|
|
81
|
+
[PostCategoryFields.id, PostCategoryFields.slug],
|
|
82
|
+
{
|
|
83
|
+
where: {
|
|
84
|
+
slug: { _eq: slug },
|
|
85
|
+
lang: { abbr: { _eq: langAbbr } },
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
).children(
|
|
89
|
+
new PostCategoryQueryOptions([
|
|
90
|
+
PostCategoryFields.id,
|
|
91
|
+
PostCategoryFields.slug,
|
|
92
|
+
]).children([PostCategoryFields.id]),
|
|
93
|
+
),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const results = await client.batchEntityList<
|
|
97
|
+
PostCategory,
|
|
98
|
+
PostCategoryBoolExp,
|
|
99
|
+
PostCategoryOrderBy,
|
|
100
|
+
PostCategoryDistinctExp
|
|
101
|
+
>(categoryOptions);
|
|
102
|
+
|
|
103
|
+
uniq.forEach((slug, i) => {
|
|
104
|
+
const item = results[i]?.items?.[0];
|
|
105
|
+
if (item) {
|
|
106
|
+
map.set(slug, collectCategoryIds(item as unknown as CategoryNode));
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return map;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildPostQueryOptions(
|
|
113
|
+
conditions: ListConditions,
|
|
114
|
+
options: PostsBatchOptions | undefined,
|
|
115
|
+
langAbbr: string,
|
|
116
|
+
categoryIdsBySlug: Map<string, string[]>,
|
|
117
|
+
): PostQueryOptions {
|
|
118
|
+
const { category: categorySlug, page = 1, pageSize } = conditions;
|
|
119
|
+
const addonFields = options?.addonFields;
|
|
120
|
+
const coverSize = options?.coverSize;
|
|
121
|
+
|
|
122
|
+
const where: Record<string, unknown> = {};
|
|
123
|
+
if (categorySlug) {
|
|
124
|
+
const ids = categoryIdsBySlug.get(categorySlug);
|
|
125
|
+
if (ids?.length) {
|
|
126
|
+
where[PostAssciations.category] = { id: { _in: ids } };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const defaultFields = [
|
|
131
|
+
PostFields.id,
|
|
132
|
+
PostFields.title,
|
|
133
|
+
PostFields.slug,
|
|
134
|
+
PostFields.description,
|
|
135
|
+
PostFields.status,
|
|
136
|
+
PostFields.updatedAt,
|
|
137
|
+
PostFields.createdAt,
|
|
138
|
+
PostFields.publishedAt,
|
|
139
|
+
];
|
|
140
|
+
const fields = addonFields || defaultFields;
|
|
141
|
+
|
|
142
|
+
const queryOptions = new PostQueryOptions(fields, {
|
|
143
|
+
offset: (page - 1) * pageSize,
|
|
144
|
+
limit: pageSize,
|
|
145
|
+
where: {
|
|
146
|
+
[PostFields.status]: { _eq: PublishableStatus.published },
|
|
147
|
+
lang: { abbr: { _eq: langAbbr } },
|
|
148
|
+
_or: [
|
|
149
|
+
{ [PostFields.isDeleted]: { _eq: false } },
|
|
150
|
+
{ [PostFields.isDeleted]: { _isNull: true } },
|
|
151
|
+
],
|
|
152
|
+
...where,
|
|
153
|
+
},
|
|
154
|
+
orderBy: [
|
|
155
|
+
{ [PostFields.pinToTop]: "desc" },
|
|
156
|
+
{ [PostFields.seqValue]: "asc" },
|
|
157
|
+
{ [PostFields.updatedAt]: "desc" },
|
|
158
|
+
],
|
|
159
|
+
})
|
|
160
|
+
.category([PostCategoryFields.id, PostCategoryFields.name, PostCategoryFields.slug])
|
|
161
|
+
.tags([TagFields.id, TagFields.name, TagFields.color]);
|
|
162
|
+
|
|
163
|
+
if (!addonFields || addonFields.includes("cover" as keyof Post)) {
|
|
164
|
+
queryOptions.cover(
|
|
165
|
+
new MediaQueryOptions().file([
|
|
166
|
+
"thumbnail",
|
|
167
|
+
coverSize
|
|
168
|
+
? `resize(width:${coverSize.width}, height:${coverSize.height})`
|
|
169
|
+
: "resize(width:480, height:180)",
|
|
170
|
+
]),
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!addonFields || addonFields.includes("author" as keyof Post)) {
|
|
175
|
+
queryOptions.author(
|
|
176
|
+
new UserQueryOptions([UserFields.id, UserFields.name]).avatar(
|
|
177
|
+
new MediaQueryOptions([
|
|
178
|
+
MediaFields.id,
|
|
179
|
+
MediaFields.mediaType,
|
|
180
|
+
MediaFields.mediaRef,
|
|
181
|
+
]).file(["thumbnail", "original"]),
|
|
182
|
+
),
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return queryOptions;
|
|
187
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Product,
|
|
3
|
+
ProductBoolExp,
|
|
4
|
+
ProductOrderBy,
|
|
5
|
+
ProductDistinctExp,
|
|
6
|
+
ProductFields,
|
|
7
|
+
ProductQueryOptions,
|
|
8
|
+
PublishableStatus,
|
|
9
|
+
ProductAssciations,
|
|
10
|
+
ProductCategory,
|
|
11
|
+
ProductCategoryBoolExp,
|
|
12
|
+
ProductCategoryOrderBy,
|
|
13
|
+
ProductCategoryDistinctExp,
|
|
14
|
+
ProductCategoryFields,
|
|
15
|
+
ProductCategoryQueryOptions,
|
|
16
|
+
TagFields,
|
|
17
|
+
ImageSize,
|
|
18
|
+
} from "@rxdrag/rxcms-models";
|
|
19
|
+
import { ListResult } from "@rxdrag/entify-lib";
|
|
20
|
+
import { ListConditions } from "./queryPosts";
|
|
21
|
+
import { queryEntityListBatch } from "./queryEntityListBatch";
|
|
22
|
+
import { newQueryProductsMediaOptions } from "./newQueryProductsMediaOptions";
|
|
23
|
+
import { createEntifyClient } from "./createEntifyClient";
|
|
24
|
+
import { EnvVariables } from "../types";
|
|
25
|
+
import { TProduct } from "../view-model";
|
|
26
|
+
import { CategoryNode, collectCategoryIds } from "./collectCategoryIds";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 批量产品列表查询:把同一语言下多个分类/分页的 products 查询折叠成最少的 HTTP 请求。
|
|
30
|
+
*
|
|
31
|
+
* 流程:
|
|
32
|
+
* 1. 收集所有非空 category slug,一次批量解析分类树(最多 1 次 HTTP)。
|
|
33
|
+
* 2. 复用统一的产品查询字段配置,构造 N 个 ProductQueryOptions。
|
|
34
|
+
* 3. 一次批量发出(1 次 HTTP),按入参顺序返回结果。
|
|
35
|
+
*
|
|
36
|
+
* 与 queryProducts 行为对齐:分类树展开(三级)、website 注入、viewmodel 转换。
|
|
37
|
+
*/
|
|
38
|
+
export async function queryProductsBatch(
|
|
39
|
+
conditionsList: ListConditions[],
|
|
40
|
+
imageSize: ImageSize | undefined,
|
|
41
|
+
envVariables: EnvVariables,
|
|
42
|
+
addonFields: (keyof Product)[] | undefined,
|
|
43
|
+
langAbbr: string,
|
|
44
|
+
): Promise<(ListResult<TProduct> | undefined)[]> {
|
|
45
|
+
if (!conditionsList?.length) return [];
|
|
46
|
+
|
|
47
|
+
const categoryIdsBySlug = await resolveProductCategoryIds(
|
|
48
|
+
conditionsList.map((c) => c.category).filter((s): s is string => !!s),
|
|
49
|
+
envVariables,
|
|
50
|
+
langAbbr,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const productOptions = conditionsList.map((c) =>
|
|
54
|
+
buildProductQueryOptions(c, imageSize, addonFields, langAbbr, categoryIdsBySlug),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const results = await queryEntityListBatch<
|
|
58
|
+
Product,
|
|
59
|
+
ProductBoolExp,
|
|
60
|
+
ProductOrderBy,
|
|
61
|
+
ProductDistinctExp
|
|
62
|
+
>(productOptions, envVariables);
|
|
63
|
+
|
|
64
|
+
return results.map((r) => r as ListResult<TProduct> | undefined);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function resolveProductCategoryIds(
|
|
68
|
+
slugs: string[],
|
|
69
|
+
envVariables: EnvVariables,
|
|
70
|
+
langAbbr: string,
|
|
71
|
+
): Promise<Map<string, string[]>> {
|
|
72
|
+
const map = new Map<string, string[]>();
|
|
73
|
+
const uniq = Array.from(new Set(slugs));
|
|
74
|
+
if (!uniq.length) return map;
|
|
75
|
+
|
|
76
|
+
const client = createEntifyClient(envVariables);
|
|
77
|
+
const categoryOptions = uniq.map((slug) =>
|
|
78
|
+
new ProductCategoryQueryOptions(
|
|
79
|
+
[ProductCategoryFields.id, ProductCategoryFields.slug],
|
|
80
|
+
{
|
|
81
|
+
where: {
|
|
82
|
+
slug: { _eq: slug },
|
|
83
|
+
lang: { abbr: { _eq: langAbbr } },
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
).children(
|
|
87
|
+
new ProductCategoryQueryOptions([
|
|
88
|
+
ProductCategoryFields.id,
|
|
89
|
+
ProductCategoryFields.slug,
|
|
90
|
+
]).children([ProductCategoryFields.id]),
|
|
91
|
+
),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
const results = await client.batchEntityList<
|
|
95
|
+
ProductCategory,
|
|
96
|
+
ProductCategoryBoolExp,
|
|
97
|
+
ProductCategoryOrderBy,
|
|
98
|
+
ProductCategoryDistinctExp
|
|
99
|
+
>(categoryOptions);
|
|
100
|
+
|
|
101
|
+
uniq.forEach((slug, i) => {
|
|
102
|
+
const item = results[i]?.items?.[0];
|
|
103
|
+
if (item) {
|
|
104
|
+
map.set(slug, collectCategoryIds(item as unknown as CategoryNode));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
return map;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function buildProductQueryOptions(
|
|
111
|
+
conditions: ListConditions,
|
|
112
|
+
imageSize: ImageSize | undefined,
|
|
113
|
+
addonFields: (keyof Product)[] | undefined,
|
|
114
|
+
langAbbr: string,
|
|
115
|
+
categoryIdsBySlug: Map<string, string[]>,
|
|
116
|
+
): ProductQueryOptions {
|
|
117
|
+
const { category: categorySlug, page = 1, pageSize = 10, orderBy } = conditions;
|
|
118
|
+
|
|
119
|
+
const where: Record<string, unknown> = {};
|
|
120
|
+
if (categorySlug) {
|
|
121
|
+
const ids = categoryIdsBySlug.get(categorySlug);
|
|
122
|
+
if (ids?.length) {
|
|
123
|
+
where[ProductAssciations.category] = { id: { _in: ids } };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const defaultFields = [
|
|
128
|
+
ProductFields.id,
|
|
129
|
+
ProductFields.slug,
|
|
130
|
+
ProductFields.title,
|
|
131
|
+
ProductFields.shortTitle,
|
|
132
|
+
ProductFields.description,
|
|
133
|
+
ProductFields.seqValue,
|
|
134
|
+
ProductFields.updatedAt,
|
|
135
|
+
];
|
|
136
|
+
const fields = [...defaultFields, ...(addonFields || [])];
|
|
137
|
+
|
|
138
|
+
const queryOptions = new ProductQueryOptions(fields, {
|
|
139
|
+
offset: (page - 1) * pageSize,
|
|
140
|
+
limit: pageSize,
|
|
141
|
+
where: {
|
|
142
|
+
[ProductFields.status]: { _eq: PublishableStatus.published },
|
|
143
|
+
lang: { abbr: { _eq: langAbbr } },
|
|
144
|
+
_or: [
|
|
145
|
+
{ [ProductFields.isDeleted]: { _eq: false } },
|
|
146
|
+
{ [ProductFields.isDeleted]: { _isNull: true } },
|
|
147
|
+
],
|
|
148
|
+
...where,
|
|
149
|
+
},
|
|
150
|
+
orderBy: orderBy || [
|
|
151
|
+
{ [ProductFields.seqValue]: "asc" },
|
|
152
|
+
{ [ProductFields.updatedAt]: "desc" },
|
|
153
|
+
],
|
|
154
|
+
}).tags([TagFields.id, TagFields.name, TagFields.color]);
|
|
155
|
+
|
|
156
|
+
if (!addonFields || !addonFields.includes("slug" as keyof Product)) {
|
|
157
|
+
queryOptions.mediaPivots(newQueryProductsMediaOptions(imageSize));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return queryOptions;
|
|
161
|
+
}
|