Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
I
init-things
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
nanahira
init-things
Commits
2d216a78
Commit
2d216a78
authored
Mar 08, 2022
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
put new things
parent
f90fe6e3
Pipeline
#10495
passed with stages
in 1 minute and 18 seconds
Changes
2
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
150 additions
and
33 deletions
+150
-33
things/nest-typeorm/src/crud-base/crud-base.ts
things/nest-typeorm/src/crud-base/crud-base.ts
+132
-32
things/nest-typeorm/src/entities/bases/TimeBase.entity.ts
things/nest-typeorm/src/entities/bases/TimeBase.entity.ts
+18
-1
No files found.
things/nest-typeorm/src/crud-base/crud-base.ts
View file @
2d216a78
import
{
ConsoleLogger
}
from
'
@nestjs/common
'
;
import
{
ClassConstructor
,
plainToClass
}
from
'
class-transformer
'
;
import
{
ClassConstructor
}
from
'
class-transformer
'
;
import
{
DeleteResult
,
FindConditions
,
...
...
@@ -7,7 +7,6 @@ import {
Repository
,
SelectQueryBuilder
,
UpdateResult
,
DeepPartial
,
}
from
'
typeorm
'
;
import
{
BlankReturnMessageDto
,
...
...
@@ -16,23 +15,36 @@ import {
}
from
'
../dto/ReturnMessage.dto
'
;
import
{
QueryWise
}
from
'
../entities/interfaces/QueryWise
'
;
import
{
camelCase
}
from
'
typeorm/util/StringUtils
'
;
import
{
DeletionWise
}
from
'
../entities/bases/TimeBase.entity
'
;
import
{
DeletionWise
,
ImportWise
}
from
'
../entities/bases/TimeBase.entity
'
;
import
{
PageSettingsFactory
}
from
'
../dto/PageSettings.dto
'
;
import
{
generateEntityFromRaw
}
from
'
../utility/generateEntityFromRaw
'
;
import
{
ImportEntry
}
from
'
src/dto/import-entry.dto
'
;
import
_
from
'
lodash
'
;
import
{
CsvParseService
}
from
'
src/csv-parse/csv-parse/csv-parse.service
'
;
export
type
EntityId
<
T
extends
{
id
:
any
}
>
=
T
[
'
id
'
];
export
interface
RelationDef
{
name
:
string
;
inner
?:
boolean
;
}
export
const
Inner
=
(
name
:
string
):
RelationDef
=>
{
return
{
name
,
inner
:
true
};
};
export
class
CrudBase
<
T
extends
Record
<
string
,
any
>
&
{
id
:
string
|
number
;
}
&
QueryWise
<
T
>
&
DeletionWise
&
PageSettingsFactory
ImportWise
&
PageSettingsFactory
,
>
extends
ConsoleLogger
{
protected
readonly
entityName
:
string
;
constructor
(
protected
entityClass
:
ClassConstructor
<
T
>
,
protected
repo
:
Repository
<
T
>
,
protected
entityRelations
:
string
[]
=
[],
protected
entityRelations
:
(
string
|
RelationDef
)
[]
=
[],
// eslint-disable-next-line @typescript-eslint/no-empty-function
protected
extraGetQuery
:
(
qb
:
SelectQueryBuilder
<
T
>
)
=>
void
=
(
qb
)
=>
{},
)
{
...
...
@@ -40,9 +52,14 @@ export class CrudBase<
this
.
entityName
=
entityClass
.
name
;
}
async
batchCreate
(
ents
:
T
[],
beforeCreate
?:
(
repo
:
Repository
<
T
>
)
=>
void
)
{
async
batchCreate
(
ents
:
T
[],
beforeCreate
?:
(
repo
:
Repository
<
T
>
)
=>
void
,
skipErrors
=
false
,
)
{
const
entsWithId
=
ents
.
filter
((
ent
)
=>
ent
.
id
!=
null
);
const
savedEnt
=
await
this
.
repo
.
manager
.
transaction
(
async
(
mdb
)
=>
{
const
result
=
await
this
.
repo
.
manager
.
transaction
(
async
(
mdb
)
=>
{
let
skipped
:
{
result
:
string
;
entry
:
T
}[]
=
[];
const
repo
=
mdb
.
getRepository
(
this
.
entityClass
);
if
(
entsWithId
.
length
)
{
...
...
@@ -55,7 +72,11 @@ export class CrudBase<
const
existingEntsWithoutDeleteTime
=
existingEnts
.
filter
(
(
ent
)
=>
ent
.
deleteTime
==
null
,
);
const
existingEntsWithDeleteTime
=
existingEnts
.
filter
(
(
ent
)
=>
ent
.
deleteTime
!=
null
,
);
if
(
existingEntsWithoutDeleteTime
.
length
)
{
if
(
!
skipErrors
)
{
throw
new
BlankReturnMessageDto
(
404
,
`
${
this
.
entityName
}
ID
${
existingEntsWithoutDeleteTime
.
join
(
...
...
@@ -63,14 +84,32 @@ export class CrudBase<
)}
already exists`
,
).
toException
();
}
await
repo
.
delete
(
existingEnts
.
map
((
ent
)
=>
ent
.
id
)
as
any
[]);
const
skippedEnts
=
ents
.
filter
((
ent
)
=>
existingEntsWithoutDeleteTime
.
some
((
e
)
=>
e
.
id
===
ent
.
id
),
);
skipped
=
skippedEnts
.
map
((
ent
)
=>
({
result
:
'
Already exists
'
,
entry
:
ent
,
}));
const
skippedEntsSet
=
new
Set
(
skippedEnts
);
ents
=
ents
.
filter
((
ent
)
=>
!
skippedEntsSet
.
has
(
ent
));
}
if
(
existingEntsWithDeleteTime
.
length
)
{
await
repo
.
delete
(
existingEntsWithDeleteTime
.
map
((
ent
)
=>
ent
.
id
)
as
any
[],
);
}
}
}
if
(
beforeCreate
)
{
await
beforeCreate
(
repo
);
}
try
{
return
await
repo
.
save
(
ents
as
DeepPartial
<
T
>
[]);
const
results
=
await
repo
.
save
(
ents
);
return
{
results
,
skipped
,
};
}
catch
(
e
)
{
this
.
error
(
`Failed to create entity
${
JSON
.
stringify
(
ents
)}
:
${
e
.
toString
()}
`
,
...
...
@@ -78,7 +117,7 @@ export class CrudBase<
throw
new
BlankReturnMessageDto
(
500
,
'
internal error
'
).
toException
();
}
});
return
new
ReturnMessageDto
(
201
,
'
success
'
,
savedEn
t
);
return
new
ReturnMessageDto
(
201
,
'
success
'
,
resul
t
);
}
async
create
(
ent
:
T
,
beforeCreate
?:
(
repo
:
Repository
<
T
>
)
=>
void
)
{
...
...
@@ -105,7 +144,7 @@ export class CrudBase<
await
beforeCreate
(
repo
);
}
try
{
return
await
repo
.
save
(
ent
as
DeepPartial
<
T
>
);
return
await
repo
.
save
(
ent
);
}
catch
(
e
)
{
this
.
error
(
`Failed to create entity
${
JSON
.
stringify
(
ent
)}
:
${
e
.
toString
()}
`
,
...
...
@@ -120,22 +159,33 @@ export class CrudBase<
return
camelCase
(
this
.
entityName
);
}
protected
applyRelationToQuery
(
qb
:
SelectQueryBuilder
<
T
>
,
relation
:
string
)
{
const
relationUnit
=
relation
.
split
(
'
.
'
);
protected
applyRelationToQuery
(
qb
:
SelectQueryBuilder
<
T
>
,
relation
:
RelationDef
,
)
{
const
{
name
}
=
relation
;
const
relationUnit
=
name
.
split
(
'
.
'
);
const
base
=
relationUnit
.
length
===
1
?
this
.
entityAliasName
:
relationUnit
.
slice
(
0
,
relationUnit
.
length
-
1
).
join
(
'
_
'
);
const
property
=
relationUnit
[
relationUnit
.
length
-
1
];
const
properyAlias
=
relationUnit
.
join
(
'
_
'
);
qb
.
leftJoinAndSelect
(
`
${
base
}
.
${
property
}
`
,
properyAlias
);
const
methodName
=
relation
.
inner
?
'
innerJoinAndSelect
'
:
(
'
leftJoinAndSelect
'
as
const
);
qb
[
methodName
](
`
${
base
}
.
${
property
}
`
,
properyAlias
);
}
protected
applyRelationsToQuery
(
qb
:
SelectQueryBuilder
<
T
>
)
{
for
(
const
relation
of
this
.
entityRelations
)
{
if
(
typeof
relation
===
'
string
'
)
{
this
.
applyRelationToQuery
(
qb
,
{
name
:
relation
});
}
else
{
this
.
applyRelationToQuery
(
qb
,
relation
);
}
}
}
protected
queryBuilder
()
{
return
this
.
repo
.
createQueryBuilder
(
this
.
entityAliasName
);
...
...
@@ -204,17 +254,6 @@ export class CrudBase<
}
}
async
findAllPlain
(
ent
?:
Partial
<
T
>
,
extraQuery
:
(
qb
:
SelectQueryBuilder
<
T
>
)
=>
void
=
()
=>
{},
)
{
const
queryEnt
=
plainToClass
(
this
.
entityClass
,
ent
,
{
groups
:
[
'
r
'
],
enableImplicitConversion
:
true
,
});
return
this
.
findAll
(
queryEnt
,
extraQuery
);
}
async
update
(
id
:
EntityId
<
T
>
,
entPart
:
Partial
<
T
>
,
...
...
@@ -272,4 +311,65 @@ export class CrudBase<
}
return
new
BlankReturnMessageDto
(
204
,
'
success
'
);
}
async
importCsv
(
csvService
:
CsvParseService
,
data
:
Buffer
,
encoding
=
'
utf8
'
,
extraChecking
?:
(
ent
:
T
)
=>
string
|
Promise
<
string
>
,
)
{
try
{
const
raw
=
await
csvService
.
parseCsv
<
any
>
(
data
,
{
targetEncoding
:
encoding
,
columns
:
true
,
cast
:
false
,
});
const
ents
=
raw
.
map
((
d
)
=>
generateEntityFromRaw
(
this
.
entityClass
,
d
));
return
this
.
importEntities
(
ents
,
extraChecking
);
}
catch
(
e
)
{
throw
new
BlankReturnMessageDto
(
400
,
'
文件解析失败,请检查文件格式。
'
,
).
toException
();
}
}
async
importEntities
(
ents
:
T
[],
extraChecking
?:
(
ent
:
T
)
=>
string
|
Promise
<
string
>
,
):
Promise
<
ReturnMessageDto
<
ImportEntry
<
T
>
[]
>>
{
const
invalidResults
=
_
.
compact
(
await
Promise
.
all
(
ents
.
map
(
async
(
ent
)
=>
{
const
reason
=
ent
.
isValidInCreation
();
if
(
reason
)
{
return
{
entry
:
ent
,
result
:
reason
};
}
if
(
extraChecking
)
{
const
reason
=
await
extraChecking
(
ent
);
if
(
reason
)
{
return
{
entry
:
ent
,
result
:
reason
};
}
}
}),
),
);
const
remainingEnts
=
ents
.
filter
(
(
ent
)
=>
!
invalidResults
.
find
((
result
)
=>
result
.
entry
===
ent
),
);
await
Promise
.
all
(
remainingEnts
.
map
((
ent
)
=>
ent
.
prepareForSaving
()));
const
{
data
}
=
await
this
.
batchCreate
(
remainingEnts
,
undefined
,
true
);
data
.
results
.
forEach
((
e
)
=>
e
.
afterSaving
());
const
results
=
[
...
invalidResults
,
...
data
.
skipped
,
...
data
.
results
.
map
((
e
)
=>
({
entry
:
e
,
result
:
'
OK
'
})),
];
return
new
ReturnMessageDto
(
201
,
'
success
'
,
results
);
}
async
exists
(
id
:
EntityId
<
T
>
):
Promise
<
boolean
>
{
const
ent
=
await
this
.
repo
.
findOne
({
where
:
{
id
},
select
:
[
'
id
'
]
});
return
!!
ent
;
}
}
things/nest-typeorm/src/entities/bases/TimeBase.entity.ts
View file @
2d216a78
...
...
@@ -6,7 +6,16 @@ export interface DeletionWise {
deleteTime
?:
Date
;
}
export
class
TimeBase
extends
PageSettingsDto
implements
DeletionWise
{
export
interface
ImportWise
{
isValidInCreation
():
string
|
undefined
;
prepareForSaving
():
Promise
<
void
>
;
afterSaving
():
void
;
}
export
class
TimeBase
extends
PageSettingsDto
implements
DeletionWise
,
ImportWise
{
@
CreateDateColumn
({
select
:
false
})
@
NotColumn
()
createTime
:
Date
;
...
...
@@ -22,6 +31,14 @@ export class TimeBase extends PageSettingsDto implements DeletionWise {
toObject
()
{
return
JSON
.
parse
(
JSON
.
stringify
(
this
));
}
isValidInCreation
():
string
|
undefined
{
return
;
}
async
prepareForSaving
():
Promise
<
void
>
{}
afterSaving
()
{}
}
export
const
TimeBaseFields
:
(
keyof
TimeBase
)[]
=
[
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment