Compare commits

...

14 Commits

Author SHA1 Message Date
brusnitsyn
2d2c5ec6b4 Исправления в build-docker
Some checks failed
Build and Push Docker Image / build (push) Failing after 26m1s
Build and Push Docker Image / test (push) Failing after 1m26s
2026-03-17 14:53:49 +09:00
brusnitsyn
5d0d80ebc3 Исправления в build-docker 2026-03-17 14:44:09 +09:00
brusnitsyn
40d1af7212 Задача #8
Some checks failed
Build and Push Docker Image / test (push) Failing after 4s
Build and Push Docker Image / build (push) Failing after 25s
Убран фильтр по номеру карты
Добавлена загрузка связи visitResult
Правка размеров колонок
Добавлена колонка исход
Исправлено определение последнего движения в SttMedicalHistory
2026-03-17 14:34:39 +09:00
brusnitsyn
3c32aeabbe Новая модель для задачи #8
Some checks failed
Build and Push Docker Image / test (push) Failing after 11s
Build and Push Docker Image / build (push) Failing after 3m17s
2026-03-17 14:30:19 +09:00
brusnitsyn
05b50cc1c8 Изменен health роут
Some checks failed
Build and Push Docker Image / test (push) Failing after 3s
Build and Push Docker Image / build (push) Failing after 25s
2026-02-27 16:01:08 +09:00
brusnitsyn
758cf666c1 Обновлен docker-compose.yml
Some checks failed
Build and Push Docker Image / test (push) Failing after 3s
Build and Push Docker Image / build (push) Has been cancelled
2026-02-27 16:00:40 +09:00
brusnitsyn
bd5261feb9 Смещение колонки отделения #2
Some checks failed
Build and Push Docker Image / test (push) Failing after 4s
Build and Push Docker Image / build (push) Has been cancelled
2026-02-27 15:59:56 +09:00
brusnitsyn
d93301ff8b Оптимизация Dockerfile
Some checks failed
Build and Push Docker Image / test (push) Failing after 3s
Build and Push Docker Image / build (push) Failing after 25s
2026-02-27 15:36:55 +09:00
brusnitsyn
47543fedba Рефакторинг
Some checks failed
Build and Push Docker Image / test (push) Failing after 3s
Build and Push Docker Image / build (push) Has been cancelled
2026-02-27 15:36:32 +09:00
brusnitsyn
b72a7f0ee1 Проверка на существование движения
Some checks failed
Build and Push Docker Image / test (push) Failing after 3s
Build and Push Docker Image / build (push) Has been cancelled
2026-02-27 15:36:18 +09:00
brusnitsyn
988ef6f519 Добавлена сортировка по имени организации
Some checks failed
Build and Push Docker Image / test (push) Failing after 3s
Build and Push Docker Image / build (push) Has been cancelled
2026-02-27 15:35:50 +09:00
brusnitsyn
5c7d1816e7 Добавлен код при выборе организации и переработана фильтрация
Some checks failed
Build and Push Docker Image / test (push) Failing after 6s
Build and Push Docker Image / build (push) Has been cancelled
2026-02-27 15:35:25 +09:00
brusnitsyn
8790b95d7b Добавлен вывод кода отделения #2
Some checks failed
Build and Push Docker Image / test (push) Failing after 4s
Build and Push Docker Image / build (push) Failing after 24s
2026-02-27 15:14:38 +09:00
brusnitsyn
707d66a532 Добавлена сортировка по номеру архива
Some checks failed
Build and Push Docker Image / test (push) Failing after 4s
Build and Push Docker Image / build (push) Failing after 25s
2026-02-27 15:11:48 +09:00
13 changed files with 170 additions and 202 deletions

View File

@@ -2,10 +2,7 @@ name: Build and Push Docker Image
on:
push:
branches: [main, master]
tags: ['v*']
pull_request:
branches: [main, master]
env:
REGISTRY: registry.brusoff.su
@@ -19,34 +16,34 @@ jobs:
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Checkout
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, xml, ctype, iconv, intl, pdo_mysql, pdo_pgsql, gd, redis, zip
coverage: none
- name: Validate composer.json
run: composer validate --strict
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Run PHP tests
run: vendor/bin/phpunit
build:
runs-on: docker
#if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/'))
steps:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v3
with:
@@ -54,14 +51,14 @@ jobs:
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
@@ -74,7 +71,7 @@ jobs:
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' }}
- name: Build and push
uses: docker/build-push-action@v5
with:
@@ -87,16 +84,16 @@ jobs:
platforms: linux/amd64,linux/arm64
build-args: |
BUILDKIT_INLINE_CACHE=1
- name: Scan image for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
if: always()
with:
sarif_file: 'trivy-results.sarif'
sarif_file: 'trivy-results.sarif'

