Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
pac
Overview
Overview
Details
Activity
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
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ai-tools
pac
Commits
a167b9ec
Commit
a167b9ec
authored
Oct 15, 2026
by
luoqi
Browse files
Options
Browse Files
Download
Plain Diff
Merge Friday export UTC fix from main into test
parents
a627d29c
21c471ba
Pipeline
#3814
failed in 0 seconds
Changes
2
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
79 additions
and
9 deletions
+79
-9
apps/pac-service/data/friday/export.sh
+7
-9
apps/pac-service/tests/friday-export-timezone.spec.ts
+72
-0
No files found.
apps/pac-service/data/friday/export.sh
View file @
a167b9ec
...
...
@@ -62,8 +62,8 @@ if [ -n "$CLINICS$SINCE" ]; then
let ids = null;
if (cl.length) ids = db.med_emr_info.distinct("patientId", Object.assign({ organizationId: { $in: cl.flatMap(both) } }, base)).map(S);
if (sn) {
//
clinicTime 是"北京墙钟伪装成 UTC"的 Date(见下方 EMR 导出注释)→ cutoff 按 Z 构造即对齐存储
const set = new Set(db.med_emr_info.distinct("patientId", Object.assign({ clinicTime: { $gte: new Date(sn + "T00:00:00
Z
") } }, base)).map(S));
//
--since 按宿主北京时间零点筛选;Mongo Date 保存真实 UTC 瞬间。
const set = new Set(db.med_emr_info.distinct("patientId", Object.assign({ clinicTime: { $gte: new Date(sn + "T00:00:00
+08:00
") } }, base)).map(S));
ids = ids === null ? Array.from(set) : ids.filter((x) => set.has(x));
}
print(JSON.stringify(ids ?? []));
...
...
@@ -171,14 +171,12 @@ mysql_csv "SELECT id,tenant_id,organization_id,customer_id,task_director_id,task
echo
"── Mongo 导出(med_emr_info,status∈{3,4} 正式病历)──"
# 不用 mongoexport:Extended JSON 会把 Date/Long 包成 {$date}/{$numberLong} 对象,破坏平铺契约。
# mongosh 手动平铺:Date→
naive 北京墙钟
字符串,Long→字符串,数组(diag/treat/dispose/examine)原样保留
# mongosh 手动平铺:Date→
带 Z 的 ISO 8601
字符串,Long→字符串,数组(diag/treat/dispose/examine)原样保留
# (PAC split_json_array 原生支持已 parse 数组)。产出 JSON 数组文件(cold-import json 格式)。
#
# ⚠️ Date 时区实证(2026-07-20,clinicTime/createdGmtAt 小时分布双证):FRIDAY 写库时把
# 北京墙钟"伪装成 UTC"存进 Mongo Date(直读 UTC 字段=营业时间双峰 上午7-10/下午13-17;
# 按真 UTC 解释则一半病历落深夜——荒谬)。故导出**不能** toISOString 带 Z(会被 PAC 当真
# UTC 再 +8,EMR 链时间全偏 8 小时,已踩):把 UTC 字段值直读为墙钟,输出 naive
# "YYYY-MM-DD HH:mm:ss",交给 manifest timezone=Asia/Shanghai 解释 → 瞬间正确。
# Mongo Date 保存真实 UTC 瞬间(2026-10-15 按同一预约的 MySQL 到诊时间与 Mongo clinicTime 核实)。
# 保留 toISOString() 的 Z 和毫秒;截掉 Z 会被 PAC 按 Asia/Shanghai 再转换,导致时间提前 8 小时。
# MySQL 无时区 datetime 仍按宿主北京时间导出,由 PAC 统一归一。
# 诊断字典 map(diag_code→std_code)—— diag[] inline std_code 的 within-host join;linkCode 在 Mongo、
# 字典在 MySQL,故预取成 map 挂进 mongo 容器,eval 里逐元素解析(不再 PAC 侧 lookup std_diag)。
printf
'SELECT diag_code,std_code FROM emr.std_diag\n'
| docker run
-i
--rm
mysql:8 mysql
\
...
...
@@ -196,7 +194,7 @@ echo " std_diag_map.json: $(node -e 'process.stdout.write(String(Object.keys(JS
MONGO_ARGS
=(
-v
"
$PWD
/std_diag_map.json:/std_diag_map.json:ro"
)
if
[
-n
"
$COHORT_IN
"
]
;
then
MONGO_ARGS+
=(
-v
"
$PWD
/cohort_ids.json:/cohort_ids.json:ro"
-e
"COHORT=1"
)
;
fi
docker run
--rm
${
MONGO_ARGS
[@]+
"
${
MONGO_ARGS
[@]
}
"
}
mongo:7 mongosh
"
$FRIDAY_MONGO_URI
"
--quiet
--eval
'
const flat = (v) => v instanceof Date ? v.toISOString()
.slice(0, 19).replace("T", " ")
const flat = (v) => v instanceof Date ? v.toISOString()
: (v && v.constructor && v.constructor.name === "Long") ? String(v)
: v === undefined ? null : v;
// id 类字段字符串化:Mongo 存的是 number/Long 混型,canonical 契约要 string
...
...
apps/pac-service/tests/friday-export-timezone.spec.ts
0 → 100644
View file @
a167b9ec
import
{
readFileSync
}
from
'node:fs'
;
import
{
join
}
from
'node:path'
;
import
{
runInNewContext
}
from
'node:vm'
;
import
{
normalizeDatetime
}
from
'../src/modules/sync/assembler/field-mapper'
;
// 执行脚本内实际交给 mongosh 的代码,无需连接数据库或运行完整导出。
const
script
=
readFileSync
(
join
(
__dirname
,
'../data/friday/export.sh'
),
'utf8'
);
const
programs
=
Array
.
from
(
script
.
matchAll
(
/--eval '
([\s\S]
*
?)
'/g
),
(
m
)
=>
m
[
1
]
!
);
if
(
programs
.
length
!==
2
)
throw
new
Error
(
'未找到 cohort 与病历导出的 mongosh 程序'
);
const
[
cohortProgram
,
exportProgram
]
=
programs
as
[
string
,
string
];
describe
(
'FRIDAY Mongo 导出时间'
,
()
=>
{
it
.
each
([
'2026-10-15T06:40:00.000Z'
,
// 北京 14:40
'2026-10-14T16:00:00.000Z'
,
// 北京次日零点
'2026-10-15T13:36:24.634Z'
,
// 保留源时间毫秒
])(
'导出后经 Asia/Shanghai 归一仍是原始时刻: %s'
,
(
instant
)
=>
{
const
output
:
string
[]
=
[];
runInNewContext
(
exportProgram
,
{
Date
,
print
:
(
line
:
string
)
=>
output
.
push
(
line
),
require
:
()
=>
({
readFileSync
:
(
path
:
string
)
=>
{
if
(
path
===
'/std_diag_map.json'
)
return
'{}'
;
throw
new
Error
(
'全量导出无 cohort 文件'
);
},
}),
db
:
{
med_emr_info
:
{
find
:
()
=>
({
forEach
:
(
visit
:
(
row
:
Record
<
string
,
unknown
>
)
=>
void
)
=>
visit
({
emrSubId
:
'emr-1'
,
patientId
:
123
,
status
:
3
,
clinicTime
:
new
Date
(
instant
),
createdGmtAt
:
new
Date
(
instant
),
updatedGmtAt
:
new
Date
(
instant
),
}),
}),
},
},
});
const
[
row
]
=
JSON
.
parse
(
output
.
join
(
'
\
n'
))
as
Array
<
Record
<
string
,
unknown
>>
;
for
(
const
field
of
[
'clinic_time'
,
'created_gmt_at'
,
'updated_gmt_at'
])
{
expect
(
row
!
[
field
]).
toBe
(
instant
);
expect
(
new
Date
(
normalizeDatetime
(
String
(
row
!
[
field
]),
'Asia/Shanghai'
)).
toISOString
())
.
toBe
(
instant
);
}
});
it
(
'按北京时间 --since 零点选人,包含当天前 8 小时并排除前一天'
,
()
=>
{
const
rows
=
[
{
patientId
:
'before'
,
status
:
3
,
clinicTime
:
new
Date
(
'2026-10-14T15:59:59.999Z'
)
},
{
patientId
:
'midnight'
,
status
:
3
,
clinicTime
:
new
Date
(
'2026-10-14T16:00:00Z'
)
},
{
patientId
:
'early'
,
status
:
4
,
clinicTime
:
new
Date
(
'2026-10-14T23:59:59Z'
)
},
{
patientId
:
'later'
,
status
:
3
,
clinicTime
:
new
Date
(
'2026-10-15T00:00:00Z'
)
},
{
patientId
:
'draft'
,
status
:
2
,
clinicTime
:
new
Date
(
'2026-10-15T00:00:00Z'
)
},
];
let
result
:
string
[]
=
[];
runInNewContext
(
cohortProgram
,
{
Date
,
process
:
{
env
:
{
SN
:
'2026-10-15'
}
},
print
:
(
line
:
string
)
=>
{
result
=
JSON
.
parse
(
line
)
as
string
[];
},
db
:
{
med_emr_info
:
{
distinct
:
(
_field
:
string
,
query
:
{
status
:
{
$in
:
number
[]
};
clinicTime
:
{
$gte
:
Date
}
})
=>
rows
.
filter
((
r
)
=>
query
.
status
.
$in
.
includes
(
r
.
status
)
&&
r
.
clinicTime
>=
query
.
clinicTime
.
$gte
).
map
((
r
)
=>
r
.
patientId
),
},
},
});
expect
(
result
).
toEqual
([
'midnight'
,
'early'
,
'later'
]);
});
});
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