바코드 기능 커밋밋
This commit is contained in:
247
backend-node/src/services/barcodeLabelService.ts
Normal file
247
backend-node/src/services/barcodeLabelService.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* 바코드 라벨 관리 서비스
|
||||
* ZD421 등 라벨 디자인 CRUD 및 기본 템플릿 제공
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { query, queryOne, transaction } from "../database/db";
|
||||
import { BarcodeLabelLayout } from "../types/barcode";
|
||||
|
||||
export interface BarcodeLabelMaster {
|
||||
label_id: string;
|
||||
label_name_kor: string;
|
||||
label_name_eng: string | null;
|
||||
description: string | null;
|
||||
width_mm: number;
|
||||
height_mm: number;
|
||||
layout_json: string | null;
|
||||
use_yn: string;
|
||||
created_at: string;
|
||||
created_by: string | null;
|
||||
updated_at: string | null;
|
||||
updated_by: string | null;
|
||||
}
|
||||
|
||||
export interface BarcodeLabelTemplate {
|
||||
template_id: string;
|
||||
template_name_kor: string;
|
||||
template_name_eng: string | null;
|
||||
width_mm: number;
|
||||
height_mm: number;
|
||||
layout_json: string;
|
||||
sort_order: number;
|
||||
}
|
||||
|
||||
export interface GetBarcodeLabelsParams {
|
||||
page?: number;
|
||||
limit?: number;
|
||||
searchText?: string;
|
||||
useYn?: string;
|
||||
sortBy?: string;
|
||||
sortOrder?: "ASC" | "DESC";
|
||||
}
|
||||
|
||||
export interface GetBarcodeLabelsResult {
|
||||
items: BarcodeLabelMaster[];
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
export class BarcodeLabelService {
|
||||
async getLabels(params: GetBarcodeLabelsParams): Promise<GetBarcodeLabelsResult> {
|
||||
const {
|
||||
page = 1,
|
||||
limit = 20,
|
||||
searchText = "",
|
||||
useYn = "Y",
|
||||
sortBy = "created_at",
|
||||
sortOrder = "DESC",
|
||||
} = params;
|
||||
|
||||
const offset = (page - 1) * limit;
|
||||
const conditions: string[] = [];
|
||||
const values: any[] = [];
|
||||
let idx = 1;
|
||||
|
||||
if (useYn) {
|
||||
conditions.push(`use_yn = $${idx++}`);
|
||||
values.push(useYn);
|
||||
}
|
||||
if (searchText) {
|
||||
conditions.push(`(label_name_kor LIKE $${idx} OR label_name_eng LIKE $${idx})`);
|
||||
values.push(`%${searchText}%`);
|
||||
idx++;
|
||||
}
|
||||
const where = conditions.length ? `WHERE ${conditions.join(" AND ")}` : "";
|
||||
|
||||
const countSql = `SELECT COUNT(*) as total FROM barcode_labels ${where}`;
|
||||
const countRow = await queryOne<{ total: string }>(countSql, values);
|
||||
const total = parseInt(countRow?.total || "0", 10);
|
||||
|
||||
const listSql = `
|
||||
SELECT label_id, label_name_kor, label_name_eng, description, width_mm, height_mm,
|
||||
layout_json, use_yn, created_at, created_by, updated_at, updated_by
|
||||
FROM barcode_labels ${where}
|
||||
ORDER BY ${sortBy} ${sortOrder}
|
||||
LIMIT $${idx++} OFFSET $${idx}
|
||||
`;
|
||||
const items = await query<BarcodeLabelMaster>(listSql, [...values, limit, offset]);
|
||||
|
||||
return { items, total, page, limit };
|
||||
}
|
||||
|
||||
async getLabelById(labelId: string): Promise<BarcodeLabelMaster | null> {
|
||||
const sql = `
|
||||
SELECT label_id, label_name_kor, label_name_eng, description, width_mm, height_mm,
|
||||
layout_json, use_yn, created_at, created_by, updated_at, updated_by
|
||||
FROM barcode_labels WHERE label_id = $1
|
||||
`;
|
||||
return queryOne<BarcodeLabelMaster>(sql, [labelId]);
|
||||
}
|
||||
|
||||
async getLayout(labelId: string): Promise<BarcodeLabelLayout | null> {
|
||||
const row = await this.getLabelById(labelId);
|
||||
if (!row?.layout_json) return null;
|
||||
try {
|
||||
return JSON.parse(row.layout_json) as BarcodeLabelLayout;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async createLabel(
|
||||
data: { labelNameKor: string; labelNameEng?: string; description?: string; templateId?: string },
|
||||
userId: string
|
||||
): Promise<string> {
|
||||
const labelId = `LBL_${uuidv4().replace(/-/g, "").substring(0, 20)}`;
|
||||
let widthMm = 50;
|
||||
let heightMm = 30;
|
||||
let layoutJson: string | null = null;
|
||||
|
||||
if (data.templateId) {
|
||||
const t = await this.getTemplateById(data.templateId);
|
||||
if (t) {
|
||||
widthMm = t.width_mm;
|
||||
heightMm = t.height_mm;
|
||||
layoutJson = t.layout_json;
|
||||
}
|
||||
}
|
||||
if (!layoutJson) {
|
||||
const defaultLayout: BarcodeLabelLayout = {
|
||||
width_mm: widthMm,
|
||||
height_mm: heightMm,
|
||||
components: [],
|
||||
};
|
||||
layoutJson = JSON.stringify(defaultLayout);
|
||||
}
|
||||
|
||||
await query(
|
||||
`INSERT INTO barcode_labels (label_id, label_name_kor, label_name_eng, description, width_mm, height_mm, layout_json, use_yn, created_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, 'Y', $8)`,
|
||||
[
|
||||
labelId,
|
||||
data.labelNameKor,
|
||||
data.labelNameEng || null,
|
||||
data.description || null,
|
||||
widthMm,
|
||||
heightMm,
|
||||
layoutJson,
|
||||
userId,
|
||||
]
|
||||
);
|
||||
return labelId;
|
||||
}
|
||||
|
||||
async updateLabel(
|
||||
labelId: string,
|
||||
data: { labelNameKor?: string; labelNameEng?: string; description?: string; useYn?: string },
|
||||
userId: string
|
||||
): Promise<boolean> {
|
||||
const setClauses: string[] = [];
|
||||
const values: any[] = [];
|
||||
let idx = 1;
|
||||
if (data.labelNameKor !== undefined) {
|
||||
setClauses.push(`label_name_kor = $${idx++}`);
|
||||
values.push(data.labelNameKor);
|
||||
}
|
||||
if (data.labelNameEng !== undefined) {
|
||||
setClauses.push(`label_name_eng = $${idx++}`);
|
||||
values.push(data.labelNameEng);
|
||||
}
|
||||
if (data.description !== undefined) {
|
||||
setClauses.push(`description = $${idx++}`);
|
||||
values.push(data.description);
|
||||
}
|
||||
if (data.useYn !== undefined) {
|
||||
setClauses.push(`use_yn = $${idx++}`);
|
||||
values.push(data.useYn);
|
||||
}
|
||||
if (setClauses.length === 0) return false;
|
||||
setClauses.push(`updated_at = CURRENT_TIMESTAMP`);
|
||||
setClauses.push(`updated_by = $${idx++}`);
|
||||
values.push(userId);
|
||||
values.push(labelId);
|
||||
|
||||
const updated = await query<{ label_id: string }>(
|
||||
`UPDATE barcode_labels SET ${setClauses.join(", ")} WHERE label_id = $${idx} RETURNING label_id`,
|
||||
values
|
||||
);
|
||||
return updated.length > 0;
|
||||
}
|
||||
|
||||
async saveLayout(labelId: string, layout: BarcodeLabelLayout, userId: string): Promise<boolean> {
|
||||
const layoutJson = JSON.stringify(layout);
|
||||
await query(
|
||||
`UPDATE barcode_labels SET width_mm = $1, height_mm = $2, layout_json = $3, updated_at = CURRENT_TIMESTAMP, updated_by = $4 WHERE label_id = $5`,
|
||||
[layout.width_mm, layout.height_mm, layoutJson, userId, labelId]
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteLabel(labelId: string): Promise<boolean> {
|
||||
const deleted = await query<{ label_id: string }>(
|
||||
`DELETE FROM barcode_labels WHERE label_id = $1 RETURNING label_id`,
|
||||
[labelId]
|
||||
);
|
||||
return deleted.length > 0;
|
||||
}
|
||||
|
||||
async copyLabel(labelId: string, userId: string): Promise<string | null> {
|
||||
const row = await this.getLabelById(labelId);
|
||||
if (!row) return null;
|
||||
const newId = `LBL_${uuidv4().replace(/-/g, "").substring(0, 20)}`;
|
||||
await query(
|
||||
`INSERT INTO barcode_labels (label_id, label_name_kor, label_name_eng, description, width_mm, height_mm, layout_json, use_yn, created_by)
|
||||
VALUES ($1, $2 || ' (복사)', $3, $4, $5, $6, $7, 'Y', $8)`,
|
||||
[
|
||||
newId,
|
||||
row.label_name_kor,
|
||||
row.label_name_eng,
|
||||
row.description,
|
||||
row.width_mm,
|
||||
row.height_mm,
|
||||
row.layout_json,
|
||||
userId,
|
||||
]
|
||||
);
|
||||
return newId;
|
||||
}
|
||||
|
||||
async getTemplates(): Promise<BarcodeLabelTemplate[]> {
|
||||
const sql = `
|
||||
SELECT template_id, template_name_kor, template_name_eng, width_mm, height_mm, layout_json, sort_order
|
||||
FROM barcode_label_templates ORDER BY sort_order, template_id
|
||||
`;
|
||||
const rows = await query<BarcodeLabelTemplate>(sql);
|
||||
return rows || [];
|
||||
}
|
||||
|
||||
async getTemplateById(templateId: string): Promise<BarcodeLabelTemplate | null> {
|
||||
const sql = `SELECT template_id, template_name_kor, template_name_eng, width_mm, height_mm, layout_json, sort_order
|
||||
FROM barcode_label_templates WHERE template_id = $1`;
|
||||
return queryOne<BarcodeLabelTemplate>(sql, [templateId]);
|
||||
}
|
||||
}
|
||||
|
||||
export default new BarcodeLabelService();
|
||||
Reference in New Issue
Block a user