feat: login
This commit is contained in:
parent
2822f338dc
commit
063e812448
|
@ -10,7 +10,7 @@ export const AuthFailedReplacePath = "/login";
|
|||
export const theme: ThemeConfig = {
|
||||
token: {
|
||||
colorPrimary: "#fa541c",
|
||||
borderRadius: 2,
|
||||
borderRadius: 5,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import React, { useRef, useState } from "react";
|
||||
import { Study, parseDcmFiles } from "./util";
|
||||
import { Button } from "antd";
|
||||
import { CloudUploadOutlined } from "@ant-design/icons";
|
||||
|
||||
declare module "react" {
|
||||
interface InputHTMLAttributes<T> extends HTMLAttributes<T> {
|
||||
|
@ -44,17 +46,28 @@ export const useDicomUploader = () => {
|
|||
setIsLoading(false);
|
||||
};
|
||||
|
||||
const UploadInput = () => (
|
||||
<input
|
||||
type="file"
|
||||
webkitdirectory="true"
|
||||
ref={fileInputRef}
|
||||
mozdirectory="true"
|
||||
multiple
|
||||
placeholder="选择文件"
|
||||
onChange={(e) => handleFileChange(e)}
|
||||
/>
|
||||
);
|
||||
const UploadInput = () => {
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="file"
|
||||
webkitdirectory="true"
|
||||
ref={fileInputRef}
|
||||
mozdirectory="true"
|
||||
multiple
|
||||
style={{ display: "none" }}
|
||||
onChange={(e) => handleFileChange(e)}
|
||||
/>
|
||||
<Button
|
||||
icon={<CloudUploadOutlined />}
|
||||
type="primary"
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
>
|
||||
上传dicom
|
||||
</Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return { UploadInput, fileCalculator, studys, isLoading };
|
||||
};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, Col, Row, Space, Spin } from "antd";
|
||||
import { Button, Col, Divider, Modal, Row, Select, Space, Spin } from "antd";
|
||||
import { useDicomUploader } from "./DicomUploader";
|
||||
import { Series, Study } from "./DicomUploader/util";
|
||||
import { DicomTable } from "./DicomTable";
|
||||
|
@ -12,11 +12,27 @@ interface DicomUploadProps {
|
|||
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) => {
|
||||
const { UploadInput, fileCalculator, studys, isLoading } = useDicomUploader();
|
||||
const { dicomDomainService } = useDomain();
|
||||
const { dicomDomainService, userDomainService } = useDomain();
|
||||
const { dcmFileNum, totalFileNum, dcmFileSize } = fileCalculator;
|
||||
const [selectRows, setSelectedRows] = useState<Study[]>([]);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [annotators, setAnnotators] = useState([]);
|
||||
const [selectAnnotatorId, setSelectAnnotatorId] = useState<string | number>(
|
||||
""
|
||||
);
|
||||
|
||||
const onUploadFiles = async (study: Study, series: Series) => {
|
||||
const { SeriesInstanceUID, subs } = series;
|
||||
|
@ -25,10 +41,9 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
|||
SeriesInstanceUID,
|
||||
StudyInstanceUID,
|
||||
});
|
||||
const dcmExistInPacs = instances.length === subs.length;
|
||||
if (dcmExistInPacs) {
|
||||
const target = `http://localhost:3000/viewer/${StudyInstanceUID}?SeriesInstanceUID=${SeriesInstanceUID}`;
|
||||
window.open(target, "_blank");
|
||||
// pacs已存在
|
||||
if (instances.length === subs.length) {
|
||||
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
||||
} else {
|
||||
let fullfilled = 0;
|
||||
Promise.all(
|
||||
|
@ -41,49 +56,79 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
|||
)
|
||||
).then((res) => {
|
||||
if (res.length === series.subs.length) {
|
||||
const target = `http://localhost:3000/viewer/${StudyInstanceUID}?SeriesInstanceUID=${SeriesInstanceUID}`;
|
||||
window.open(target, "_blank");
|
||||
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onClickAssign = () => {
|
||||
userDomainService.getDmpAnnotators().then((res) => {
|
||||
console.log(res);
|
||||
const { data } = res;
|
||||
setIsModalOpen(true);
|
||||
setAnnotators(data.map((u) => ({ label: u.username, value: u.id })));
|
||||
});
|
||||
console.log(selectRows);
|
||||
};
|
||||
|
||||
const dcmFileInfo = !!totalFileNum && (
|
||||
<Space>
|
||||
<Text type="secondary">扫描总文件数: {totalFileNum}</Text>
|
||||
<Divider type="vertical" />
|
||||
<Text type="secondary">包含: {dcmFileNum} 个dicom文件</Text>
|
||||
<Divider type="vertical" />
|
||||
<Text type="secondary">dcm文件总体积: {dcmFileSize} </Text>
|
||||
</Space>
|
||||
);
|
||||
|
||||
const onAssignConfirm = () => {
|
||||
if (!selectAnnotatorId) return;
|
||||
console.log(selectAnnotatorId);
|
||||
setSelectAnnotatorId(0);
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<header>
|
||||
<Row style={{ padding: "10px 0" }}>
|
||||
<Col span={16}>
|
||||
<div style={{ padding: 20 }}>
|
||||
<Row style={{ paddingBottom: 20 }}>
|
||||
<Col span={24}>
|
||||
<Space>
|
||||
<UploadInput />
|
||||
<Text type="secondary">
|
||||
文件统计, dicom文件: {dcmFileNum}, 总文件: {totalFileNum}, 大小:
|
||||
{dcmFileSize}
|
||||
</Text>
|
||||
</Col>
|
||||
<Col span={8} style={{ textAlign: "right" }}>
|
||||
<Space>
|
||||
<Button type="primary" size="small" onClick={onClickAssign}>
|
||||
分配
|
||||
</Button>
|
||||
<Button type="primary" size="small">
|
||||
添加标签
|
||||
</Button>
|
||||
<Button type="primary" size="small">
|
||||
备注
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
</header>
|
||||
<Button
|
||||
disabled={selectRows.length === 0}
|
||||
type="primary"
|
||||
onClick={onClickAssign}
|
||||
>
|
||||
分配
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
{dcmFileInfo}
|
||||
<DicomTable
|
||||
studys={studys}
|
||||
loading={isLoading}
|
||||
onSelectedRows={(rows) => setSelectedRows(rows)}
|
||||
onUploadFiles={onUploadFiles}
|
||||
/>
|
||||
<Modal
|
||||
width={320}
|
||||
title="选择标注"
|
||||
open={isModalOpen}
|
||||
closable
|
||||
cancelText="再想想"
|
||||
okText="确定"
|
||||
onOk={onAssignConfirm}
|
||||
onCancel={() => setIsModalOpen(false)}
|
||||
>
|
||||
<Select
|
||||
style={{ width: "100%", marginBottom: 20 }}
|
||||
defaultValue={selectAnnotatorId}
|
||||
onChange={(e) => setSelectAnnotatorId(e)}
|
||||
options={annotators}
|
||||
/>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
8
apps/services/dicom/.env.dev
Normal file
8
apps/services/dicom/.env.dev
Normal file
|
@ -0,0 +1,8 @@
|
|||
PORT=31222
|
||||
# nacos中注册服务的名称
|
||||
NACOS_SERVICE_NAME=dicom
|
||||
NACOS_ADDR=127.0.0.1:8848
|
||||
NACOS_NAMESPACE=56a3b295-f319-4ced-82b5-0df2e98cc541
|
||||
# nacos配置中心
|
||||
NACOS_DATAID='test'
|
||||
NACOS_GROUP='DEFAULT_GROUP'
|
0
apps/services/dicom/.env.prod
Normal file
0
apps/services/dicom/.env.prod
Normal file
25
apps/services/dicom/.eslintrc.js
Normal file
25
apps/services/dicom/.eslintrc.js
Normal file
|
@ -0,0 +1,25 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
35
apps/services/dicom/.gitignore
vendored
Normal file
35
apps/services/dicom/.gitignore
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
# compiled output
|
||||
/dist
|
||||
/node_modules
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
pnpm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Tests
|
||||
/coverage
|
||||
/.nyc_output
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
4
apps/services/dicom/.prettierrc
Normal file
4
apps/services/dicom/.prettierrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
73
apps/services/dicom/README.md
Normal file
73
apps/services/dicom/README.md
Normal file
|
@ -0,0 +1,73 @@
|
|||
<p align="center">
|
||||
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||
|
||||
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||
</p>
|
||||
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
|
||||
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
|
||||
|
||||
## Description
|
||||
|
||||
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ pnpm install
|
||||
```
|
||||
|
||||
## Running the app
|
||||
|
||||
```bash
|
||||
# development
|
||||
$ pnpm run start
|
||||
|
||||
# watch mode
|
||||
$ pnpm run start:dev
|
||||
|
||||
# production mode
|
||||
$ pnpm run start:prod
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
# unit tests
|
||||
$ pnpm run test
|
||||
|
||||
# e2e tests
|
||||
$ pnpm run test:e2e
|
||||
|
||||
# test coverage
|
||||
$ pnpm run test:cov
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
Nest is [MIT licensed](LICENSE).
|
8
apps/services/dicom/nest-cli.json
Normal file
8
apps/services/dicom/nest-cli.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
56
apps/services/dicom/package.json
Normal file
56
apps/services/dicom/package.json
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"name": "@tavi/dicom",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"author": "",
|
||||
"private": true,
|
||||
"license": "UNLICENSED",
|
||||
"scripts": {
|
||||
"build": "nest build",
|
||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"start": "nest start",
|
||||
"start:dev": "cross-env NODE_ENV=dev nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "cross-env NODE_ENV=dev node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "10.1.0",
|
||||
"@nestjs/core": "10.1.0",
|
||||
"@nestjs/platform-express": "10.1.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"@nestjs/config": "3.0.0",
|
||||
"rxjs": "7.8.1",
|
||||
"nats": "2.15.1",
|
||||
"@nestjs/microservices": "10.0.5",
|
||||
"nacos": "2.5.1",
|
||||
"cross-env": "7.0.3",
|
||||
"cookie-parser": "1.4.6",
|
||||
"@casl/ability": "6.5.0",
|
||||
"typeorm": "0.3.17",
|
||||
"@nestjs/typeorm": "10.0.0",
|
||||
"bcrypt": "5.1.0",
|
||||
"minimatch": "9.0.3",
|
||||
"dayjs": "1.11.9",
|
||||
"flatted": "3.2.7",
|
||||
"crypto-js": "4.1.1",
|
||||
"@tavi/util": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nestjs/cli": "^9.0.0",
|
||||
"@nestjs/schematics": "^9.0.0",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/node": "18.16.12",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"prettier": "^2.3.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"ts-loader": "^9.2.3",
|
||||
"ts-node": "^10.0.0",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "^5.0.0"
|
||||
}
|
||||
}
|
27
apps/services/dicom/src/app.controller.ts
Normal file
27
apps/services/dicom/src/app.controller.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
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()
|
||||
export class AppController {
|
||||
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;
|
||||
}
|
||||
}
|
20
apps/services/dicom/src/app.module.ts
Normal file
20
apps/services/dicom/src/app.module.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { NacosModule } from './nacos/nacos.module';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: `.env.${process.env.NODE_ENV}`,
|
||||
}),
|
||||
NacosModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
exports: [],
|
||||
})
|
||||
export class AppModule {}
|
6
apps/services/dicom/src/app.service.ts
Normal file
6
apps/services/dicom/src/app.service.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {}
|
21
apps/services/dicom/src/app.util.ts
Normal file
21
apps/services/dicom/src/app.util.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as CryptoJS from 'crypto-js';
|
||||
import { parse } from 'flatted';
|
||||
|
||||
export class SymmetricCrypto {
|
||||
private key: string;
|
||||
|
||||
constructor(key: string) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
decrypt(encryptedData: string): object | null {
|
||||
try {
|
||||
const bytes = CryptoJS.AES.decrypt(encryptedData, this.key);
|
||||
const decryptedData = bytes.toString(CryptoJS.enc.Utf8);
|
||||
return parse(decryptedData);
|
||||
} catch (error) {
|
||||
console.error('Decryption failed:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
19
apps/services/dicom/src/main.ts
Normal file
19
apps/services/dicom/src/main.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.createMicroservice<MicroserviceOptions>(
|
||||
AppModule,
|
||||
{
|
||||
transport: Transport.NATS,
|
||||
options: {
|
||||
servers: ['nats://localhost:4222'], // 可以指定链接到多个nats的消息队列
|
||||
maxReconnectAttempts: 5,
|
||||
reconnectTimeWait: 1000,
|
||||
},
|
||||
},
|
||||
);
|
||||
await app.listen();
|
||||
}
|
||||
bootstrap();
|
8
apps/services/dicom/src/nacos/nacos.module.ts
Normal file
8
apps/services/dicom/src/nacos/nacos.module.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { NacosService } from './nacos.service';
|
||||
|
||||
@Module({
|
||||
providers: [NacosService],
|
||||
exports: [NacosService],
|
||||
})
|
||||
export class NacosModule {}
|
108
apps/services/dicom/src/nacos/nacos.service.ts
Normal file
108
apps/services/dicom/src/nacos/nacos.service.ts
Normal file
|
@ -0,0 +1,108 @@
|
|||
// nacos.service.ts
|
||||
import {
|
||||
Injectable,
|
||||
OnApplicationBootstrap,
|
||||
OnApplicationShutdown,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { NacosConfigClient, NacosNamingClient } from 'nacos'; // ts
|
||||
import * as os from 'os';
|
||||
|
||||
@Injectable()
|
||||
export class NacosService
|
||||
implements OnApplicationBootstrap, OnApplicationShutdown
|
||||
{
|
||||
private nacosNamingClient: NacosNamingClient;
|
||||
private nacosConfigClient: NacosConfigClient;
|
||||
serviceName: string;
|
||||
instance: { ip: string; port: number };
|
||||
group: string;
|
||||
dataId: string;
|
||||
constructor(private configService: ConfigService) {
|
||||
this.nacosNamingClient = new NacosNamingClient({
|
||||
logger: console,
|
||||
serverList: configService.get('NACOS_ADDR'),
|
||||
namespace: configService.get('NACOS_NAMESPACE'),
|
||||
});
|
||||
this.nacosConfigClient = new NacosConfigClient({
|
||||
namespace: configService.get('NACOS_NAMESPACE'),
|
||||
serverAddr: configService.get('NACOS_ADDR'),
|
||||
});
|
||||
this.serviceName = configService.get('NACOS_SERVICE_NAME');
|
||||
this.dataId = configService.get('NACOS_DATAID');
|
||||
this.group = configService.get('NACOS_GROUP');
|
||||
this.instance = {
|
||||
ip: this.getServerIP(),
|
||||
port: configService.get('PORT'),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* nestjs应用被关闭前
|
||||
* @param {string} signal 'SIGTERM' | 'SIGINT' | 'SIGHUP' | 'SIGBREAK'
|
||||
*/
|
||||
onApplicationShutdown(signal?: string) {
|
||||
if (signal) {
|
||||
const { serviceName, instance, group } = this;
|
||||
this.nacosNamingClient.deregisterInstance(serviceName, instance, group);
|
||||
this.nacosConfigClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用完全启动&微服务也被成功启动
|
||||
*/
|
||||
onApplicationBootstrap() {
|
||||
const { serviceName, instance } = this;
|
||||
this.nacosNamingClient.registerInstance(serviceName, instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* 先于 onApplicationBootstrap
|
||||
*/
|
||||
async onModuleInit() {
|
||||
this.nacosNamingClient.ready();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从nacos获取最新的配置信息
|
||||
*/
|
||||
async getConfig() {
|
||||
const { dataId, group } = this;
|
||||
const configFromNacos = await this.nacosConfigClient.getConfig(
|
||||
dataId,
|
||||
group,
|
||||
);
|
||||
return configFromNacos;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订阅配置中心,当远程修改nacos的配置时,触发
|
||||
*/
|
||||
async subscribeConfiguration() {
|
||||
const { dataId, group } = this;
|
||||
this.nacosConfigClient.subscribe(
|
||||
{
|
||||
dataId,
|
||||
group,
|
||||
},
|
||||
(content) => console.log('content', content),
|
||||
);
|
||||
}
|
||||
|
||||
getServerIP(): string {
|
||||
const networkInterfaces = os.networkInterfaces();
|
||||
for (const name of Object.keys(networkInterfaces)) {
|
||||
for (const iface of networkInterfaces[name]) {
|
||||
// 跳过IPv6和内部地址
|
||||
if ('IPv4' !== iface.family || iface.internal !== false) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 返回第一个找到的IPv4地址
|
||||
return iface.address;
|
||||
}
|
||||
}
|
||||
return 'localhost'; // 如果找不到外部IPv4地址,返回localhost
|
||||
}
|
||||
}
|
4
apps/services/dicom/tsconfig.build.json
Normal file
4
apps/services/dicom/tsconfig.build.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
21
apps/services/dicom/tsconfig.json
Normal file
21
apps/services/dicom/tsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
}
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
version: '3.1'
|
||||
version: "3.1"
|
||||
services:
|
||||
|
||||
nats:
|
||||
image: nats
|
||||
container_name: tavi-nats
|
||||
|
@ -46,14 +45,13 @@ services:
|
|||
ports:
|
||||
- "5432:5432"
|
||||
volumes:
|
||||
# postgrel->data 挂载出来
|
||||
# postgrel->data 挂载出来,如果出现docker报错清空 page_data/data以及/orthanc_db文件夹
|
||||
- ./orthancOHIF/pg_data/data:/var/lib/postgresql/data
|
||||
- ./orthancOHIF/orthanc.sql:/docker-entrypoint-initdb.d/orthanc.sql
|
||||
environment:
|
||||
- PGDATA=/var/lib/postgresql/data
|
||||
- POSTGRES_DB=tavi-orthanc
|
||||
- POSTGRES_USER=orthanc
|
||||
- POSTGRES_PASSWORD=orthanc
|
||||
- TZ=Asia/Shanghai
|
||||
- POSTGRES_DB=postgres
|
||||
|
||||
pacs:
|
||||
image: osimis/orthanc:20.11.2
|
||||
|
|
1
orthancOHIF/orthanc.sql
Normal file
1
orthancOHIF/orthanc.sql
Normal file
|
@ -0,0 +1 @@
|
|||
CREATE DATABASE orthanc;
|
|
@ -4,6 +4,7 @@
|
|||
"scripts": {
|
||||
"dev:all": "chmod 777 ./terminal.sh && ./terminal.sh",
|
||||
"mock": "pnpm run --filter @tavi/mock mock",
|
||||
"dev:dicom": "pnpm run --filter @tavi/dicom start:dev",
|
||||
"dev:logger": "pnpm run --filter @tavi/logger start:dev",
|
||||
"dev:dmp-web": "pnpm run --filter @tavi/dmp-web dev",
|
||||
"dev:dmp-gateway": "pnpm run --filter @tavi/dmp-gateway start:dev",
|
||||
|
|
9788
pnpm-lock.yaml
9788
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
23
structures.md
Normal file
23
structures.md
Normal file
|
@ -0,0 +1,23 @@
|
|||
# dicom 微服务
|
||||
|
||||
## 脱敏
|
||||
|
||||
针对PaitentName、扫描日期
|
||||
|
||||
假脱敏:
|
||||
|
||||
真脱敏:针对下载影像,请求pacs,洗掉patient信息,压缩zip,流
|
||||
|
||||
## 上传影像
|
||||
|
||||
关联业务网关:
|
||||
- dmp-gateway:
|
||||
- 管理员上传dicom:前端吐pacs,数据表记录病人信息,脱敏病人姓名
|
||||
- 标注上传标注文件:……
|
||||
- aorta等业务系统
|
||||
- 用户上传dicom:前端吐pacs,过dicom微服务,假脱敏
|
||||
|
||||
## 下载影像
|
||||
|
||||
- dmp-gateway: 标注、算法下载dicom,脱敏
|
||||
|
Loading…
Reference in New Issue
Block a user