明树Git Lab
Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
J
jt_backend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
zengfanpei
jt_backend
Commits
6588ab91
Commit
6588ab91
authored
Dec 06, 2025
by
zfp1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update
parent
7912bdfb
Pipeline
#104235
passed with stage
in 4 seconds
Changes
7
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
198 additions
and
162 deletions
+198
-162
projectController.js
controller/projectController.js
+32
-2
templateController.js
controller/templateController.js
+101
-160
index.js
db/index.js
+5
-0
projectExcelData.js
db/model/jt/projectExcelData.js
+55
-0
projectTjjh_del.js
db/model/jt/projectTjjh_del.js
+0
-0
package.json
package.json
+1
-0
projectRouter.js
router/projectRouter.js
+4
-0
No files found.
controller/projectController.js
View file @
6588ab91
...
...
@@ -766,6 +766,35 @@ async function finalJugProject(req, res, next) {
}
}
async
function
getProjectCwpj
(
req
,
res
,
next
)
{
try
{
const
projectId
=
req
.
body
.
id
;
if
(
!
projectId
)
{
return
res
.
sendError
(
errorMessage
.
resourceNotFound
);
}
const
sql
=
`SELECT * FROM (
SELECT
*,
ROW_NUMBER() OVER (
PARTITION BY tampName
ORDER BY updatedAt DESC, id DESC
) AS row_num
FROM jt_project_excel_data
WHERE projectId = :projectId AND del = 0
) AS ranked
WHERE row_num = 1`
;
const
results
=
await
DB
.
sequelize
.
query
(
sql
,
{
replacements
:
{
projectId
},
type
:
sequelize
.
QueryTypes
.
SELECT
,
model
:
DB
.
ProjectExcelData
,
// 替换为实际模型
mapToModel
:
true
});
return
res
.
sendData
(
results
);
}
catch
(
error
)
{
next
(
error
);
}
}
async
function
getProjectInfo
(
req
,
res
,
next
)
{
try
{
if
(
!
req
.
body
.
id
)
{
...
...
@@ -834,8 +863,8 @@ async function listProject(req, res, next) {
projectLzTypes
=
[
5
,
7
,
8
,
9
];
break
;
}
if
(
req
.
body
.
menuType
)
{
where
.
projectLzType
=
{
[
Op
.
in
]:
projectLzTypes
}
if
(
req
.
body
.
menuType
)
{
where
.
projectLzType
=
{
[
Op
.
in
]:
projectLzTypes
}
}
console
.
log
(
projectLzTypes
)
search
.
where
=
where
;
...
...
@@ -999,4 +1028,5 @@ module.exports = {
saveProjectPreLixiang
,
queryLixiangResult
,
queryJueceResult
,
getProjectCwpj
,
}
\ No newline at end of file
controller/templateController.js
View file @
6588ab91
...
...
@@ -6,16 +6,25 @@
*/
const
ExcelJS
=
require
(
'exceljs'
);
const
path
=
require
(
'path'
);
const
{
HyperFormula
}
=
require
(
'hyperformula'
);
const
errorMessage
=
require
(
"../utils/errorMessage"
);
const
keyName
=
{
tjjh
:
"投决计划"
,
xmtzzjll
:
"项目投资资金流量表"
,
xmzbjxjll
:
"项目资本金现金流量表"
,
njfxjll
:
"能建方现金流量表"
,
lrb
:
"利润表"
}
async
function
getXmtzzjllTem
({
startYear
,
endYear
,
tampName
,
projectId
})
{
const
workbook
=
new
ExcelJS
.
Workbook
();
// 1. 读取空模板文件
const
inputFilePath
=
path
.
join
(
__dirname
,
'../public/template/项目投资资金流量表.xlsx'
);
const
inputFilePath
=
path
.
join
(
__dirname
,
`../public/template/
${
keyName
[
tampName
]}
.xlsx`
);
// 2. 处理excel 文件,动态生成表格
await
workbook
.
xlsx
.
readFile
(
inputFilePath
);
const
worksheet
=
await
workbook
.
getWorksheet
(
1
);
console
.
log
(
worksheet
.
actualRowCount
,
worksheet
.
actualColumnCount
,)
// 2.1 处理表头
let
count
=
Number
(
endYear
)
-
Number
(
startYear
);
let
columns
=
[];
...
...
@@ -24,7 +33,7 @@ async function getXmtzzjllTem({ startYear, endYear, tampName, projectId }) {
}
// 2.2 处理数据
let
rows
=
Array
(
5
).
fill
(
Array
(
columns
.
length
).
fill
(
0
));
// 5 是模板有5行
let
rows
=
Array
(
worksheet
.
actualRowCount
-
3
).
fill
(
Array
(
columns
.
length
).
fill
(
""
));
// 5 是模板有5行
if
(
projectId
)
{
let
tzzjlls
=
await
DB
.
ProjectTzzjll
.
findAll
({
where
:
{
projectId
:
projectId
,
del
:
0
},
...
...
@@ -71,74 +80,30 @@ async function getXmtzzjllTem({ startYear, endYear, tampName, projectId }) {
wrapText
:
true
};
cell
.
border
=
BLACK_BORDER
;
//边框
});
});
const
buffer
=
await
workbook
.
xlsx
.
writeBuffer
();
return
buffer
;
}
async
function
getTjjh
({
startYear
,
endYear
,
projectId
,
tampName
})
{
const
workbook
=
new
ExcelJS
.
Workbook
();
const
inputFilePath
=
path
.
join
(
__dirname
,
'../public/templates/投决计划.xlsx'
);
// 2. 处理excel 文件,动态生成表格
await
workbook
.
xlsx
.
readFile
(
inputFilePath
);
const
worksheet
=
await
workbook
.
getWorksheet
(
1
);
// 2.1 处理表头
let
count
=
endYear
-
startYear
;
let
columns
=
[];
for
(
let
index
=
0
;
index
<=
count
;
index
++
)
{
columns
.
push
({
name
:
Number
(
startYear
+
index
)
});
}
// 2.2 处理数据
let
rows
=
Array
(
42
).
fill
([]);
// 5 是模板有5行
if
(
!
projectId
)
{
let
tzzjlls
=
await
ProjectTzzjll
.
findAll
({
where
:
{
projectId
:
projectId
,
del
:
0
},
raw
:
true
,
});
tzzjlls
=
_
.
sortBy
(
tzzjlls
,
'year'
);
rows
=
thvc
(
tzzjlls
);
}
console
.
log
(
rows
,
"============="
)
// 使用addTable创建表格
worksheet
.
addTable
({
name
:
'FinancialData'
,
ref
:
'D3'
,
headerRow
:
true
,
totalsRow
:
false
,
style
:
{
theme
:
null
,
showRowStripes
:
false
,
showColumnStripes
:
false
,
},
columns
:
columns
,
rows
:
rows
});
worksheet
.
mergeCells
(
2
,
3
,
2
,
(
3
+
columns
.
length
))
worksheet
.
mergeCells
(
1
,
1
,
1
,
(
3
+
columns
.
length
))
const
BLACK_BORDER
=
{
top
:
{
style
:
'thin'
,
color
:
{
argb
:
'FF000000'
}
},
left
:
{
style
:
'thin'
,
color
:
{
argb
:
'FF000000'
}
},
bottom
:
{
style
:
'thin'
,
color
:
{
argb
:
'FF000000'
}
},
right
:
{
style
:
'thin'
,
color
:
{
argb
:
'FF000000'
}
}
};
// 遍历所有单元格添加边框
worksheet
.
eachRow
((
row
,
rowNumber
)
=>
{
row
.
eachCell
((
cell
,
colNumber
)
=>
{
if
(
rowNumber
>=
4
&&
rowNumber
<=
8
&&
colNumber
>=
4
&&
colNumber
<=
(
3
+
columns
.
length
))
{
cell
.
numFmt
=
'#,##0.00'
;
//数字格式
if
(
tampName
==
"tjjh"
)
{
let
enColNum
=
getExcelColumnLetter
(
colNumber
)
if
(
rowNumber
==
13
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=SUM(
${
enColNum
}
5,
${
enColNum
}
8,
${
enColNum
}
11)`
}
}
if
(
rowNumber
==
21
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=
${
enColNum
}
19-
${
enColNum
}
20`
}
//{ formula: '=AF19-AF20' }
}
if
(
rowNumber
==
26
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=SUM(
${
enColNum
}
27,
${
enColNum
}
28)`
}
}
if
(
rowNumber
==
29
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=SUM(
${
enColNum
}
30,
${
enColNum
}
31)`
}
}
if
(
rowNumber
==
35
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=SUM(
${
enColNum
}
36-
${
enColNum
}
37)`
}
}
if
(
rowNumber
==
38
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=
${
enColNum
}
39-
${
enColNum
}
40`
}
}
if
(
rowNumber
==
41
&&
colNumber
>=
4
)
{
cell
.
value
=
{
formula
:
`=SUM(
${
enColNum
}
42-
${
enColNum
}
43)`
}
}
}
cell
.
alignment
=
{
horizontal
:
'center'
,
vertical
:
'middle'
,
//垂直居中
wrapText
:
true
};
cell
.
border
=
BLACK_BORDER
;
//边框
});
});
const
buffer
=
await
workbook
.
xlsx
.
writeBuffer
();
...
...
@@ -150,14 +115,7 @@ async function getExcelTemplate(req, res, next) {
let
{
tampName
,
projectId
,
startYear
,
endYear
}
=
req
.
query
;
startYear
=
Number
(
startYear
)
||
new
Date
().
getFullYear
();
endYear
=
endYear
||
Number
(
startYear
+
33
);
let
keyName
=
{
tjjh
:
"投决计划"
,
xmtzzjll
:
"项目投资资金流量表"
,
xmzbjxjll
:
"项目资本金现金流量表"
,
njfxjll
:
"能建方现金流量表"
,
lrb
:
"利润表"
}
let
buffer
=
await
getXmtzzjllTem
({
startYear
,
endYear
,
tampName
,
projectId
,
keyName
});
let
buffer
=
await
getXmtzzjllTem
({
startYear
,
endYear
,
tampName
,
projectId
});
// 3. 将处理后的文件发送给前端
res
.
setHeader
(
'Content-Type'
,
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
);
res
.
setHeader
(
'Content-Disposition'
,
'attachment; filename='
+
encodeURIComponent
(
`
${
keyName
[
tampName
]}
.xlsx`
));
...
...
@@ -168,104 +126,87 @@ async function getExcelTemplate(req, res, next) {
}
}
//处理 项目投资资金流量表 以【数据项、年度】为【数据库表结构字段】 转化为excel行数据
function
thvc
(
a
)
{
let
xjlr
=
[],
xjlc
=
[],
sdsqjxjll
=
[],
tzsds
=
[],
sdshjxjll
=
[];
for
(
let
index
=
0
;
index
<
a
.
length
;
index
++
)
{
const
element
=
a
[
index
];
xjlr
.
push
(
element
.
xjlr
);
xjlc
.
push
(
element
.
xjlc
);
sdsqjxjll
.
push
(
element
.
sdsqjxjll
);
tzsds
.
push
(
element
.
tzsds
);
sdshjxjll
.
push
(
element
.
sdshjxjll
);
// year.push(element.year);
// 处理列数字转字母
function
getExcelColumnLetter
(
columnNumber
)
{
// 验证输入有效性
if
(
typeof
columnNumber
!==
'number'
||
columnNumber
<
1
)
{
throw
new
Error
(
'列号必须是大于0的整数'
);
}
return
[
xjlr
,
xjlc
,
sdsqjxjll
,
tzsds
,
sdshjxjll
];
let
dividend
=
columnNumber
;
let
columnLetter
=
''
;
while
(
dividend
>
0
)
{
// 取余数(0-25对应A-Z)
const
modulo
=
(
dividend
-
1
)
%
26
;
// 将ASCII码转换为字母(65是'A'的ASCII码)
columnLetter
=
String
.
fromCharCode
(
65
+
modulo
)
+
columnLetter
;
// 更新被除数
dividend
=
Math
.
floor
((
dividend
-
modulo
)
/
26
);
}
return
columnLetter
;
}
async
function
importExcelTempData
(
req
,
res
,
next
)
{
try
{
let
{
tampName
,
projectId
}
=
req
.
body
;
if
(
tampName
==
'xmtzzjll'
)
{
// if (!projectId) {
// errorMessage.paramsError.message = "项目ID不能为空";
// return res.sendError(errorMessage.paramsError);
// }
const
workbook
=
new
ExcelJS
.
Workbook
();
workbook
.
calcProperties
.
fullCalcOnLoad
=
true
;
await
workbook
.
xlsx
.
load
(
req
.
file
.
buffer
,
{
ignoreNodes
:
[
'relationships'
,
'styles'
,
'calcChain'
,
'drawings'
,
'core'
]
});
const
worksheet
=
await
workbook
.
getWorksheet
(
1
);
let
headers
=
[],
data
=
[];
worksheet
.
eachRow
((
row
,
rowIndex
)
=>
{
let
colText
=
[];
row
.
eachCell
((
col
,
colIndex
)
=>
{
console
.
log
(
col
.
result
,
col
.
value
,
"--"
,
col
.
text
,
"-"
,
col
.
toString
());
if
(
rowIndex
==
3
&&
colIndex
>
3
)
{
console
.
log
(
col
);
headers
.
push
(
col
.
text
);
}
if
(
rowIndex
>
3
)
{
colText
.
push
(
col
.
value
);
}
});
if
(
rowIndex
>
3
)
{
data
.
push
(
colText
);
}
});
return
res
.
sendData
({
headers
,
data
});
}
else
if
(
tampName
===
'tjjh'
)
{
if
(
!
projectId
)
{
errorMessage
.
paramsError
.
message
=
"项目ID不能为空"
;
return
res
.
sendError
(
errorMessage
.
paramsError
);
}
const
workbook
=
new
ExcelJS
.
Workbook
();
await
workbook
.
xlsx
.
load
(
req
.
file
.
buffer
);
const
worksheet
=
await
workbook
.
getWorksheet
(
1
);
let
years
=
[],
data
=
[];
worksheet
.
eachRow
((
row
,
rowIndex
)
=>
{
if
(
rowIndex
===
3
)
{
years
=
row
.
values
.
slice
(
4
);
}
if
(
rowIndex
>
3
)
{
data
.
push
(
row
.
values
.
slice
(
4
));
console
.
log
(
tampName
,
projectId
);
const
workbook
=
new
ExcelJS
.
Workbook
();
console
.
log
(
req
.
file
);
console
.
log
(
req
.
file
.
buffer
);
await
workbook
.
xlsx
.
load
(
req
.
file
.
buffer
);
const
worksheet
=
workbook
.
getWorksheet
(
1
);
const
hf
=
HyperFormula
.
buildEmpty
({
licenseKey
:
'gpl-v3'
,
precisionRounding
:
14
});
const
sheetData
=
[];
worksheet
.
eachRow
({
includeEmpty
:
true
},
(
row
,
ri
)
=>
{
const
rowData
=
[];
row
.
eachCell
({
includeEmpty
:
true
},
(
cell
,
ci
)
=>
{
if
(
cell
.
formula
)
{
rowData
.
push
(
cell
.
formula
);
}
else
if
(
cell
.
type
===
ExcelJS
.
ValueType
.
String
)
{
rowData
.
push
(
cell
.
text
+
"_suffix"
);
}
else
if
(
cell
.
value
===
null
||
cell
.
value
===
undefined
)
{
rowData
.
push
(
null
);
}
else
{
rowData
.
push
(
cell
.
value
);
}
});
const
fieldMap
=
{
1
:
'xjlr'
,
// 现金流入
2
:
'xjlc'
,
// 现金流出
3
:
'sdsqjxjll'
,
// 所得税前净现金流量
4
:
'tzsds'
,
// 调整所得税
5
:
'sdshjxjll'
// 所得税后净现金流量
};
let
retData
=
[];
years
.
map
((
year
,
yearIndex
)
=>
{
const
yearData
=
{
year
};
for
(
let
rowIndex
=
1
;
rowIndex
<=
data
.
length
;
rowIndex
++
)
{
const
fieldKey
=
fieldMap
[
rowIndex
];
if
(
fieldKey
)
{
const
value
=
data
[
rowIndex
-
1
][
yearIndex
];
yearData
[
fieldKey
]
=
parseFloat
(
value
)
||
0
;
}
sheetData
.
push
(
rowData
);
});
hf
.
addSheet
(
'Sheet1'
);
// 先建表
hf
.
setSheetContent
(
0
,
sheetData
);
// 灌数据+公式
const
{
height
,
width
}
=
hf
.
getSheetDimensions
(
0
);
const
out
=
[];
for
(
let
r
=
0
;
r
<
height
;
r
++
)
{
let
cc
=
[];
for
(
let
c
=
0
;
c
<
sheetData
[
0
].
length
;
c
++
)
{
// const formulaString = hf.getCellFormula({ sheet: 0, row: r, col: c });
let
calculated
=
hf
.
getCellValue
({
sheet
:
0
,
row
:
r
,
col
:
c
});
// console.log(formulaString, calculated)
if
(
typeof
calculated
==
'string'
)
{
calculated
=
removeSuffix
(
String
(
calculated
),
'_suffix'
)
}
retData
.
push
(
yearData
);
});
await
ProjectTzzjll
.
destroy
({
where
:
{
projectId
:
projectId
},
});
await
ProjectTzzjll
.
bulkCreate
(
retData
);
cc
.
push
(
calculated
);
}
out
.
push
(
cc
);
}
await
DB
.
ProjectExcelData
.
create
({
projectId
,
tampName
,
data
:
out
});
return
res
.
sendData
(
out
);
}
catch
(
error
)
{
next
(
error
)
}
}
function
removeSuffix
(
str
,
suffix
)
{
return
str
.
endsWith
(
suffix
)
?
str
.
slice
(
0
,
-
suffix
.
length
)
:
str
;
}
async
function
importExcelTempData2
(
req
,
res
,
next
)
{
try
{
let
{
tampName
,
projectId
}
=
req
.
body
;
...
...
db/index.js
View file @
6588ab91
const
sequelize
=
require
(
'./model/index'
);
// 模型初始化
const
User
=
require
(
"./model/system/user"
);
const
Role
=
require
(
"./model/system/role"
);
...
...
@@ -31,6 +33,7 @@ const ProjectTzzjll = require("./model/jt/projectTzzjll");
const
ProjectTzzt
=
require
(
"./model/jt/projectTzzt"
);
const
ProjectXmtzze
=
require
(
"./model/jt/projectXmtzze"
);
const
ProjectLixiang
=
require
(
"./model/jt/projectLixiang"
);
const
ProjectExcelData
=
require
(
"./model/jt/projectExcelData"
);
/**
...
...
@@ -40,6 +43,7 @@ const ProjectLixiang = require("./model/jt/projectLixiang");
global
.
DB
=
{
sequelize
,
User
,
RequestLog
,
Role
,
...
...
@@ -69,6 +73,7 @@ global.DB = {
Message
,
FlowRecord
,
ProjectLixiang
,
ProjectExcelData
,
}
...
...
db/model/jt/projectExcelData.js
0 → 100644
View file @
6588ab91
const
{
DataTypes
}
=
require
(
'sequelize'
);
const
sequelize
=
require
(
'../index'
);
const
moment
=
require
(
'moment'
);
//投决计划---决策信息--财务评价--投决计划
const
ProjectExcelData
=
sequelize
.
define
(
'ProjectExcelData'
,
{
id
:
{
type
:
DataTypes
.
INTEGER
,
primaryKey
:
true
,
autoIncrement
:
true
},
tampName
:
{
type
:
DataTypes
.
STRING
,
allowNull
:
true
,
comment
:
"模板名称"
},
data
:
{
type
:
DataTypes
.
JSON
,
allowNull
:
true
,
comment
:
"解析数据"
},
//原本设计数据横向转纵向存储,再取出来纵向转横向还原,过于复杂,而且数据无其他统计计算要求,只是展示,暂时此方案,后续若改动,清洗数据即可
projectId
:
{
type
:
DataTypes
.
INTEGER
,
comment
:
"所属项目ID"
,
},
del
:
{
type
:
DataTypes
.
INTEGER
,
defaultValue
:
0
,
comment
:
"0 正常 1 删除"
},
// createdAt: {
// type: DataTypes.DATE,
// defaultValue: new Date(),
// get() {
// const rawValue = this.getDataValue('createdAt');
// return rawValue ? moment(rawValue).format('YYYY-MM-DD HH:mm:ss') : '';
// }
// },
// updatedAt: { // 同样处理 updatedAt
// type: DataTypes.DATE,
// defaultValue: new Date(),
// get() {
// const rawValue = this.getDataValue('createdAt');
// return rawValue ? moment(rawValue).format('YYYY-MM-DD HH:mm:ss') : '';
// }
// }
},
{
tableName
:
'jt_project_excel_data'
,
// 指定表名(如果与模型名不同)
timestamps
:
true
,
// 是否自动添加 createdAt 和 updatedAt 字段(覆盖全局设置)
});
// 同步模型到数据库(创建表)
ProjectExcelData
.
sync
({
force
:
false
,
// force: true ,//会删除已存在表并重新创建
// alter: true
})
.
then
(()
=>
{
console
.
log
(
'ProjectExcelData 表同步成功'
);
});
module
.
exports
=
ProjectExcelData
;
\ No newline at end of file
db/model/jt/projectTjjh.js
→
db/model/jt/projectTjjh
_del
.js
View file @
6588ab91
File moved
package.json
View file @
6588ab91
...
...
@@ -24,6 +24,7 @@
"
exceljs
"
:
"
4.4.0
"
,
"
express
"
:
"
4.19.2
"
,
"
form-data
"
:
"
4.0.0
"
,
"
hyperformula
"
:
"
^3.1.0
"
,
"
ioredis
"
:
"
5.4.1
"
,
"
joi
"
:
"
17.13.1
"
,
"
lodash
"
:
"
4.17.21
"
,
...
...
router/projectRouter.js
View file @
6588ab91
...
...
@@ -25,6 +25,10 @@ router.post('/startJuece', projectController.startJuece); //发起决策
router
.
post
(
'/queryJueceResult'
,
projectController
.
queryJueceResult
);
//查询决策审批结果
// 财务评价
router
.
post
(
'/getProjectCwpj'
,
projectController
.
getProjectCwpj
);
router
.
post
(
'/getProjectInfo'
,
projectController
.
getProjectInfo
);
router
.
post
(
'/updateProject'
,
projectController
.
updateProject
);
...
...
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