feat: 上传全部影像
This commit is contained in:
parent
43eb5e70af
commit
976d687b1b
|
@ -1,4 +1,4 @@
|
|||
import { Apis } from "@@/infra/api";
|
||||
import { Apis, ArchiveTaskCreateDto } from "@@/infra/api";
|
||||
import { User } from "./entities/User";
|
||||
|
||||
export class UserRepository {
|
||||
|
@ -20,4 +20,8 @@ export class UserRepository {
|
|||
async getDmpAnnotators() {
|
||||
return await Apis.getDmpAnnotators();
|
||||
}
|
||||
|
||||
async createArchiveTask(p: ArchiveTaskCreateDto) {
|
||||
return await Apis.createArchiveTask(p);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,8 @@ export class UserService {
|
|||
/**
|
||||
* 分配标注序列
|
||||
*/
|
||||
async assignLabelDicom(user: User, study: Study[]) {
|
||||
async createArchiveTask(user: User, study: Study[]) {
|
||||
console.log(user, study);
|
||||
return await this.userRepository.createArchiveTask({ user, study });
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { User } from "@@/domain/User/entities/User";
|
||||
import { Request } from "./Request";
|
||||
import { Study } from "@/modules/Admin/Dicom/Upload/DicomUploader/util";
|
||||
|
||||
const PREFIX = "/api/dmp";
|
||||
const PREFIX_CERT = "/cert";
|
||||
|
@ -25,6 +26,11 @@ export type ExistInPacsDTO = {
|
|||
SeriesInstanceUID: string;
|
||||
};
|
||||
|
||||
export type ArchiveTaskCreateDto = {
|
||||
user: User;
|
||||
study: Study[];
|
||||
};
|
||||
|
||||
export const Apis = {
|
||||
/**
|
||||
* 用户登录
|
||||
|
@ -59,5 +65,11 @@ export const Apis = {
|
|||
* 获取标注人员信息
|
||||
*/
|
||||
getDmpAnnotators: (): ResponseType =>
|
||||
Request.get(PREFIX + `/user/find/annotator`),
|
||||
Request.get(PREFIX + `/admin/find/annotator`),
|
||||
|
||||
/**
|
||||
* 创建标注任务
|
||||
*/
|
||||
createArchiveTask: (p: ArchiveTaskCreateDto): ResponseType =>
|
||||
Request.post(PREFIX + "/admin/createArchiveTask", p),
|
||||
};
|
||||
|
|
|
@ -12,14 +12,11 @@ export const columnsForStudy: TableColumnsType<Study> = [
|
|||
{
|
||||
title: "切片数量",
|
||||
key: "seriesNumber",
|
||||
render: (_: any, record: Study) => {
|
||||
console.log(record);
|
||||
return (
|
||||
<span>
|
||||
{record.subs.map((s) => s.subs.length).reduce((p, n) => p + n)}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
render: (_: any, record: Study) => (
|
||||
<span>
|
||||
{record.subs.map((s) => s.subs.length).reduce((p, n) => p + n)}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
{ title: "年龄", dataIndex: "patientAge", key: "patientAge" },
|
||||
{ title: "性别", dataIndex: "patientSex", key: "patientSex" },
|
||||
|
|
|
@ -42,7 +42,6 @@ export const useDicomUploader = () => {
|
|||
//TODO: 耗时
|
||||
setIsLoading(true);
|
||||
const studys = await parseDcmFiles(dcmFiles);
|
||||
console.log("studys", studys);
|
||||
setStudys(studys);
|
||||
setIsLoading(false);
|
||||
};
|
||||
|
|
|
@ -67,9 +67,9 @@ export const parseDcmFiles = async (dcmFiles: File[]): Promise<Study[]> => {
|
|||
};
|
||||
studys.push(study);
|
||||
}
|
||||
// 序列级别
|
||||
// 序列级别,可能会存在某个序列被复制一份的情况,也会计算在切片数量中
|
||||
let series = study.subs.find(
|
||||
(s) => s.SeriesInstanceUID === SeriesInstanceUID && s.
|
||||
(s) => s.SeriesInstanceUID === SeriesInstanceUID
|
||||
);
|
||||
if (!series) {
|
||||
series = {
|
||||
|
|
|
@ -134,7 +134,7 @@ export const DicomUpload = (props: DicomUploadProps) => {
|
|||
*/
|
||||
const onAssignConfirm = () => {
|
||||
if (!selectAnnotator?.id) return;
|
||||
userDomainService.assignLabelDicom(selectAnnotator, selectRows);
|
||||
userDomainService.createArchiveTask(selectAnnotator, selectRows);
|
||||
setSelectAnnotator(undefined);
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
|
33
apps/services/dicom/src/retrieval/entity/archiveTask.ts
Normal file
33
apps/services/dicom/src/retrieval/entity/archiveTask.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { Study } from './study';
|
||||
|
||||
@Entity()
|
||||
export class ArchiveTask {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
annotatorId: number;
|
||||
|
||||
@Column()
|
||||
StudyInstanceUID;
|
||||
|
||||
@Column()
|
||||
SeriesInstanceUID: string;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamp' })
|
||||
createTime: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamp' })
|
||||
updateTime: Date;
|
||||
|
||||
@OneToMany(() => Study, (study) => study.archiveTask)
|
||||
studies: Study[];
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { Study } from './study';
|
||||
|
||||
@Entity()
|
||||
export class Series {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
SeriesInstanceUID: string;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamp' })
|
||||
createTime: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamp' })
|
||||
updateTime: Date;
|
||||
|
||||
@ManyToOne(() => Study, (study) => study.series)
|
||||
@JoinColumn({
|
||||
name: 'StudyInstanceUID',
|
||||
referencedColumnName: 'StudyInstanceUID',
|
||||
}) // 自定义连接列
|
||||
study: Study;
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { Series } from './series';
|
||||
import { ArchiveTask } from './archiveTask';
|
||||
|
||||
@Entity()
|
||||
export class Study {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
StudyInstanceUID: string;
|
||||
|
||||
@Column()
|
||||
PatientID: string | number;
|
||||
|
||||
@Column()
|
||||
PatientName: string;
|
||||
|
||||
@Column()
|
||||
PatientSex: string;
|
||||
|
||||
@Column()
|
||||
PatientAge: string;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamp' })
|
||||
createTime: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamp' })
|
||||
updateTime: Date;
|
||||
|
||||
@OneToMany(() => Series, (series) => series.study)
|
||||
series: Series[];
|
||||
|
||||
@ManyToOne(() => ArchiveTask, (archiveTask) => archiveTask.studies)
|
||||
@JoinColumn({ name: 'ArchiveTaskId', referencedColumnName: 'id' }) // 自定义连接列
|
||||
archiveTask: ArchiveTask;
|
||||
}
|
|
@ -1,4 +1,17 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { EventPattern } from '@nestjs/microservices';
|
||||
import { RetrievalService } from './retrieval.service';
|
||||
|
||||
@Controller('retrieval')
|
||||
export class RetrievalController {}
|
||||
@Controller()
|
||||
export class RetrievalController {
|
||||
constructor(private readonly retrievalSerivce: RetrievalService) {}
|
||||
@EventPattern({ cmd: 'dicom.retrieval.archivetask.create' })
|
||||
async createArchiveTask(payload) {
|
||||
const { user, study } = payload;
|
||||
const { id: annotatorId } = user;
|
||||
return await this.retrievalSerivce.createArchiveTask({
|
||||
annotatorId,
|
||||
study,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ import { Module } from '@nestjs/common';
|
|||
import { RetrievalController } from './retrieval.controller';
|
||||
import { RetrievalService } from './retrieval.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ArchiveTask } from './entity/archiveTask';
|
||||
import { Series } from './entity/series';
|
||||
import { Study } from './entity/study';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -16,7 +19,7 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||
synchronize: true,
|
||||
timezone: 'Asia/Shanghai', // 这里设置了时区
|
||||
}),
|
||||
TypeOrmModule.forFeature([]),
|
||||
TypeOrmModule.forFeature([ArchiveTask, Series, Study]),
|
||||
],
|
||||
controllers: [RetrievalController],
|
||||
providers: [RetrievalService],
|
||||
|
|
|
@ -1,4 +1,16 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { ArchiveTask } from './entity/archiveTask';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class RetrievalService {}
|
||||
export class RetrievalService {
|
||||
constructor(
|
||||
@InjectRepository(ArchiveTask)
|
||||
private readonly archiveTaskRepository: Repository<ArchiveTask>,
|
||||
) {}
|
||||
|
||||
async createArchiveTask({ annotatorId, study }) {
|
||||
return this.archiveTaskRepository.save({ annotatorId });
|
||||
}
|
||||
}
|
||||
|
|
8
apps/services/dmp/archive/.env.dev
Normal file
8
apps/services/dmp/archive/.env.dev
Normal file
|
@ -0,0 +1,8 @@
|
|||
PORT=31541
|
||||
# nacos中注册服务的名称
|
||||
NACOS_SERVICE_NAME=archive
|
||||
NACOS_ADDR=127.0.0.1:8848
|
||||
NACOS_NAMESPACE=56a3b295-f319-4ced-82b5-0df2e98cc541
|
||||
# nacos配置中心
|
||||
NACOS_DATAID='test'
|
||||
NACOS_GROUP='DEFAULT_GROUP'
|
25
apps/services/dmp/archive/.eslintrc.js
Normal file
25
apps/services/dmp/archive/.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/dmp/archive/.gitignore
vendored
Normal file
35
apps/services/dmp/archive/.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/dmp/archive/.prettierrc
Normal file
4
apps/services/dmp/archive/.prettierrc
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
73
apps/services/dmp/archive/README.md
Normal file
73
apps/services/dmp/archive/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/dmp/archive/nest-cli.json
Normal file
8
apps/services/dmp/archive/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/dmp/archive/package.json
Normal file
56
apps/services/dmp/archive/package.json
Normal file
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"name": "@tavi/dmp-archive",
|
||||
"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"
|
||||
}
|
||||
}
|
15
apps/services/dmp/archive/src/app.controller.ts
Normal file
15
apps/services/dmp/archive/src/app.controller.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { AppService } from './app.service';
|
||||
import { EventPattern } from '@nestjs/microservices';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
|
||||
@EventPattern({ cmd: 'archive.task.create' })
|
||||
async createArchiveTask(payload) {
|
||||
const { annotatorId, study } = payload;
|
||||
console.log(study);
|
||||
return 123;
|
||||
}
|
||||
}
|
33
apps/services/dmp/archive/src/app.module.ts
Normal file
33
apps/services/dmp/archive/src/app.module.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
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';
|
||||
import { ArchiveTask } from './entity/archiveTask';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
envFilePath: `.env.${process.env.NODE_ENV}`,
|
||||
}),
|
||||
NacosModule,
|
||||
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([ArchiveTask]),
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
exports: [],
|
||||
})
|
||||
export class AppModule {}
|
4
apps/services/dmp/archive/src/app.service.ts
Normal file
4
apps/services/dmp/archive/src/app.service.ts
Normal file
|
@ -0,0 +1,4 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AppService {}
|
21
apps/services/dmp/archive/src/app.util.ts
Normal file
21
apps/services/dmp/archive/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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,9 +5,8 @@ import {
|
|||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class Task {
|
||||
export class ArchiveTask {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
19
apps/services/dmp/archive/src/main.ts
Normal file
19
apps/services/dmp/archive/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/dmp/archive/src/nacos/nacos.module.ts
Normal file
8
apps/services/dmp/archive/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/dmp/archive/src/nacos/nacos.service.ts
Normal file
108
apps/services/dmp/archive/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/dmp/archive/tsconfig.build.json
Normal file
4
apps/services/dmp/archive/tsconfig.build.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
21
apps/services/dmp/archive/tsconfig.json
Normal file
21
apps/services/dmp/archive/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,9 +1,9 @@
|
|||
import { Controller, Get, Inject, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Inject, Post } from '@nestjs/common';
|
||||
import { ClientProxy } from '@nestjs/microservices';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
@Controller('user')
|
||||
export class UserController {
|
||||
@Controller('admin')
|
||||
export class AdminController {
|
||||
constructor(@Inject('Client') private readonly client: ClientProxy) {}
|
||||
|
||||
@Get('find/annotator')
|
||||
|
@ -14,10 +14,12 @@ export class UserController {
|
|||
return { data, code: 0 };
|
||||
}
|
||||
|
||||
@Post("assign")
|
||||
async assign(){
|
||||
@Post('createArchiveTask')
|
||||
async createArchiveTask(@Body() body) {
|
||||
const { user, study } = body;
|
||||
const { annotatorId } = user;
|
||||
const { data } = await firstValueFrom(
|
||||
this.client.send({ cmd: 'dicom.user.find.annotator' }, {}),
|
||||
this.client.send({ cmd: 'archive.task.create' }, { annotatorId, study }),
|
||||
);
|
||||
return { data, code: 0 };
|
||||
}
|
0
apps/services/dmp/gateway/src/admin/admin.dto.ts
Normal file
0
apps/services/dmp/gateway/src/admin/admin.dto.ts
Normal file
|
@ -1,5 +1,5 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { UserController } from './user.controller';
|
||||
import { AdminController } from './admin.controller';
|
||||
import { ClientsModule, Transport } from '@nestjs/microservices';
|
||||
|
||||
@Module({
|
||||
|
@ -16,7 +16,7 @@ import { ClientsModule, Transport } from '@nestjs/microservices';
|
|||
},
|
||||
]),
|
||||
],
|
||||
controllers: [UserController],
|
||||
controllers: [AdminController],
|
||||
providers: [],
|
||||
})
|
||||
export class UserModule {}
|
||||
export class AdminModule {}
|
|
@ -7,7 +7,7 @@ import { APP_FILTER, APP_GUARD } from '@nestjs/core';
|
|||
import { AuthGuard } from './guard/auth.guard';
|
||||
import { ForbiddenExceptionFilter } from './filter/forbid.filter';
|
||||
import { AuthController } from './auth/auth.controller';
|
||||
import { UserModule } from './user/user.module';
|
||||
import { AdminModule } from './admin/admin.module';
|
||||
import * as cookieParser from 'cookie-parser';
|
||||
|
||||
@Module({
|
||||
|
@ -27,7 +27,7 @@ import * as cookieParser from 'cookie-parser';
|
|||
},
|
||||
},
|
||||
]),
|
||||
UserModule,
|
||||
AdminModule,
|
||||
],
|
||||
controllers: [AppController, AuthController],
|
||||
providers: [
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
"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",
|
||||
"dev:dmp-archive": "pnpm run --filter @tavi/dmp-archive start:dev",
|
||||
"dev:aorta": "pnpm run --filter @tavi/aorta dev",
|
||||
"dev:aorta-gateway": "pnpm run --filter @tavi/aorta-gateway start:dev",
|
||||
"build:aorta": "pnpm run --filter @tavi/aorta build",
|
||||
|
|
895
pnpm-lock.yaml
895
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user