v3.5.4版本提交,修改api
CI / verify (pull_request) Waiting to run

This commit is contained in:
2026-08-21 16:22:53 +08:00
parent a01217539c
commit 62e61a9e63
21 changed files with 594 additions and 142 deletions
@@ -5,6 +5,30 @@
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const PREFIX = 'DK';
const PAD = 6;
const RE = /^DK(\d+)$/;
async function nextDkCode() {
const [items, skus] = await Promise.all([
prisma.commonProductItem.findMany({
where: { skuCode: { startsWith: PREFIX } },
select: { skuCode: true },
}),
prisma.commonProductSku.findMany({
where: { skuCode: { startsWith: PREFIX } },
select: { skuCode: true },
}),
]);
let maxSeq = 0;
for (const row of [...items, ...skus]) {
const m = RE.exec(row.skuCode);
if (!m) continue;
const n = Number(m[1]);
if (Number.isFinite(n) && n > maxSeq) maxSeq = n;
}
return `${PREFIX}${String(maxSeq + 1).padStart(PAD, '0')}`;
}
async function main() {
const products = await prisma.commonProductItem.findMany({
@@ -13,10 +37,11 @@ async function main() {
let created = 0;
for (const p of products) {
if (p.skus.length > 0) continue;
const skuCode = RE.test(p.skuCode) ? p.skuCode : await nextDkCode();
await prisma.commonProductSku.create({
data: {
productId: p.id,
skuCode: p.skuCode,
skuCode,
barcode69: p.barcode69,
specKey: '',
specText: p.spec,
@@ -32,8 +57,14 @@ async function main() {
sortOrder: 0,
},
});
if (p.skuCode !== skuCode) {
await prisma.commonProductItem.update({
where: { id: p.id },
data: { skuCode },
});
}
created += 1;
console.log(`created default sku for product ${p.id} ${p.skuCode}`);
console.log(`created default sku for product ${p.id} ${skuCode}`);
}
console.log(`done, created=${created}, scanned=${products.length}`);
}
@@ -0,0 +1,5 @@
-- 发票开票品类:酒水类 / 餐饮类(票种仍为增值税普通发票)
-- 存量申请默认酒水类,与 Prisma InvoiceCategory 默认值一致
ALTER TABLE `user_invoice`
ADD COLUMN `invoice_category` VARCHAR(16) NOT NULL DEFAULT 'LIQUOR' AFTER `invoice_kind`;
@@ -0,0 +1,147 @@
/**
* 将存量商品 / SKU 的 sku_code 全部改成 DK + 6 位数字。
* 已是 DK 数字码的保持不变;其余先写临时码再分配,避免唯一约束冲突。
*
* 用法:cd server/dukang-api && npx ts-node prisma/rewrite-sku-codes-dk.ts
*/
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const PREFIX = 'DK';
const PAD = 6;
const RE = /^DK(\d+)$/;
function isDk(code: string) {
return RE.test(code);
}
function formatCode(seq: number) {
return `${PREFIX}${String(seq).padStart(PAD, '0')}`;
}
async function maxExistingSeq(): Promise<number> {
const [items, skus] = await Promise.all([
prisma.commonProductItem.findMany({
where: { skuCode: { startsWith: PREFIX } },
select: { skuCode: true },
}),
prisma.commonProductSku.findMany({
where: { skuCode: { startsWith: PREFIX } },
select: { skuCode: true },
}),
]);
let maxSeq = 0;
for (const row of [...items, ...skus]) {
const m = RE.exec(row.skuCode);
if (!m) continue;
const n = Number(m[1]);
if (Number.isFinite(n) && n > maxSeq) maxSeq = n;
}
return maxSeq;
}
async function main() {
const skus = await prisma.commonProductSku.findMany({
orderBy: [{ productId: 'asc' }, { id: 'asc' }],
select: { id: true, productId: true, skuCode: true, isDefault: true },
});
const products = await prisma.commonProductItem.findMany({
select: { id: true, skuCode: true },
});
const skuNeed = skus.filter((s) => !isDk(s.skuCode));
const productNeed = products.filter((p) => !isDk(p.skuCode));
console.log(`sku total=${skus.length} rewrite=${skuNeed.length}`);
console.log(`product total=${products.length} rewrite=${productNeed.length}`);
await prisma.$transaction(async (tx) => {
for (const s of skuNeed) {
await tx.commonProductSku.update({
where: { id: s.id },
data: { skuCode: `TMP${s.id.toString()}` },
});
}
for (const p of productNeed) {
await tx.commonProductItem.update({
where: { id: p.id },
data: { skuCode: `TMPP${p.id.toString()}` },
});
}
const seqStart = await (async () => {
const [items, skuRows] = await Promise.all([
tx.commonProductItem.findMany({
where: { skuCode: { startsWith: PREFIX } },
select: { skuCode: true },
}),
tx.commonProductSku.findMany({
where: { skuCode: { startsWith: PREFIX } },
select: { skuCode: true },
}),
]);
let maxSeq = 0;
for (const row of [...items, ...skuRows]) {
const m = RE.exec(row.skuCode);
if (!m) continue;
const n = Number(m[1]);
if (Number.isFinite(n) && n > maxSeq) maxSeq = n;
}
return maxSeq;
})();
let seq = seqStart;
const defaultSkuCodeByProduct = new Map<string, string>();
for (const s of skuNeed) {
seq += 1;
const code = formatCode(seq);
await tx.commonProductSku.update({
where: { id: s.id },
data: { skuCode: code },
});
if (s.isDefault) defaultSkuCodeByProduct.set(s.productId.toString(), code);
console.log(`sku ${s.id} ${s.skuCode} -> ${code}`);
}
for (const p of productNeed) {
const fromDefault = defaultSkuCodeByProduct.get(p.id.toString());
let code = fromDefault;
if (!code) {
seq += 1;
code = formatCode(seq);
}
await tx.commonProductItem.update({
where: { id: p.id },
data: { skuCode: code },
});
console.log(`product ${p.id} ${p.skuCode} -> ${code}`);
}
const stillDefault = await tx.commonProductSku.findMany({
where: { isDefault: true },
select: { productId: true, skuCode: true },
});
for (const s of stillDefault) {
if (!isDk(s.skuCode)) continue;
await tx.commonProductItem.updateMany({
where: { id: s.productId, NOT: { skuCode: s.skuCode } },
data: { skuCode: s.skuCode },
});
}
});
const leftoverSku = await prisma.commonProductSku.count({
where: { NOT: { skuCode: { startsWith: PREFIX } } },
});
const leftoverProduct = await prisma.commonProductItem.count({
where: { NOT: { skuCode: { startsWith: PREFIX } } },
});
console.log(`done leftoverSku=${leftoverSku} leftoverProduct=${leftoverProduct} maxWas=${await maxExistingSeq()}`);
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
+8
View File
@@ -146,6 +146,12 @@ enum InvoiceKind {
SPECIAL
}
/// C 端开票品类(增值税票种固定普通发票)
enum InvoiceCategory {
LIQUOR
CATERING
}
enum InvoiceStatus {
PENDING
ISSUED
@@ -1678,6 +1684,8 @@ model UserInvoice {
userId BigInt @map("user_id") @db.UnsignedBigInt
titleType InvoiceTitleType @map("title_type")
invoiceKind InvoiceKind @map("invoice_kind")
/// 酒水类 / 餐饮类;历史单默认酒水类
invoiceCategory InvoiceCategory @default(LIQUOR) @map("invoice_category")
titleName String @map("title_name") @db.VarChar(128)
taxNo String? @map("tax_no") @db.VarChar(32)
addressPhone String? @map("address_phone") @db.VarChar(256)