Files
backend-node/src/serials/serials.service.ts

282 lines
7.0 KiB
TypeScript

import { Injectable } from "@nestjs/common";
import { DatabaseService } from "../database/database.service";
import * as QRCode from "qrcode";
import { Serial, SerialListItem } from "./dto";
@Injectable()
export class SerialsService {
constructor(private dbService: DatabaseService) {}
async generate(
companyName: string,
quantity: number,
validDays: number,
userId: number,
serialPrefix?: string,
): Promise<SerialListItem[]> {
const prisma = this.dbService.getPrisma();
const validUntil = new Date();
validUntil.setDate(validUntil.getDate() + validDays);
const existingCompany = await prisma.company.findUnique({
where: { companyName },
});
if (!existingCompany) {
await prisma.company.create({
data: { companyName, isActive: true },
});
}
const serials: SerialListItem[] = [];
const prefix = serialPrefix
? serialPrefix.toUpperCase().replace(/[^A-Z0-9]/g, "")
: "BF" + new Date().getFullYear().toString().substr(2);
for (let i = 0; i < quantity; i++) {
const randomPart = Math.floor(Math.random() * 1000000)
.toString()
.padStart(6, "0");
const serialNumber = `${prefix}${randomPart}`;
await prisma.serial.create({
data: {
serialNumber,
companyName,
validUntil,
createdBy: userId,
isActive: true,
},
});
serials.push({
serialNumber,
companyName,
validUntil: validUntil.toISOString(),
isActive: true,
createdAt: new Date().toISOString(),
});
}
return serials;
}
async generateQRCode(
serialNumber: string,
baseUrl?: string,
requestHost?: string,
protocol?: string,
) {
const prisma = this.dbService.getPrisma();
const serial = await prisma.serial.findUnique({
where: { serialNumber: serialNumber.toUpperCase() },
include: {
user: {
select: { name: true },
},
},
});
if (!serial) {
throw new Error("序列号不存在");
}
if (!serial.isActive) {
throw new Error("序列号已被禁用");
}
if (serial.validUntil && new Date(serial.validUntil) < new Date()) {
throw new Error("序列号已过期");
}
if (!baseUrl) {
baseUrl = `${protocol}://${requestHost}/query.html`;
}
const queryUrl = baseUrl.includes("?")
? `${baseUrl}&serial=${serial.serialNumber}`
: `${baseUrl}?serial=${serial.serialNumber}`;
const qrCodeData = await QRCode.toDataURL(queryUrl, {
width: 200,
color: {
dark: "#165DFF",
light: "#ffffff",
},
});
return {
message: "二维码生成成功",
qrCodeData,
queryUrl,
serialNumber: serial.serialNumber,
companyName: serial.companyName,
validUntil: serial.validUntil,
};
}
async query(serialNumber: string) {
const prisma = this.dbService.getPrisma();
const serial = await prisma.serial.findUnique({
where: { serialNumber: serialNumber.toUpperCase() },
include: {
user: {
select: { name: true },
},
},
});
if (!serial) {
throw new Error("序列号不存在");
}
if (serial.validUntil && new Date(serial.validUntil) < new Date()) {
throw new Error("序列号已过期");
}
return {
message: "查询成功",
serial: {
serialNumber: serial.serialNumber,
companyName: serial.companyName,
validUntil: serial.validUntil,
status: serial.isActive ? "active" : "disabled",
isActive: serial.isActive,
createdAt: serial.createdAt,
createdBy: serial.user?.name,
},
};
}
async findAll(page: number, limit: number, search: string) {
const prisma = this.dbService.getPrisma();
const offset = (page - 1) * limit;
const where = search
? {
OR: [
{ serialNumber: { contains: search } },
{ companyName: { contains: search } },
],
}
: undefined;
const [serials, total] = await Promise.all([
prisma.serial.findMany({
where,
include: {
user: {
select: { name: true },
},
},
orderBy: { createdAt: "desc" },
skip: offset,
take: limit,
}),
prisma.serial.count({ where }),
]);
const totalPages = Math.ceil(total / limit);
return {
message: "获取序列号列表成功",
data: serials.map((s) => ({
serialNumber: s.serialNumber,
companyName: s.companyName,
validUntil: s.validUntil,
isActive: s.isActive,
createdAt: s.createdAt,
createdBy: s.user?.name,
})),
pagination: {
page: parseInt(page.toString()),
limit: parseInt(limit.toString()),
total,
totalPages,
},
};
}
async update(
serialNumber: string,
updateData: {
companyName?: string;
validUntil?: string;
isActive?: boolean;
},
) {
const prisma = this.dbService.getPrisma();
const existingSerial = await prisma.serial.findUnique({
where: { serialNumber: serialNumber.toUpperCase() },
});
if (!existingSerial) {
throw new Error("序列号不存在");
}
const updateFields: any = {};
if (updateData.companyName !== undefined) {
updateFields.companyName = updateData.companyName;
}
if (updateData.validUntil !== undefined) {
updateFields.validUntil = new Date(updateData.validUntil);
}
if (updateData.isActive !== undefined) {
updateFields.isActive = updateData.isActive;
}
if (Object.keys(updateFields).length === 0) {
throw new Error("没有提供更新字段");
}
const updatedSerial = await prisma.serial.update({
where: { serialNumber: serialNumber.toUpperCase() },
data: updateFields,
include: {
user: {
select: { name: true },
},
},
});
return {
message: "序列号更新成功",
serial: {
serialNumber: updatedSerial.serialNumber,
companyName: updatedSerial.companyName,
validUntil: updatedSerial.validUntil,
isActive: updatedSerial.isActive,
createdAt: updatedSerial.createdAt,
updatedAt: updatedSerial.updatedAt,
createdBy: updatedSerial.user?.name,
},
};
}
async revoke(serialNumber: string) {
const prisma = this.dbService.getPrisma();
const existingSerial = await prisma.serial.findUnique({
where: { serialNumber: serialNumber.toUpperCase() },
});
if (!existingSerial) {
throw new Error("序列号不存在");
}
if (!existingSerial.isActive) {
throw new Error("序列号已被吊销");
}
await prisma.serial.update({
where: { serialNumber: serialNumber.toUpperCase() },
data: { isActive: false },
});
return {
message: "序列号已吊销",
data: {
serialNumber: serialNumber.toUpperCase(),
},
};
}
}