курсач / 1302_3_Курсовая
.pdf31
stmt.finalize();
db.run('COMMIT', (err) => { if (err) reject(err);
else resolve(); });
});
});
}
}
export const studentRepository = new StudentRepository();
Листинг программного кода серверной части. student.soap.ts import { studentService } from '@/student/student.service';
export const soapService = { StudentService: {
StudentPort: {
getStudent: async function (args: { id: string }) {
const student = await studentService.getStudentById(Number(args.id)); return { student };
},
// НОВЫЙ МЕТОД getStudents: async function () {
const students = await studentService.getAllStudents(); return { students };
},
createStudent: async function (args: { name: string;
specialization: string; course: string;
}) {
32
const newStudent = await studentService.createStudent({ name: args.name,
specialization: args.specialization, course: Number(args.course),
});
return { student: newStudent }; },
},
},
};
Листинг программного кода серверной части. rest.service.ts
import type { Student, CreateStudentDto, RequestMetrics,
} from "@/types/student";
const API_URL = "http://localhost:8000/api/students";
//==========================================
//1. ХЕЛПЕР ДЛЯ ЗАМЕРОВ (Специфика курсовой)
//==========================================
async function measure<T>( label: string,
requestAction: () => Promise<Response>,
): Promise<{ data: T; metrics: RequestMetrics }> { const startTotal = performance.now();
//1. СЕТЬ (Скачиваем текст, но не парсим в объект) const response = await requestAction();
const textData = await response.text();
//2. ЗАМЕР ПАРСИНГА
const startParse = performance.now();
33
const jsonData = JSON.parse(textData); const endParse = performance.now();
const endTotal = performance.now();
const rawString = JSON.stringify(jsonData, null, 2);
return {
data: jsonData, metrics: {
method: label, protocol: "REST",
duration: endTotal - startTotal, parsingTime: endParse - startParse, dataSize: rawString.length, rawResponse: rawString,
},
};
}
//==========================================
//2. ЭКСПОРТ ДЛЯ UI (Адаптер)
//==========================================
/*
App.vue импортирует именно это.
ServicePanel получит то, что ждет (данные + метрики),
а внутри мы вызываем чистый pureRestService. */
export const restService = { async getAll() {
return measure("Get List", () => fetch(API_URL)); },
async create(student: CreateStudentDto) { return measure("Create", () =>
34
fetch(API_URL, { method: "POST",
headers: { "Content-Type": "application/json" }, body: JSON.stringify(student),
}),
);
},
};
Листинг программного кода серверной части. soap.service.ts import type {
Student,
CreateStudentDto,
RequestMetrics,
} from "@/types/student";
const SOAP_URL = "http://localhost:8000/soap";
async function measure<T>( label: string,
xmlBody: string,
parseLogic: (rawXml: string) => T,
): Promise<{ data: T; metrics: RequestMetrics }> { const startTotal = performance.now();
const response = await fetch(SOAP_URL, { method: "POST",
headers: { "Content-Type": "text/xml" }, body: xmlBody,
});
const textResponse = await response.text();
const startParse = performance.now();
const parsedData = parseLogic(textResponse); const endParse = performance.now();
35
const endTotal = performance.now();
return {
data: parsedData, metrics: {
method: label, protocol: "SOAP",
duration: endTotal - startTotal, parsingTime: endParse - startParse, dataSize: textResponse.length, rawResponse: textResponse,
},
};
}
export const soapService = { async getAll() {
const xml = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://www.examples.com/wsdl/StudentService.wsdl">
<soapenv:Header/>
<soapenv:Body><tns:getStudentsRequest/></soapenv:Body>
</soapenv:Envelope>`;
return measure<Student[]>("Get List", xml, (rawXml) => { const parser = new DOMParser();
const doc = parser.parseFromString(rawXml, "text/xml"); const nodes = doc.getElementsByTagName("students"); const students: Student[] = [];
for (let i = 0; i < nodes.length; i++) { const node = nodes[i];
if (!node) continue;
36
const getId = (tag: string) => node.getElementsByTagName(tag)[0]?.textContent;
students.push({
id: Number(getId("id") || 0), name: getId("name") || "",
specialization: getId("specialization") || "", course: Number(getId("course") || 0),
});
}
return students; });
},
async create(student: CreateStudentDto) { const xml = `
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://www.examples.com/wsdl/StudentService.wsdl">
<soapenv:Header/>
<soapenv:Body>
<tns:createStudentRequest> <tns:name>${student.name}</tns:name> <tns:specialization>${student.specialization}</tns:specialization> <tns:course>${student.course}</tns:course>
</tns:createStudentRequest>
</soapenv:Body>
</soapenv:Envelope>`;
return measure<any>("Create", xml, (rawXml) => { return rawXml;
});
},
};
Листинг программного кода серверной части. ServicePanel.vue
37
<template> <v-card
class="h-100" elevation="4" border
>
<!-- Шапка --> <v-toolbar
density="comfortable"
:color="color"
>
<template v-slot:prepend> <v-icon
:icon="icon || 'mdi-api'" class="ml-2"
/>
</template>
<v-toolbar-title class="font-weight-bold">{{ title }}</v-toolbar-title> <template v-slot:append>
<v-chip class="font-weight-bold mr-2" color="white" variant="elevated"
label
>
{{ protocol }} </v-chip>
</template> </v-toolbar>
<v-card-text class="pa-4"> <!-- Блок управления --> <v-sheet
class="pa-4 mb-4 bg-grey-lighten-5" rounded
38
border
>
<div
class="text-subtitle-2 mb-2 text-medium-emphasis text-uppercase font-weight-bold"
>
Действия
</div>
<v-btn class="mb-4" block size="large" variant="flat"
prepend-icon="mdi-download" :loading="loading" :color="color" @click="loadData"
>
Загрузить список </v-btn>
<v-row dense> <v-col cols="6">
<v-text-field v-model="newStudent.name" label="Имя" density="compact" variant="outlined" hide-details bg-color="white"
/> </v-col>
<v-col cols="4"> <v-text-field
v-model="newStudent.specialization"
39
label="Спец-ть" density="compact" variant="outlined" hide-details bg-color="white"
/> </v-col>
<v-col cols="2"> <v-btn
block class="rounded" variant="elevated" color="success" height="40" :loading="loading" @click="createData"
>
<v-icon icon="mdi-plus" /> </v-btn>
</v-col> </v-row> </v-sheet>
<div class="d-flex justify-space-between align-center mb-2"> <h3 class="text-h6">Данные</h3>
<v-badge color="grey-darken-1" inline :content="students.length"
/>
</div>
<v-sheet border rounded
40
class="student-list-container mb-4 pa-2 bg-grey-lighten-5"
>
<div
v-if="students.length === 0" class="text-center pa-4 text-grey"
>
Нет данных. Нажмите "Загрузить". </div>
<v-list v-else
bg-color="transparent" density="compact"
>
<!-- Карточка с рамкой для "студент" --> <v-list-item
v-for="s in students" :key="s.id"
class="mb-2 bg-white rounded border" elevation="1"
>
<template v-slot:prepend> <v-avatar
color="grey-lighten-3" size="small"
>
<span class="text-subtitle-2 text-black">{{ s.course }}</span> </v-avatar>
</template>
<v-list-item-title class="font-weight-bold text-body-1 ml-2"> {{ s.name }}
</v-list-item-title>
<v-list-item-subtitle class="ml-2 text-caption">
