feat: 添加购物车相关迁移和索引功能
- 在 package.json 中添加迁移脚本 `migrate:cart-active-unique-index`。 - 修改 `pocketbase.cart-order.js` 文件,更新 `cart_id` 和 `cart_product_id` 字段的必填属性,并添加唯一索引 `idx_tbl_cart_owner_product_active_unique`。 - 在 `pocketbase.ensure-cart-order-autogen-id.js` 中,调整 `cart_id` 字段的必填属性为可选,并确保 `order_id` 字段为必填。 - 在 `pocketbase.product-list.js` 中,新增 `prod_list_barcode` 字段。 - 新增 `make-openapi-standalone.cjs` 脚本,用于处理 OpenAPI 文档。 - 新增 `pocketbase.cart-active-unique-index.js` 脚本,处理购物车的唯一索引和去重逻辑。
This commit is contained in:
145
script/pocketbase.cart-active-unique-index.js
Normal file
145
script/pocketbase.cart-active-unique-index.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import { createRequire } from 'module';
|
||||
import PocketBase from 'pocketbase';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
let runtimeConfig = {};
|
||||
try {
|
||||
runtimeConfig = require('../pocket-base/bai_api_pb_hooks/bai_api_shared/config/runtime.js');
|
||||
} catch (_error) {
|
||||
runtimeConfig = {};
|
||||
}
|
||||
|
||||
const PB_URL = String(process.env.PB_URL || 'https://bai-api.blv-oa.com/pb').replace(/\/+$/, '');
|
||||
const AUTH_TOKEN = process.env.POCKETBASE_AUTH_TOKEN || runtimeConfig.POCKETBASE_AUTH_TOKEN || '';
|
||||
const COLLECTION = 'tbl_cart';
|
||||
const ACTIVE_UNIQUE_INDEX = 'CREATE UNIQUE INDEX idx_tbl_cart_owner_product_active_unique ON tbl_cart (cart_owner, cart_product_id) WHERE is_delete = 0';
|
||||
const TEMP_RULE = '@request.auth.id != ""';
|
||||
|
||||
if (!AUTH_TOKEN) {
|
||||
console.error('Missing POCKETBASE_AUTH_TOKEN');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const pb = new PocketBase(PB_URL);
|
||||
|
||||
function buildCollectionPayload(base, overrides = {}) {
|
||||
const rawFields = Array.isArray(base.fields) ? base.fields : [];
|
||||
const safeFields = rawFields.filter((field) => field && field.name !== 'id');
|
||||
|
||||
return {
|
||||
name: base.name,
|
||||
type: base.type,
|
||||
listRule: Object.prototype.hasOwnProperty.call(overrides, 'listRule') ? overrides.listRule : base.listRule,
|
||||
viewRule: Object.prototype.hasOwnProperty.call(overrides, 'viewRule') ? overrides.viewRule : base.viewRule,
|
||||
createRule: Object.prototype.hasOwnProperty.call(overrides, 'createRule') ? overrides.createRule : base.createRule,
|
||||
updateRule: Object.prototype.hasOwnProperty.call(overrides, 'updateRule') ? overrides.updateRule : base.updateRule,
|
||||
deleteRule: Object.prototype.hasOwnProperty.call(overrides, 'deleteRule') ? overrides.deleteRule : base.deleteRule,
|
||||
fields: safeFields,
|
||||
indexes: Object.prototype.hasOwnProperty.call(overrides, 'indexes') ? overrides.indexes : (base.indexes || []),
|
||||
};
|
||||
}
|
||||
|
||||
async function setTempRules(collection) {
|
||||
const payload = buildCollectionPayload(collection, {
|
||||
listRule: TEMP_RULE,
|
||||
viewRule: TEMP_RULE,
|
||||
createRule: TEMP_RULE,
|
||||
updateRule: TEMP_RULE,
|
||||
deleteRule: TEMP_RULE,
|
||||
});
|
||||
await pb.collections.update(collection.id, payload);
|
||||
}
|
||||
|
||||
async function restoreRules(collection) {
|
||||
await pb.collections.update(collection.id, buildCollectionPayload(collection));
|
||||
}
|
||||
|
||||
function groupKey(record) {
|
||||
const owner = String(record.cart_owner || '').trim();
|
||||
const product = String(record.cart_product_id || '').trim();
|
||||
return `${owner}||${product}`;
|
||||
}
|
||||
|
||||
async function dedupeActiveRows() {
|
||||
const rows = await pb.collection(COLLECTION).getFullList({
|
||||
filter: 'is_delete = 0',
|
||||
sort: '-updated',
|
||||
fields: 'id,cart_owner,cart_product_id,is_delete,updated',
|
||||
});
|
||||
|
||||
const groups = new Map();
|
||||
for (const row of rows) {
|
||||
const key = groupKey(row);
|
||||
if (!groups.has(key)) groups.set(key, []);
|
||||
groups.get(key).push(row);
|
||||
}
|
||||
|
||||
let duplicateGroups = 0;
|
||||
let softDeleted = 0;
|
||||
|
||||
for (const [, items] of groups) {
|
||||
if (items.length <= 1) continue;
|
||||
duplicateGroups += 1;
|
||||
|
||||
const keep = items[0];
|
||||
for (let i = 1; i < items.length; i += 1) {
|
||||
const row = items[i];
|
||||
await pb.collection(COLLECTION).update(row.id, { is_delete: 1 });
|
||||
softDeleted += 1;
|
||||
}
|
||||
|
||||
console.log(`dedupe group keep=${keep.id} deleted=${items.length - 1}`);
|
||||
}
|
||||
|
||||
return { total: rows.length, duplicateGroups, softDeleted };
|
||||
}
|
||||
|
||||
async function applyActiveUniqueIndex() {
|
||||
const collection = await pb.collections.getOne(COLLECTION);
|
||||
const indexes = Array.isArray(collection.indexes) ? collection.indexes.slice() : [];
|
||||
if (!indexes.includes(ACTIVE_UNIQUE_INDEX)) {
|
||||
indexes.push(ACTIVE_UNIQUE_INDEX);
|
||||
}
|
||||
|
||||
const payload = buildCollectionPayload(collection, { indexes });
|
||||
await pb.collections.update(collection.id, payload);
|
||||
|
||||
const latest = await pb.collections.getOne(COLLECTION);
|
||||
const ok = Array.isArray(latest.indexes) && latest.indexes.includes(ACTIVE_UNIQUE_INDEX);
|
||||
if (!ok) {
|
||||
throw new Error('Active unique index was not applied.');
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
pb.authStore.save(AUTH_TOKEN, null);
|
||||
console.log(`connect ${PB_URL}`);
|
||||
|
||||
const original = await pb.collections.getOne(COLLECTION);
|
||||
|
||||
try {
|
||||
await setTempRules(original);
|
||||
const dedupe = await dedupeActiveRows();
|
||||
console.log(JSON.stringify(dedupe, null, 2));
|
||||
} finally {
|
||||
const latest = await pb.collections.getOne(COLLECTION);
|
||||
const restoreBase = {
|
||||
...latest,
|
||||
listRule: original.listRule,
|
||||
viewRule: original.viewRule,
|
||||
createRule: original.createRule,
|
||||
updateRule: original.updateRule,
|
||||
deleteRule: original.deleteRule,
|
||||
};
|
||||
await restoreRules(restoreBase);
|
||||
}
|
||||
|
||||
await applyActiveUniqueIndex();
|
||||
console.log('active unique index applied');
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('migration failed', error?.response || error?.message || error);
|
||||
process.exitCode = 1;
|
||||
});
|
||||
Reference in New Issue
Block a user