feat: 上传全部影像
This commit is contained in:
parent
09bcfb81b4
commit
267c5dfa43
|
@ -17,8 +17,9 @@ export class UserService {
|
||||||
|
|
||||||
async signIn(user: Pick<User, "username" | "password">) {
|
async signIn(user: Pick<User, "username" | "password">) {
|
||||||
const { code, data, msg } = await this.userRepository.authLogin(user);
|
const { code, data, msg } = await this.userRepository.authLogin(user);
|
||||||
this.user.signIn(data as User);
|
const success = code === 0;
|
||||||
return { success: code === 0, msg, data: data as User };
|
if (success) this.user.signIn(data as User);
|
||||||
|
return { success, msg, data: data as User };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -35,9 +35,9 @@ export class User {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取角色菜单
|
* 获取用户拥有的角色名数组
|
||||||
*/
|
*/
|
||||||
getRolesName() {
|
getRolesNames() {
|
||||||
return this.roles?.map((r: { name: string }) => r.name);
|
return this.roles?.map((r: { name: string }) => r.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { useRef, useState } from "react";
|
import React, { useRef, useState } from "react";
|
||||||
import { Study, parseDcmFiles } from "./util";
|
import { Study, parseDcmFiles } from "./util";
|
||||||
import { Button } from "antd";
|
import { Button } from "antd";
|
||||||
import { CloudUploadOutlined } from "@ant-design/icons";
|
import { FolderOutlined } from "@ant-design/icons";
|
||||||
|
|
||||||
declare module "react" {
|
declare module "react" {
|
||||||
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
|
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
|
||||||
|
@ -59,11 +59,11 @@ export const useDicomUploader = () => {
|
||||||
onChange={(e) => handleFileChange(e)}
|
onChange={(e) => handleFileChange(e)}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
icon={<CloudUploadOutlined />}
|
icon={<FolderOutlined />}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={() => fileInputRef.current?.click()}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
>
|
>
|
||||||
上传dicom
|
选择dicom
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,14 @@
|
||||||
import { Button, Col, Divider, Modal, Row, Select, Space, Spin } from "antd";
|
import {
|
||||||
|
Button,
|
||||||
|
Col,
|
||||||
|
Divider,
|
||||||
|
Modal,
|
||||||
|
Progress,
|
||||||
|
Row,
|
||||||
|
Select,
|
||||||
|
Space,
|
||||||
|
message,
|
||||||
|
} from "antd";
|
||||||
import { useDicomUploader } from "./DicomUploader";
|
import { useDicomUploader } from "./DicomUploader";
|
||||||
import { Series, Study } from "./DicomUploader/util";
|
import { Series, Study } from "./DicomUploader/util";
|
||||||
import { DicomTable } from "./DicomTable";
|
import { DicomTable } from "./DicomTable";
|
||||||
|
@ -7,7 +17,8 @@ import { Typography } from "antd";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { User } from "@@/domain/User/entities/User";
|
import { User } from "@@/domain/User/entities/User";
|
||||||
import { limitConcurrency } from "./limitConcurrency";
|
import { limitConcurrency } from "./limitConcurrency";
|
||||||
import { useProgress } from "./useProgress";
|
import { CloudUploadOutlined, InboxOutlined } from "@ant-design/icons";
|
||||||
|
import { openOHIFViewer, flatternStudies, FlatStudyItem } from "./util";
|
||||||
|
|
||||||
const { Text } = Typography;
|
const { Text } = Typography;
|
||||||
|
|
||||||
|
@ -15,21 +26,9 @@ interface DicomUploadProps {
|
||||||
children?: JSX.Element;
|
children?: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 新窗口打开ohif阅片
|
|
||||||
*/
|
|
||||||
const openOHIFViewer = (
|
|
||||||
StudyInstanceUID: string,
|
|
||||||
SeriesInstanceUID: string
|
|
||||||
) => {
|
|
||||||
const target = `http://localhost:3000/viewer/${StudyInstanceUID}?SeriesInstanceUID=${SeriesInstanceUID}`;
|
|
||||||
window.open(target, "_blank");
|
|
||||||
};
|
|
||||||
|
|
||||||
export const DicomUpload = (props: DicomUploadProps) => {
|
export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
|
const [messageApi, contextHolder] = message.useMessage();
|
||||||
const { UploadInput, fileCalculator, studys, isLoading } = useDicomUploader();
|
const { UploadInput, fileCalculator, studys, isLoading } = useDicomUploader();
|
||||||
const { ProgressModal, showProgress, hideProgress, updatePercent } =
|
|
||||||
useProgress();
|
|
||||||
const { dicomDomainService, userDomainService } = useDomain();
|
const { dicomDomainService, userDomainService } = useDomain();
|
||||||
const { dcmFileNum, totalFileNum, dcmFileSize } = fileCalculator;
|
const { dcmFileNum, totalFileNum, dcmFileSize } = fileCalculator;
|
||||||
const [selectRows, setSelectedRows] = useState<Study[]>([]);
|
const [selectRows, setSelectedRows] = useState<Study[]>([]);
|
||||||
|
@ -38,7 +37,12 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
const [selectAnnotator, setSelectAnnotator] = useState<User | undefined>(
|
const [selectAnnotator, setSelectAnnotator] = useState<User | undefined>(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
const [visible, setVisible] = useState<boolean>(false);
|
||||||
|
const [percent, setPercent] = useState<number>(0);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单个序列阅片
|
||||||
|
*/
|
||||||
const onUploadFiles = async (study: Study, series: Series) => {
|
const onUploadFiles = async (study: Study, series: Series) => {
|
||||||
const { SeriesInstanceUID, subs } = series;
|
const { SeriesInstanceUID, subs } = series;
|
||||||
const { StudyInstanceUID } = study;
|
const { StudyInstanceUID } = study;
|
||||||
|
@ -50,20 +54,60 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
if (instances.length === subs.length) {
|
if (instances.length === subs.length) {
|
||||||
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
||||||
} else {
|
} else {
|
||||||
const uploadFunc = (f: File) => () => dicomDomainService.upload2Pacs(f);
|
setVisible(true);
|
||||||
showProgress();
|
|
||||||
limitConcurrency(
|
limitConcurrency(
|
||||||
series.subs.map(uploadFunc),
|
series.subs.map((f: File) => () => dicomDomainService.upload2Pacs(f)),
|
||||||
10,
|
10,
|
||||||
(completed, total) => {
|
(completed, total) => {
|
||||||
console.log(`${completed} out of ${total} tasks completed.`);
|
console.log(`${completed} out of ${total} tasks completed.`);
|
||||||
updatePercent(Math.floor((completed / total) * 100));
|
setPercent(Math.floor((completed / total) * 100));
|
||||||
},
|
},
|
||||||
() => hideProgress()
|
() => {
|
||||||
|
setVisible(false);
|
||||||
|
setPercent(0);
|
||||||
|
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传全部
|
||||||
|
*/
|
||||||
|
const onUploadEntiredDicom = () => {
|
||||||
|
const items = flatternStudies(studys);
|
||||||
|
// 过滤存在pacs中的序列
|
||||||
|
const checkExistPromises = items.map(
|
||||||
|
(item) =>
|
||||||
|
new Promise(async (resolve) => {
|
||||||
|
const instances = await dicomDomainService.existInPacs({
|
||||||
|
SeriesInstanceUID: item.SeriesInstanceUID,
|
||||||
|
StudyInstanceUID: item.StudyInstanceUID,
|
||||||
|
});
|
||||||
|
const existInPacs = instances.length === item.Files.length;
|
||||||
|
resolve(existInPacs ? false : item);
|
||||||
|
})
|
||||||
|
) as Promise<FlatStudyItem | false>[];
|
||||||
|
// 上传到pacs
|
||||||
|
Promise.all(checkExistPromises).then((result) => {
|
||||||
|
const files = result.map((i) => (!i ? [] : i.Files)).flat();
|
||||||
|
if (files.length === 0) return messageApi.info("全部影像均已存在pacs中");
|
||||||
|
setVisible(true);
|
||||||
|
limitConcurrency(
|
||||||
|
files.map((f: File) => () => dicomDomainService.upload2Pacs(f)),
|
||||||
|
10,
|
||||||
|
(completed, total) => {
|
||||||
|
console.log(`${completed} out of ${total} tasks completed.`);
|
||||||
|
setPercent(Math.floor((completed / total) * 100));
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
setVisible(false);
|
||||||
|
setPercent(0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onClickAssign = () => {
|
const onClickAssign = () => {
|
||||||
userDomainService.getDmpAnnotators().then((res) => {
|
userDomainService.getDmpAnnotators().then((res) => {
|
||||||
setIsModalOpen(true);
|
setIsModalOpen(true);
|
||||||
|
@ -71,7 +115,11 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const dcmFileInfo = !!totalFileNum && (
|
/**
|
||||||
|
* 统计信息
|
||||||
|
*/
|
||||||
|
const DicomFileInfo = () =>
|
||||||
|
!!totalFileNum && (
|
||||||
<Space>
|
<Space>
|
||||||
<Text type="secondary">扫描总文件数: {totalFileNum}</Text>
|
<Text type="secondary">扫描总文件数: {totalFileNum}</Text>
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
|
@ -98,6 +146,15 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
<Space>
|
<Space>
|
||||||
<UploadInput />
|
<UploadInput />
|
||||||
<Button
|
<Button
|
||||||
|
icon={<CloudUploadOutlined />}
|
||||||
|
disabled={Number(dcmFileNum) === 0}
|
||||||
|
type="primary"
|
||||||
|
onClick={onUploadEntiredDicom}
|
||||||
|
>
|
||||||
|
上传全部
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
icon={<InboxOutlined />}
|
||||||
disabled={selectRows.length === 0}
|
disabled={selectRows.length === 0}
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={onClickAssign}
|
onClick={onClickAssign}
|
||||||
|
@ -107,7 +164,7 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
</Space>
|
</Space>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
{dcmFileInfo}
|
<DicomFileInfo />
|
||||||
<DicomTable
|
<DicomTable
|
||||||
studys={studys}
|
studys={studys}
|
||||||
loading={isLoading}
|
loading={isLoading}
|
||||||
|
@ -137,7 +194,10 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</Modal>
|
</Modal>
|
||||||
<ProgressModal />
|
<Modal title="上传影像中" open={visible} footer={null} closable={false}>
|
||||||
|
<Progress percent={percent} strokeLinecap="butt" />
|
||||||
|
</Modal>
|
||||||
|
{contextHolder}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -15,15 +15,7 @@ export async function limitConcurrency(
|
||||||
const index = currentIndex;
|
const index = currentIndex;
|
||||||
currentIndex += 1;
|
currentIndex += 1;
|
||||||
|
|
||||||
if (index >= tasks.length) {
|
if (index >= tasks.length) return;
|
||||||
if (results.length === tasks.length) {
|
|
||||||
resolve(results);
|
|
||||||
if (onComplete) {
|
|
||||||
onComplete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const task = tasks[index];
|
const task = tasks[index];
|
||||||
|
|
||||||
|
@ -34,6 +26,13 @@ export async function limitConcurrency(
|
||||||
if (onProgress) {
|
if (onProgress) {
|
||||||
onProgress(completedTasks, tasks.length);
|
onProgress(completedTasks, tasks.length);
|
||||||
}
|
}
|
||||||
|
// 当所有任务都完成时触发
|
||||||
|
if (completedTasks === tasks.length) {
|
||||||
|
resolve(results);
|
||||||
|
if (onComplete) {
|
||||||
|
onComplete();
|
||||||
|
}
|
||||||
|
}
|
||||||
executeTask();
|
executeTask();
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
import { useState, useCallback } from "react";
|
|
||||||
import { Modal, Progress } from "antd";
|
|
||||||
|
|
||||||
export const useProgress = () => {
|
|
||||||
const [visible, setVisible] = useState(false);
|
|
||||||
const [percent, setPercent] = useState(0);
|
|
||||||
|
|
||||||
const showProgress = useCallback(() => {
|
|
||||||
setVisible(true);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const hideProgress = useCallback(() => {
|
|
||||||
setVisible(false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const updatePercent = useCallback((newPercent: number) => {
|
|
||||||
setPercent(newPercent);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const ProgressModal = () => (
|
|
||||||
<Modal open={visible} footer={null} closable={false}>
|
|
||||||
<Progress percent={percent} />
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
ProgressModal,
|
|
||||||
showProgress,
|
|
||||||
hideProgress,
|
|
||||||
updatePercent,
|
|
||||||
};
|
|
||||||
};
|
|
27
apps/dmp/src/modules/Admin/Dicom/Upload/util.ts
Normal file
27
apps/dmp/src/modules/Admin/Dicom/Upload/util.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { Study } from "./DicomUploader/util";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新窗口打开ohif阅片
|
||||||
|
*/
|
||||||
|
export const openOHIFViewer = (
|
||||||
|
StudyInstanceUID: string,
|
||||||
|
SeriesInstanceUID: string
|
||||||
|
) => {
|
||||||
|
const target = `http://localhost:3000/viewer/${StudyInstanceUID}?SeriesInstanceUID=${SeriesInstanceUID}`;
|
||||||
|
window.open(target, "_blank");
|
||||||
|
};
|
||||||
|
|
||||||
|
export type FlatStudyItem = {
|
||||||
|
StudyInstanceUID: string;
|
||||||
|
SeriesInstanceUID: string;
|
||||||
|
Files: File[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const flatternStudies = (studies: Study[]): FlatStudyItem[] =>
|
||||||
|
studies.flatMap((study) =>
|
||||||
|
study.subs.map((series) => ({
|
||||||
|
StudyInstanceUID: study.StudyInstanceUID,
|
||||||
|
SeriesInstanceUID: series.SeriesInstanceUID,
|
||||||
|
Files: series.subs,
|
||||||
|
}))
|
||||||
|
);
|
|
@ -1,18 +1,11 @@
|
||||||
|
import { Route, RouteObject, Routes, useLocation } from "react-router-dom";
|
||||||
|
import { ROLE_NAME, defaultDocumentTitle } from "@/constant";
|
||||||
|
import { pathToRegexp } from "path-to-regexp";
|
||||||
import { useDomain } from "@/hook/useDomain";
|
import { useDomain } from "@/hook/useDomain";
|
||||||
import {
|
import { observer } from "mobx-react-lite";
|
||||||
BrowserRouter,
|
|
||||||
Route,
|
|
||||||
RouteObject,
|
|
||||||
Routes,
|
|
||||||
useLocation,
|
|
||||||
useRoutes,
|
|
||||||
} from "react-router-dom";
|
|
||||||
import { baseRoutes } from "./baseRoutes";
|
import { baseRoutes } from "./baseRoutes";
|
||||||
import { roleRoutes } from "./roleRoutes";
|
import { roleRoutes } from "./roleRoutes";
|
||||||
import { RouteGuard } from "./Guard";
|
import { RouteGuard } from "./Guard";
|
||||||
import { observer } from "mobx-react-lite";
|
|
||||||
import { ROLE_NAME, defaultDocumentTitle } from "@/constant";
|
|
||||||
import { pathToRegexp } from "path-to-regexp";
|
|
||||||
|
|
||||||
export type ExpandRouteProps = {
|
export type ExpandRouteProps = {
|
||||||
title?: string;
|
title?: string;
|
||||||
|
@ -23,14 +16,19 @@ export const RouterElements = observer(() => {
|
||||||
const { user } = userDomainService;
|
const { user } = userDomainService;
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const roleNames = user.roles?.map(
|
/**
|
||||||
(r: { name: string }) => r.name
|
* 用户拥有的角色名
|
||||||
) as ROLE_NAME[];
|
*/
|
||||||
|
const userRoleNames = user.getRolesNames() as ROLE_NAME[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据角色生成路由
|
* 根据角色生成路由
|
||||||
*/
|
*/
|
||||||
const currentRoutes = roleNames?.map((name) => roleRoutes[name]).flat() ?? [];
|
const currentRoutes =
|
||||||
|
userRoleNames
|
||||||
|
?.map((n) => roleRoutes[n])
|
||||||
|
.filter(Boolean)
|
||||||
|
.flat() ?? [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* document.title
|
* document.title
|
||||||
|
|
|
@ -1,27 +1,7 @@
|
||||||
import { Controller } from '@nestjs/common';
|
import { Controller } from '@nestjs/common';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { EventPattern } from '@nestjs/microservices';
|
|
||||||
import * as dayjs from 'dayjs';
|
|
||||||
import { SymmetricCrypto } from '@tavi/util';
|
|
||||||
|
|
||||||
interface UserSignLoggerDto {
|
|
||||||
platform: string;
|
|
||||||
username: string;
|
|
||||||
finger: string;
|
|
||||||
finger2: string;
|
|
||||||
isLegal: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
constructor(private readonly appService: AppService) {}
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
@EventPattern({ cmd: 'logger.user.signIn' })
|
|
||||||
async userSignIn(payload: UserSignLoggerDto) {
|
|
||||||
const dateTime = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
||||||
const { finger2, ...rest } = payload;
|
|
||||||
const browserInfo = new SymmetricCrypto().decrypt(finger2);
|
|
||||||
console.log({ ...rest, dateTime, browserInfo });
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { AppService } from './app.service';
|
||||||
import { NacosModule } from './nacos/nacos.module';
|
import { NacosModule } from './nacos/nacos.module';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { RetrievalModule } from './retrieval/retrieval.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -12,6 +13,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
envFilePath: `.env.${process.env.NODE_ENV}`,
|
envFilePath: `.env.${process.env.NODE_ENV}`,
|
||||||
}),
|
}),
|
||||||
NacosModule,
|
NacosModule,
|
||||||
|
RetrievalModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService {}
|
export class AppService {}
|
||||||
|
|
0
apps/services/dicom/src/retrieval/entity/patient.ts
Normal file
0
apps/services/dicom/src/retrieval/entity/patient.ts
Normal file
0
apps/services/dicom/src/retrieval/entity/series.ts
Normal file
0
apps/services/dicom/src/retrieval/entity/series.ts
Normal file
0
apps/services/dicom/src/retrieval/entity/study.ts
Normal file
0
apps/services/dicom/src/retrieval/entity/study.ts
Normal file
28
apps/services/dicom/src/retrieval/entity/task.ts
Normal file
28
apps/services/dicom/src/retrieval/entity/task.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Task {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
annotatorId: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
StudyInstanceUID;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
SeriesInstanceUID: string;
|
||||||
|
|
||||||
|
@CreateDateColumn({ type: 'timestamp' })
|
||||||
|
createTime: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn({ type: 'timestamp' })
|
||||||
|
updateTime: Date;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { Controller } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Controller('retrieval')
|
||||||
|
export class RetrievalController {}
|
24
apps/services/dicom/src/retrieval/retrieval.module.ts
Normal file
24
apps/services/dicom/src/retrieval/retrieval.module.ts
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { RetrievalController } from './retrieval.controller';
|
||||||
|
import { RetrievalService } from './retrieval.service';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forRoot({
|
||||||
|
type: 'mysql',
|
||||||
|
host: 'localhost',
|
||||||
|
port: 3306,
|
||||||
|
username: 'root',
|
||||||
|
password: 'root',
|
||||||
|
database: 'dicom',
|
||||||
|
entities: [__dirname + '/**/*.entity{.ts,.js}'],
|
||||||
|
synchronize: true,
|
||||||
|
timezone: 'Asia/Shanghai', // 这里设置了时区
|
||||||
|
}),
|
||||||
|
TypeOrmModule.forFeature([]),
|
||||||
|
],
|
||||||
|
controllers: [RetrievalController],
|
||||||
|
providers: [RetrievalService],
|
||||||
|
})
|
||||||
|
export class RetrievalModule {}
|
4
apps/services/dicom/src/retrieval/retrieval.service.ts
Normal file
4
apps/services/dicom/src/retrieval/retrieval.service.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RetrievalService {}
|
|
@ -1,4 +1,4 @@
|
||||||
import { Controller, Get, Inject } from '@nestjs/common';
|
import { Controller, Get, Inject, Post } from '@nestjs/common';
|
||||||
import { ClientProxy } from '@nestjs/microservices';
|
import { ClientProxy } from '@nestjs/microservices';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
|
@ -13,4 +13,12 @@ export class UserController {
|
||||||
);
|
);
|
||||||
return { data, code: 0 };
|
return { data, code: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post("assign")
|
||||||
|
async assign(){
|
||||||
|
const { data } = await firstValueFrom(
|
||||||
|
this.client.send({ cmd: 'dicom.user.find.annotator' }, {}),
|
||||||
|
);
|
||||||
|
return { data, code: 0 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user