View File

@@ -1,93 +1,63 @@
# Этап 1: PHP зависимости
FROM dh-mirror.gitverse.ru/php:8.3-fpm AS phpbuild
# Этап 1: Сборка PHP зависимостей
FROM webdevops/php:8.3-alpine AS php-build
# Установка системных зависимостей
RUN apt-get update && apt-get install -y \
ENV WEB_DOCUMENT_ROOT=/var/www/html/public
ENV PHP_DATE_TIMEZONE=UTC
ENV PHP_DISPLAY_ERRORS=0
ENV PHP_MEMORY_LIMIT=512M
ENV PHP_MAX_EXECUTION_TIME=300
ENV PHP_OPCACHE_ENABLE=1
ENV PHP_OPCACHE_MEMORY_CONSUMPTION=256
# Проверяем установленные расширения (для отладки)
RUN php -m && \
php -i | grep gd && \
php -i | grep redis
# Установка дополнительных системных зависимостей, если нужно
RUN apk update && apk add --no-cache \
git \
curl \
wget \
unzip \
# Для сборки некоторых зависимостей Composer
libzip-dev \
libxml2-dev \
libicu-dev \
libonig-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev \
libpq-dev \
libxslt1-dev \
libexif-dev \
libffi-dev \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
# Установка PHP расширений
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
--with-webp \
&& docker-php-ext-install -j$(nproc) \
bcmath \
intl \
mbstring \
zip \
opcache \
pdo \
pdo_mysql \
pdo_pgsql \
gd \
exif \
sockets \
xsl \
ffi \
pcntl
# Установка Redis расширения
RUN pecl install redis && docker-php-ext-enable redis
# Настройка opcache для production
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.interned_strings_buffer=32" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.max_accelerated_files=32531" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini
oniguruma-dev \
&& docker-run-bootstrap
# Установка Composer
COPY --from=dh-mirror.gitverse.ru/composer:2.7 /usr/bin/composer /usr/bin/composer
COPY --from=composer:2.7 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
WORKDIR /var/www/html
# Копирование файлов для установки зависимостей
# Копируем только файлы зависимостей (для лучшего кэширования слоев)
COPY composer.json composer.lock ./
# Установка PHP зависимостей
# Установка PHP зависимостей (NO DEV для production)
RUN composer install \
--no-interaction \
--no-progress \
--no-scripts \
--prefer-dist \
--optimize-autoloader \
--apcu-autoloader
--apcu-autoloader \
--no-dev
# Копируем исходный код
# Копируем остальной код
COPY . .
# Установка прав
RUN chown -R www-data:www-data /var/www && \
chmod -R 775 /var/www/storage /var/www/bootstrap/cache
# Выполняем скрипты post-install
#RUN composer run-script post-install-cmd
# Этап 2: Сборка фронтенда (Inertia + Vue 3)
FROM dh-mirror.gitverse.ru/node:20 AS jsbuild
# Оптимизируем автозагрузчик
RUN composer dump-autoload --optimize
# Установка дополнительных зависимостей для сборки
RUN apt-get update && apt-get install -y \
python3 \
make \
g++ \
&& rm -rf /var/lib/apt/lists/*
# Устанавливаем права
RUN chown -R application:application /var/www/html && \
chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
WORKDIR /var/www
# Этап 2: Сборка фронтенда
FROM node:20-alpine AS node-build
WORKDIR /var/www/html
# Копируем зависимости Node.js
COPY package.json package-lock.json* ./
@@ -99,109 +69,77 @@ RUN npm ci \
# Копируем файлы для сборки фронтенда
COPY vite.config.js ./
COPY resources/js ./resources/js/
COPY resources/css ./resources/css/
COPY resources/ ./resources/
# Сборка ассетов Vite
# Сборка ассетов
RUN npm run build
# Этап 3: Финальный образ
FROM dh-mirror.gitverse.ru/php:8.3-fpm
# Этап 3: Финальный production образ
FROM webdevops/php-nginx:8.3-alpine
# Установка runtime зависимостей
RUN apt-get update && apt-get install -y \
libxml2 \
libonig5 \
libpng16-16 \
libjpeg62-turbo \
libfreetype6 \
libwebp7 \
libpq5 \
libxslt1.1 \
libexif12 \
libffi8 \
# Production настройки
ENV WEB_DOCUMENT_ROOT=/var/www/html/public
ENV PHP_DATE_TIMEZONE=UTC
ENV PHP_DISPLAY_ERRORS=0
ENV PHP_MEMORY_LIMIT=512M
ENV PHP_MAX_EXECUTION_TIME=300
ENV PHP_POST_MAX_SIZE=100M
ENV PHP_UPLOAD_MAX_FILESIZE=100M
ENV PHP_OPCACHE_ENABLE=1
ENV PHP_OPCACHE_MEMORY_CONSUMPTION=256
ENV PHP_OPCACHE_MAX_ACCELERATED_FILES=32531
ENV PHP_OPCACHE_VALIDATE_TIMESTAMPS=0
ENV NGINX_WORKER_PROCESSES=auto
ENV NGINX_WORKER_CONNECTIONS=1024
# Включаем дополнительные сервисы
#RUN docker-service enable cron && \
# docker-service enable supervisor
# Устанавливаем дополнительные пакеты, если нужны
RUN apk update && apk add --no-cache \
supervisor \
nginx \
cron \
&& rm -rf /var/lib/apt/lists/*
# Установка системных зависимостей
RUN apt-get update && apt-get install -y \
git \
curl \
wget \
unzip \
libzip-dev \
libxml2-dev \
libicu-dev \
libonig-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
libwebp-dev \
libpq-dev \
libxslt1-dev \
libexif-dev \
libffi-dev \
pkg-config \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
# Установка PHP расширений
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
--with-webp \
&& docker-php-ext-install -j$(nproc) \
bcmath \
intl \
mbstring \
zip \
opcache \
pdo \
pdo_mysql \
pdo_pgsql \
gd \
exif \
sockets \
xsl \
ffi \
pcntl
# Установка Redis расширения
RUN pecl install redis && docker-php-ext-enable redis
# Настройка opcache для production
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.interned_strings_buffer=32" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.max_accelerated_files=32531" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.validate_timestamps=0" >> /usr/local/etc/php/conf.d/opcache.ini
# Копируем PHP расширения из первого этапа
#COPY --from=phpbuild /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
#COPY --from=phpbuild /usr/local/lib/php/extensions/no-debug-non-zts-20230831/ /usr/local/lib/php/extensions/no-debug-non-zts-20230831/
#COPY --from=phpbuild /usr/local/bin/ /usr/local/bin/
# Для планировщика Laravel
dcron \
# Дополнительные утилиты
nano \
htop \
&& docker-run-bootstrap
# Копируем конфигурации
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/app.conf /etc/nginx/conf.d/default.conf
COPY docker/supervisord.conf /etc/supervisor/supervisord.conf
WORKDIR /var/www
WORKDIR /var/www/html
# Копируем приложение
COPY --chown=www-data:www-data --from=phpbuild /var/www .
COPY --chown=www-data:www-data --from=jsbuild /var/www/public/build ./public/build
COPY --chown=www-data:www-data --from=jsbuild /var/www/node_modules ./node_modules
# Копируем приложение из этапов сборки
COPY --chown=application:application --from=php-build /var/www/html .
COPY --chown=application:application --from=node-build /var/www/html/public/build ./public/build
# Настройка прав и оптимизация Laravel
RUN mkdir -p /var/log/supervisor && \
chown -R www-data:www-data /var/www /var/log/supervisor && \
chmod -R 775 /var/www/storage /var/www/bootstrap/cache
# Настраиваем права
RUN chown -R application:application /var/www/html && \
chmod -R 775 /var/www/html/storage /var/www/html/bootstrap/cache
# Создание ссылки на Storage
# Создаем символические ссылки
RUN php artisan storage:link
# Кэшируем конфигурации Laravel для production
#RUN php artisan config:cache && \
# php artisan route:cache && \
# php artisan view:cache
# Удаляем все тестовые файлы PHP (для docker-squash)
# https://github.com/shinsenter/docker-squash
RUN rm -rf /usr/src/php \
&& rm -f /usr/src/php.tar.xz 2>/dev/null || true \
&& rm -rf /usr/local/lib/php/test \
&& rm -rf /usr/local/lib/php/doc
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/health || exit 1
EXPOSE 80
CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/supervisord.conf"]
CMD ["supervisord", "-c", "/etc/supervisor.d/supervisord.conf"]

View File

@@ -47,8 +47,6 @@ class IndexController extends Controller
$pageSize
);
// dd($data);
$statuses = ArchiveStatus::all()->map(function ($status) {
return [
'value' => $status->id,

View File

@@ -23,7 +23,7 @@ class MedicalHistoryController extends Controller
else $patient = MisSttMedicalHistory::where('MedicalHistoryID', $id)->first();
if($patient instanceof MisSttMedicalHistory) {
if ($patient->archiveHistory->count() === 0) {
if ($patient->archiveHistory->count() === 0 && $patient->archiveInfo->foxpro_history_id) {
$foxproCardId = $patient->archiveInfo->foxpro_history_id;
$foxproPatient = SiSttMedicalHistory::where('keykarta', $foxproCardId)->first();
@@ -44,7 +44,7 @@ class MedicalHistoryController extends Controller
...PatientInfoResource::make($patient)->toArray(request()),
'can_be_issued' => $patient->canBeIssued()
],
'journal' => ArchiveHistoryResource::collection($journalHistory),
'journal' => $journalHistory ? ArchiveHistoryResource::collection($journalHistory) : [],
'archiveInfo' => $archiveInfo ? ArchiveInfoResource::make($archiveInfo) : null
];

View File

@@ -9,7 +9,7 @@ class OrgController extends Controller
{
public function index()
{
$orgs = Org::all(['id', 'name', 'code']);
$orgs = Org::orderBy('name')->get(['id', 'name', 'code']);
return response()->json($orgs);
}

View File

@@ -57,7 +57,9 @@ class IndexSttMedicalHistoryResource extends JsonResource
? "$archiveInfoMisCardNumber / $archiveInfoFoxproCardNumber"
: $archiveInfoMisCardNumber ?? $archiveInfoFoxproCardNumber;
$department = $historyType === 'mis' ? $model->outcomeMigration->stationarBranch->department : null;
$department = $historyType === 'mis' ? $model->outcomeMigration?->first()?->stationarBranch->department : null;
$visitResult = $historyType === 'mis' ? $this->visitResult->NAME : null;
return [
'id' => $id,
@@ -71,7 +73,8 @@ class IndexSttMedicalHistoryResource extends JsonResource
'dr' => $formattedBirthDate,
'daterecipient' => $formattedDateRecipient,
'dateextract' => $formattedDateExtract,
'department' => $department?->DepartmentNAME,
'department' => $department ? '[' . $department->DepartmentCODE . '] ' . $department->DepartmentNAME : 'Н/д',
'visitresult' => $visitResult,
// Номера карт
'medcardnum' => $cardNumber, // MIS номер или FoxPro номер

View File

@@ -4,6 +4,7 @@ namespace App\Models\Mis;
use App\Models\ArchiveHistory;
use App\Models\ArchiveInfo;
use App\Models\OmsVisitResult;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
@@ -34,14 +35,14 @@ class SttMedicalHistory extends Model
public function migrations()
{
return $this->hasOne(SttMigrationPatient::class, 'rf_MedicalHistoryID', 'MedicalHistoryID');
return $this->hasMany(SttMigrationPatient::class, 'rf_MedicalHistoryID', 'MedicalHistoryID');
}
public function outcomeMigration()
{
return $this->migrations()
->where('rf_kl_VisitResultID', 1)
->whereDate('DateOut', '<>', '2222-01-01');
->whereDate('DateOut', '<>', '2222-01-01')
->orderBy('DateOut', 'desc');
}
/**
@@ -77,4 +78,9 @@ class SttMedicalHistory extends Model
->latest('issue_at')
->first();
}
public function visitResult()
{
return $this->hasOne(OmsVisitResult::class, 'kl_VisitResultID', 'rf_kl_VisitResultID');
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class OmsVisitResult extends Model
{
protected $table = 'oms_kl_visitresult';
protected $primaryKey = 'kl_VisitResultID';
}

View File

@@ -78,6 +78,7 @@ class ArchiveCardService
// Приоритет MIS истории
if ($archiveInfo->misHistory) {
$history = $archiveInfo->misHistory;
$history->load('visitResult');
$history->history_type = 'mis';
} else {
$history = $archiveInfo->foxproHistory;

View File

@@ -10,7 +10,7 @@ return Application::configure(basePath: dirname(__DIR__))
web: __DIR__.'/../routes/web.php',
api: __DIR__.'/../routes/api.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
health: '/health',
)
->withMiddleware(function (Middleware $middleware): void {
$middleware->web(append: [

View File

@@ -1,15 +1,15 @@
services:
#PHP Service
app:
image: registry.brusoff.su/kartoteka:1.0
image: registry.brusoff.su/kartoteka:2026.02-prod
build: .
container_name: kartoteka_app
restart: unless-stopped
ports:
- "8090:80"
working_dir: /var/www
working_dir: /var/www/html
volumes:
- ./.env:/var/www/.env
- ./.env:/var/www/html/.env
- ./docker/php.ini:/usr/local/etc/php/conf.d/app.ini
- ./docker/blocked_ips.map:/etc/nginx/blocked_ips.map
- ./storage/logs:/var/www/storage/logs

View File

@@ -63,7 +63,10 @@ const onCloseWithoutSave = () => {
const loadArchiveHistoryData = async () => {
try {
await axios.get('/api/orgs').then(res => {
orgs.value = res.data
orgs.value = res.data.map(org => ({
...org,
name: `${org.code} - ${org.name}`
}))
})
if (!props.archiveHistoryId) return
@@ -100,6 +103,10 @@ const submit = () => {
}
}
const filterOrg = (pattern, option) => {
return option.name.startsWith(pattern)
}
// Наблюдаем за изменением archiveHistoryId
watch(() => props.archiveHistoryId, async (newId) => {
if (!props.isCreateNew) {
@@ -123,7 +130,7 @@ watch(() => props.archiveHistoryId, async (newId) => {
<NDatePicker v-model:value="archiveHistory.return_at" format="dd.MM.yyyy" />
</NFormItem>
<NFormItem label="Организация">
<NSelect v-model:value="archiveHistory.org_id" :options="orgs" label-field="name" value-field="id" filterable />
<NSelect v-model:value="archiveHistory.org_id" :filter="(pattern, option) => filterOrg(pattern, option)" :options="orgs" label-field="name" value-field="id" filterable />
</NFormItem>
<NFormItem label="Имя сотрудника">
<NInput v-model:value="archiveHistory.employee_name" />

View File

@@ -44,15 +44,27 @@ const columns = ref([
{
title: '№ карты',
key: 'medcardnum',
width: 100,
width: 80,
render: (row) => h(NEllipsis, null, { default: () => row.medcardnum })
},
{
title: 'ФИО',
key: 'fullname',
width: 250,
width: 200,
render: (row) => h(NEllipsis, null, { default: () => row.fullname })
},
{
title: 'Исход',
key: 'visitresult',
width: 60,
render: (row) => h(NEllipsis, null, { default: () => row.visitresult ?? 'Н/д' })
},
{
title: 'Отделение',
key: 'department',
width: 220,
render: (row) => h(NEllipsis, null, { default: () => row.department })
},
{
title: 'Дата рождения',
key: 'dr',
@@ -68,31 +80,25 @@ const columns = ref([
{
title: 'Дата выписки',
key: 'dateextract',
width: 100,
width: 80,
render: (row) => h(NEllipsis, null, { default: () => row.dateextract })
},
{
title: 'Отделение',
key: 'department',
width: 180,
render: (row) => h(NEllipsis, null, { default: () => row.department })
},
{
title: '№ архива',
key: 'card_num',
width: 120,
width: 80,
render: (row) => h(NEllipsis, null, { default: () => row.card_num || '-' })
},
{
title: 'Дата архива',
key: 'datearhiv',
width: 130,
width: 80,
render: (row) => h(NEllipsis, null, { default: () => row.datearhiv })
},
{
title: 'Статус',
key: 'status',
width: 100,
width: 60,
render: (row) => archiveStatusColumn(row.status)
}
])
@@ -152,6 +158,7 @@ watch(() => archiveCardStore.isOpenArchiveCard, (isOpen) => {
<template>
<NDataTable remote
:single-line="false"
striped
:loading="isLoading"
:row-props="rowProps"