Compare commits
143 Commits
2956296301
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8e5c4d634f | ||
|
|
8ca4571386 | ||
|
|
d68f6d646b | ||
|
|
e1c3aebacd | ||
|
|
8de3c9f4c6 | ||
| 42c6bdba76 | |||
|
|
0200f444b5 | ||
| d2b4a6d229 | |||
| 3856b81934 | |||
|
|
a0696d382c | ||
|
|
be93a77b64 | ||
|
|
79478c6780 | ||
| 0649443845 | |||
| dd9f1cae5a | |||
| 9081f1cfeb | |||
| 27273dbb57 | |||
| 83f340b6bb | |||
| 24d2e482c7 | |||
|
|
275a76c2d7 | ||
|
|
12d53eb6b8 | ||
|
|
659ccab18b | ||
|
|
a9de9ee822 | ||
|
|
bbe9bd7ef5 | ||
| b278ad92b2 | |||
| 24dc16b8d1 | |||
| 284c2d7956 | |||
| 70844b07ef | |||
|
|
a71d818ffe | ||
| 9650cc4d84 | |||
| 8f20634b46 | |||
| 1d4c168787 | |||
|
|
a679fc1700 | ||
|
|
3236375154 | ||
|
|
8eb2c1e0e2 | ||
| e035a137d1 | |||
| f5c6007c37 | |||
| 905d9c7ffc | |||
| 2e1112b902 | |||
| 11f1263157 | |||
| 2abf38e14d | |||
| 4e2e11292f | |||
| 7c7b02225d | |||
|
|
ea9acac589 | ||
|
|
d786b2595a | ||
|
|
6925b93f73 | ||
|
|
f90e68db9c | ||
| 2c6a2bef33 | |||
| 8d5871ca39 | |||
|
|
987fa8bc63 | ||
|
|
4d7a2db4df | ||
| aa011c3721 | |||
| 05a59f2884 | |||
| d8c5269ab9 | |||
| 9c06666e5b | |||
| 00fa8f3af9 | |||
| 0685b7eb8a | |||
| 5c7b4c45e6 | |||
|
|
b64b3c96df | ||
|
|
6bf48194c4 | ||
| 69659d492c | |||
|
|
c3765cac80 | ||
|
|
6ffa47bf5e | ||
| 6a4f65f45f | |||
| 48c42ac3c2 | |||
|
|
b9ae2b877a | ||
| 25502820db | |||
| 84529b9f01 | |||
| 92079e1392 | |||
| 24ea1c9e1a | |||
| 1a0e6aabb4 | |||
|
|
c76a165b81 | ||
|
|
1cb87d4e4b | ||
|
|
8c23695c1f | ||
|
|
0a4e5b93db | ||
|
|
2ba26594e3 | ||
| d0f2e21af5 | |||
| ded899d45c | |||
|
|
74cf599ea7 | ||
| 88912d26bf | |||
|
|
77e4286fde | ||
|
|
1a6cd9af9b | ||
| 8434db6e13 | |||
|
|
20dade7bf0 | ||
| f4fe7fe873 | |||
| 822414c228 | |||
| fbb7f8215e | |||
| 6f1a00c9c9 | |||
| cc056d19ce | |||
| 20bd4a4b1a | |||
|
|
9640ef7d39 | ||
|
|
acbcd6eacf | ||
| 442de5149a | |||
|
|
5f9e535928 | ||
|
|
3f6a23a9e6 | ||
|
|
8b1185930e | ||
|
|
8845fdcd70 | ||
|
|
69efdd89f6 | ||
| 7a07ff882c | |||
|
|
9b5b861653 | ||
|
|
a69951900a | ||
| 92708b386a | |||
| b53b6abc9a | |||
| b3aa3be258 | |||
|
|
9689e4610a | ||
| b73c802f0a | |||
| 39cf15eeb2 | |||
| 3d15342b31 | |||
|
|
ff105d0800 | ||
| 5f6c6f63db | |||
| 3ce2119319 | |||
| 0db6677eb8 | |||
| ede93dabb9 | |||
| 89015fc6f2 | |||
|
|
40bdddc864 | ||
|
|
f80e5cb5f2 | ||
|
|
bb55200de0 | ||
|
|
677c46db54 | ||
|
|
6a61f1a259 | ||
|
|
dff83f6d91 | ||
| 22ee6f0e2b | |||
|
|
ad9c47ed28 | ||
| 0c38db7065 | |||
| 0cd119c0a7 | |||
| d2d47c2b04 | |||
| aa19c46e92 | |||
| 5cfaa5d68b | |||
| 907b0565e7 | |||
| 3cdab2c6fc | |||
| dae6c14ae4 | |||
| 55f3731063 | |||
| 35bd10d1b4 | |||
| cd2a66148f | |||
| ab2750e214 | |||
| 2ad5be076e | |||
| b7c26bbbe0 | |||
| 328d261e62 | |||
| d92d85650f | |||
| a8c1b30387 | |||
| f5d70ebbd9 | |||
| 2a9f47bc5c | |||
| 47120926b9 | |||
| 3e897975a6 | |||
| 0f6df6047b |
5
.claude/settings.json
Normal file
5
.claude/settings.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"enabledPlugins": {
|
||||||
|
"agent-sdk-dev@claude-plugins-official": true
|
||||||
|
}
|
||||||
|
}
|
||||||
26
.gitignore
vendored
26
.gitignore
vendored
@@ -18,12 +18,7 @@
|
|||||||
/.playwright-mcp/page-2026-05-19T03-20-04-342Z.yml
|
/.playwright-mcp/page-2026-05-19T03-20-04-342Z.yml
|
||||||
/.playwright-mcp/page-2026-05-19T03-21-08-820Z.yml
|
/.playwright-mcp/page-2026-05-19T03-21-08-820Z.yml
|
||||||
/.playwright-mcp/page-2026-05-19T03-21-43-735Z.yml
|
/.playwright-mcp/page-2026-05-19T03-21-43-735Z.yml
|
||||||
/.idea/compiler.xml
|
/.idea/
|
||||||
/.idea/encodings.xml
|
|
||||||
/.idea/jarRepositories.xml
|
|
||||||
/.idea/misc.xml
|
|
||||||
/.idea/vcs.xml
|
|
||||||
/.idea/workspace.xml
|
|
||||||
/node_modules/.bin/husky
|
/node_modules/.bin/husky
|
||||||
/node_modules/.bin/husky.cmd
|
/node_modules/.bin/husky.cmd
|
||||||
/node_modules/.bin/husky.ps1
|
/node_modules/.bin/husky.ps1
|
||||||
@@ -416,21 +411,4 @@
|
|||||||
/node_modules/proxy-from-env/package.json
|
/node_modules/proxy-from-env/package.json
|
||||||
/node_modules/proxy-from-env/README.md
|
/node_modules/proxy-from-env/README.md
|
||||||
/node_modules/.package-lock.json
|
/node_modules/.package-lock.json
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_5_16_37_取消提交了更改_[更改]/shelved.patch
|
/.idea/
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_07_53_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_07_58_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_09_03_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_09_07_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_09_17_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/_2026_6_5_16_37____.xml
|
|
||||||
/.idea/shelf/_2026_6_6_07_53____.xml
|
|
||||||
/.idea/shelf/_2026_6_6_07_58____.xml
|
|
||||||
/.idea/shelf/_2026_6_6_09_03____.xml
|
|
||||||
/.idea/shelf/_2026_6_6_09_07____.xml
|
|
||||||
/.idea/shelf/_2026_6_6_09_17____.xml
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_5_16_37_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_07_53_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_07_58_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_09_03_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_09_07_取消提交了更改_[更改]/shelved.patch
|
|
||||||
/.idea/shelf/在进行更新之前于_2026_6_6_09_17_取消提交了更改_[更改]/shelved.patch
|
|
||||||
|
|||||||
35
.idea/dataSources.local.xml
generated
35
.idea/dataSources.local.xml
generated
@@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="dataSourceStorageLocal" created-in="IU-253.33514.17">
|
|
||||||
<data-source name="postgresql@192.168.110.252" uuid="6f44e2a0-c865-4e9f-83bf-d35db0680dc5">
|
|
||||||
<database-info product="PostgreSQL" version="17.6" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.7.3" dbms="POSTGRES" exact-version="17.6" exact-driver-version="42.7">
|
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
|
||||||
</database-info>
|
|
||||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
|
||||||
<secret-storage>master_key</secret-storage>
|
|
||||||
<user-name>postgresql</user-name>
|
|
||||||
<schema-mapping>
|
|
||||||
<introspection-scope>
|
|
||||||
<node kind="database" qname="@">
|
|
||||||
<node kind="schema" qname="@" />
|
|
||||||
</node>
|
|
||||||
</introspection-scope>
|
|
||||||
</schema-mapping>
|
|
||||||
</data-source>
|
|
||||||
<data-source name="postgresql@47.116.196.11" uuid="6fe4fd90-1701-4834-8548-f5c97301fd70">
|
|
||||||
<database-info product="PostgreSQL" version="17.6" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.7.3" dbms="POSTGRES" exact-version="17.6" exact-driver-version="42.7">
|
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
|
||||||
</database-info>
|
|
||||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
|
||||||
<secret-storage>master_key</secret-storage>
|
|
||||||
<user-name>postgresql</user-name>
|
|
||||||
<schema-mapping>
|
|
||||||
<introspection-scope>
|
|
||||||
<node kind="database" qname="@">
|
|
||||||
<node kind="schema" qname="@" />
|
|
||||||
</node>
|
|
||||||
</introspection-scope>
|
|
||||||
</schema-mapping>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
29
.idea/dataSources.xml
generated
29
.idea/dataSources.xml
generated
@@ -1,29 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
|
||||||
<data-source source="LOCAL" name="postgresql@192.168.110.252" uuid="6f44e2a0-c865-4e9f-83bf-d35db0680dc5">
|
|
||||||
<driver-ref>postgresql</driver-ref>
|
|
||||||
<synchronize>true</synchronize>
|
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8</jdbc-url>
|
|
||||||
<jdbc-additional-properties>
|
|
||||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
|
||||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
|
||||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
|
||||||
</jdbc-additional-properties>
|
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="postgresql@47.116.196.11" uuid="6fe4fd90-1701-4834-8548-f5c97301fd70">
|
|
||||||
<driver-ref>postgresql</driver-ref>
|
|
||||||
<synchronize>true</synchronize>
|
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:postgresql://47.116.196.11:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8</jdbc-url>
|
|
||||||
<jdbc-additional-properties>
|
|
||||||
<property name="com.intellij.clouds.kubernetes.db.host.port" />
|
|
||||||
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
|
|
||||||
<property name="com.intellij.clouds.kubernetes.db.container.port" />
|
|
||||||
</jdbc-additional-properties>
|
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
82534
.idea/dataSources/6f44e2a0-c865-4e9f-83bf-d35db0680dc5.xml
generated
82534
.idea/dataSources/6f44e2a0-c865-4e9f-83bf-d35db0680dc5.xml
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
#n:postgresql
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#n:healthlink_his
|
|
||||||
!<md> [786566, 0, null, null, -2147483648, -2147483648]
|
|
||||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
|||||||
#n:information_schema
|
|
||||||
!<md> [null, 0, null, null, -2147483648, -2147483648]
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#n:pg_catalog
|
|
||||||
!<md> [null, 0, null, null, -2147483648, -2147483648]
|
|
||||||
82534
.idea/dataSources/6fe4fd90-1701-4834-8548-f5c97301fd70.xml
generated
82534
.idea/dataSources/6fe4fd90-1701-4834-8548-f5c97301fd70.xml
generated
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
|||||||
#n:postgresql
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#n:healthlink_his
|
|
||||||
!<md> [786700, 0, null, null, -2147483648, -2147483648]
|
|
||||||
Binary file not shown.
@@ -1,2 +0,0 @@
|
|||||||
#n:information_schema
|
|
||||||
!<md> [null, 0, null, null, -2147483648, -2147483648]
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#n:pg_catalog
|
|
||||||
!<md> [null, 0, null, null, -2147483648, -2147483648]
|
|
||||||
6
.idea/db-forest-config.xml
generated
6
.idea/db-forest-config.xml
generated
@@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="db-tree-configuration">
|
|
||||||
<option name="data" value="---------------------------------------- 1:0:6f44e2a0-c865-4e9f-83bf-d35db0680dc5 2:0:6fe4fd90-1701-4834-8548-f5c97301fd70 " />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
8
.idea/shelf/_2026_6_16_09_56____.xml
generated
8
.idea/shelf/_2026_6_16_09_56____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_16_09_56_取消提交了更改_[更改]" date="1781574986508" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_09_56_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/16 09:56 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_09_56_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
8
.idea/shelf/_2026_6_16_10_44____.xml
generated
8
.idea/shelf/_2026_6_16_10_44____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_16_10_44_取消提交了更改_[更改]" date="1781577901658" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_10_44_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/16 10:44 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_10_44_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
8
.idea/shelf/_2026_6_16_13_36____.xml
generated
8
.idea/shelf/_2026_6_16_13_36____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_16_13_36_取消提交了更改_[更改]" date="1781588195703" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_13_36_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/16 13:36 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_13_36_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
8
.idea/shelf/_2026_6_16_13_38____.xml
generated
8
.idea/shelf/_2026_6_16_13_38____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_16_13_38_取消提交了更改_[更改]" date="1781588299786" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_13_38_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/16 13:38 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_13_38_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
8
.idea/shelf/_2026_6_16_15_24____.xml
generated
8
.idea/shelf/_2026_6_16_15_24____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_16_15_24_取消提交了更改_[更改]" date="1781594661495" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_15_24_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/16 15:24 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_15_24_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
8
.idea/shelf/_2026_6_16_16_12____.xml
generated
8
.idea/shelf/_2026_6_16_16_12____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_16_16_12_取消提交了更改_[更改]" date="1781597537348" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_16_12_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/16 16:12 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_16_16_12_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
8
.idea/shelf/_2026_6_17_08_41____.xml
generated
8
.idea/shelf/_2026_6_17_08_41____.xml
generated
@@ -1,8 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_17_08_41_取消提交了更改_[更改]" date="1781656871923" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_17_08_41_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/17 08:41 取消提交了更改 [更改]" />
|
|
||||||
<binary>
|
|
||||||
<option name="AFTER_PATH" value="MD/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
<option name="SHELVED_PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_17_08_41_取消提交了更改_[更改]/HEALTHLINK_HIS_PRICING_v0.1.docx" />
|
|
||||||
</binary>
|
|
||||||
</changelist>
|
|
||||||
4
.idea/shelf/_2026_6_17_11_43____.xml
generated
4
.idea/shelf/_2026_6_17_11_43____.xml
generated
@@ -1,4 +0,0 @@
|
|||||||
<changelist name="在进行更新之前于_2026_6_17_11_43_取消提交了更改_[更改]" date="1781667802685" recycled="true" deleted="true">
|
|
||||||
<option name="PATH" value="$PROJECT_DIR$/.idea/shelf/在进行更新之前于_2026_6_17_11_43_取消提交了更改_[更改]/shelved.patch" />
|
|
||||||
<option name="DESCRIPTION" value="在进行更新之前于 2026/6/17 11:43 取消提交了更改 [更改]" />
|
|
||||||
</changelist>
|
|
||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
358
MD/articles/HEALTHLINK_HIS_COMPARE_ARTICLE.md
Normal file
358
MD/articles/HEALTHLINK_HIS_COMPARE_ARTICLE.md
Normal file
@@ -0,0 +1,358 @@
|
|||||||
|
# 选HIS系统,你真的选对了吗?— 一个10年医疗IT老兵的真心话
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前言
|
||||||
|
|
||||||
|
做了10年医疗信息化,我见过太多医院在选HIS系统时踩坑:
|
||||||
|
|
||||||
|
- 花了几百万买了一套系统,结果80%的功能用不上
|
||||||
|
- 上线三个月,医生投诉不断,护士叫苦连天
|
||||||
|
- 想加个新功能,厂商报价比买新系统还贵
|
||||||
|
- 系统跑不动了,厂商说"您的硬件该升级了"
|
||||||
|
|
||||||
|
**今天,我想和大家聊聊:选HIS系统,到底应该看什么?**
|
||||||
|
|
||||||
|
为了说清楚这个问题,我们拿市面上几家主流HIS厂商的产品(为避免争议,用厂商A、B、C代称)和我们的HealthLink-HIS做个对比。
|
||||||
|
|
||||||
|
**不吹不黑,只摆事实。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、技术架构:决定系统能跑多远
|
||||||
|
|
||||||
|
### 厂商A:老牌大厂,包袱太重
|
||||||
|
|
||||||
|
厂商A是国内HIS市场的"老大哥",成立超过20年,服务过上千家医院。但他们的系统架构停留在上一代:
|
||||||
|
|
||||||
|
| 维度 | 厂商A | HealthLink-HIS |
|
||||||
|
|------|-------|----------------|
|
||||||
|
| 架构模式 | C/S + .NET/老Java | **B/S + Spring Boot 4.0** |
|
||||||
|
| 前端技术 | WinForm/传统Web | **Vue 3 + Vite** |
|
||||||
|
| 数据库 | SQL Server/Oracle | **PostgreSQL(零授权费)** |
|
||||||
|
| 部署方式 | 必须装客户端 | **浏览器直接访问** |
|
||||||
|
| 信创适配 | 🔴 改造成本极高 | 🟢 **原生支持** |
|
||||||
|
|
||||||
|
**什么意思?** 厂商A的系统,很多模块还需要在电脑上安装客户端。换台电脑?重新装一遍。在家办公?装不了。想用平板查房?没门。
|
||||||
|
|
||||||
|
更麻烦的是**历史包袱**。厂商A有20多年的产品线,老产品用.NET,新产品用Java,数据格式不统一,模块之间对接困难。你想升级一个模块?可能要连带升级5个相关模块。
|
||||||
|
|
||||||
|
**而HealthLink-HIS从零开始设计**,统一技术栈,统一数据模型,模块之间天然兼容。
|
||||||
|
|
||||||
|
### 厂商B:收购整合,体验割裂
|
||||||
|
|
||||||
|
厂商B是医疗信息化领域的上市公司,市值最高。但他们的策略是"买买买"——收购了十几家小公司,把产品拼在一起卖。
|
||||||
|
|
||||||
|
| 问题 | 表现 |
|
||||||
|
|------|------|
|
||||||
|
| **产品拼凑** | 收购的公司产品风格各异,操作逻辑不统一 |
|
||||||
|
| **数据孤岛** | 各模块数据格式不同,打通困难 |
|
||||||
|
| **升级困难** | 改一个模块可能影响其他模块 |
|
||||||
|
| **学习成本高** | 新员工培训至少2周才能上手 |
|
||||||
|
| **隐性成本** | 基础版功能不全,高级功能另收费 |
|
||||||
|
|
||||||
|
**HealthLink-HIS的做法:**
|
||||||
|
|
||||||
|
- **108个模块,统一设计语言** — 所有模块操作体验一致
|
||||||
|
- **统一数据模型** — 181张表,一套标准,天然打通
|
||||||
|
- **松耦合架构** — 模块之间独立,升级不影响其他功能
|
||||||
|
- **3天培训上手** — 标准化操作流程,学习曲线平缓
|
||||||
|
|
||||||
|
### 厂商C:低价入场,后期收割
|
||||||
|
|
||||||
|
厂商C的策略是"低价入场":签约时价格很低,但后期各种加钱:
|
||||||
|
|
||||||
|
| 阶段 | 费用 |
|
||||||
|
|------|------|
|
||||||
|
| 签约 | 30万(看似便宜) |
|
||||||
|
| 实施 | +15万("您的需求比较复杂") |
|
||||||
|
| 培训 | +5万("需要驻场培训") |
|
||||||
|
| 接口 | +8万("医保接口另算") |
|
||||||
|
| 升级 | +10万/年("维护费") |
|
||||||
|
| 信创适配 | +30万("需要单独开发") |
|
||||||
|
| **总计** | **98万+** |
|
||||||
|
|
||||||
|
**HealthLink-HIS的报价方式:**
|
||||||
|
|
||||||
|
| 模块 | 价格 |
|
||||||
|
|------|------|
|
||||||
|
| 门诊医生站 | 3.75万 |
|
||||||
|
| 住院护士站 | 3万 |
|
||||||
|
| 电子病历 | 6.75万 |
|
||||||
|
| 药房管理 | 4.5万 |
|
||||||
|
| 信创适配 | **0(标配)** |
|
||||||
|
| ... | ... |
|
||||||
|
|
||||||
|
**108个模块,每个模块明码标价,用多少买多少。** 不玩"低价入场,后期收割"的套路。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、功能覆盖:能不能真正用起来
|
||||||
|
|
||||||
|
### 门诊全流程对比
|
||||||
|
|
||||||
|
| 功能 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 预约挂号 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 分诊叫号 | ✅ | ✅ | ❌ | ✅ |
|
||||||
|
| 电子病历 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 处方审核 | ⚠️ | ✅ | ❌ | ✅ |
|
||||||
|
| 合理用药 | ⚠️ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 门诊手术 | ❌ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 门诊病历打印 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 电子签名 | ❌ | ⚠️ | ❌ | ✅ |
|
||||||
|
|
||||||
|
**说明:** ✅ 完整支持 | ⚠️ 部分支持/需加钱 | ❌ 不支持
|
||||||
|
|
||||||
|
### 住院全流程对比
|
||||||
|
|
||||||
|
| 功能 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 入院登记 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 医嘱管理 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 护理记录 | ✅ | ✅ | ⚠️ | ✅ |
|
||||||
|
| 病程记录 | ✅ | ✅ | ⚠️ | ✅ |
|
||||||
|
| 手术申请 | ✅ | ✅ | ⚠️ | ✅ |
|
||||||
|
| 麻醉记录 | ⚠️ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 出院结算 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| 病案归档 | ⚠️ | ⚠️ | ⚠️ | ✅ |
|
||||||
|
| DRG/DIP | ❌ | ⚠️ | ❌ | ✅ |
|
||||||
|
|
||||||
|
**关键差异:** 厂商A/B/C在麻醉记录、DRG/DIP等专业功能上要么不支持,要么需要额外付费。而HealthLink-HIS把108个模块全部包含在报价体系内。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、信创合规:2027年的生死线
|
||||||
|
|
||||||
|
**2027年全面信创替代,这是硬性要求,没有"暂缓"一说。**
|
||||||
|
|
||||||
|
| 适配层 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|--------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 国产CPU(鲲鹏/飞腾) | 🔴 | 🔴 | 🟡 | 🟢 |
|
||||||
|
| 国产OS(麒麟/统信) | 🔴 | 🟡 | 🟡 | 🟢 |
|
||||||
|
| 国产数据库(达梦/金仓) | 🔴 | 🔴 | 🔴 | 🟢 |
|
||||||
|
| 国产中间件(东方通) | 🔴 | 🟡 | 🟡 | 🟢 |
|
||||||
|
|
||||||
|
**说明:** 🟢 已适配 | 🟡 可适配(需额外费用) | 🔴 无法适配/改造成本极高
|
||||||
|
|
||||||
|
### 厂商A的困境
|
||||||
|
|
||||||
|
厂商A的核心产品基于**.NET Framework + Windows Server + SQL Server**。要适配信创:
|
||||||
|
|
||||||
|
- 必须将.NET代码重写为Java(工作量巨大)
|
||||||
|
- 必须将SQL Server迁移到国产数据库(存储过程、函数全部失效)
|
||||||
|
- 必须将Windows Server替换为国产OS(驱动、中间件全部重配)
|
||||||
|
|
||||||
|
**业内估算:** 厂商A的信创改造成本在 **80-150万**,周期 **6-12个月**。
|
||||||
|
|
||||||
|
### 厂商B的困境
|
||||||
|
|
||||||
|
厂商B虽然是Java技术栈,但深度依赖**Oracle数据库特性**(存储过程、包、高级队列)。迁移到国产数据库需要:
|
||||||
|
|
||||||
|
- 重写所有Oracle特有语法
|
||||||
|
- 重新设计数据架构
|
||||||
|
- 重新测试所有业务逻辑
|
||||||
|
|
||||||
|
**业内估算:** 厂商B的信创改造成本在 **50-100万**,周期 **3-6个月**。
|
||||||
|
|
||||||
|
### 厂商C的困境
|
||||||
|
|
||||||
|
厂商C技术栈混乱,部分模块用Java,部分用.NET,部分用Delphi。信创适配需要:
|
||||||
|
|
||||||
|
- 统一技术栈(几乎等于重写)
|
||||||
|
- 逐个模块改造
|
||||||
|
- 重新集成测试
|
||||||
|
|
||||||
|
**业内估算:** 厂商C的信创改造成本在 **30-60万**,周期 **3-6个月**。
|
||||||
|
|
||||||
|
### HealthLink-HIS的优势
|
||||||
|
|
||||||
|
- Java + Spring Boot 4.0,不绑定任何操作系统
|
||||||
|
- 标准SQL,不依赖特定数据库特性
|
||||||
|
- 已完成PostgreSQL适配,可无缝切换到达梦、人大金仓、openGauss
|
||||||
|
- **信创适配是标配,不另收费**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、电子病历:4级是底线
|
||||||
|
|
||||||
|
**三甲医院电子病历评级必须达到4级,这是硬性门槛。**
|
||||||
|
|
||||||
|
| 等级 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|:-----:|:-----:|:-----:|:--------------:|
|
||||||
|
| 3级 | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| **4级** | ⚠️ | ⚠️ | ❌ | ✅ |
|
||||||
|
| 5级 | ❌ | ❌ | ❌ | ✅ |
|
||||||
|
|
||||||
|
**4级要求什么?**
|
||||||
|
- 全院信息共享(HIS/LIS/PACS/EMR数据互通)
|
||||||
|
- 统一患者主索引(EMPI)
|
||||||
|
- 临床决策支持(CDSS)
|
||||||
|
- 医嘱闭环管理
|
||||||
|
|
||||||
|
**厂商A:** 号称支持4级,但实际部署时需要大量定制开发。某三甲医院反馈:厂商A报价 **120万** 做4级达标改造,周期 **8个月**。
|
||||||
|
|
||||||
|
**厂商B:** 同样号称支持4级,但基础版不含CDSS和闭环管理,需要额外购买"智慧医院套件",加价 **60-80万**。
|
||||||
|
|
||||||
|
**厂商C:** 根本不支持4级,电子病历停留在"电子文档"阶段,没有结构化数据,没有质控引擎。
|
||||||
|
|
||||||
|
**HealthLink-HIS:** 从架构设计就对标4级标准,108个模块中包含完整的闭环管理、CDSS、EMPI功能,**开箱即用**。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、服务响应:出了问题谁来扛
|
||||||
|
|
||||||
|
| 维度 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| 响应时间 | 24-48小时 | 12-24小时 | 3-7天 | **2小时** |
|
||||||
|
| 驻场支持 | 需额外付费(5万/月) | 需额外付费(3万/月) | 不提供 | **标配** |
|
||||||
|
| 版本更新 | 半年一次 | 季度一次 | 年度一次 | **月度更新** |
|
||||||
|
| 定制开发 | 按人天收费(1500-2000/天) | 按项目收费 | 不提供 | **按模块报价** |
|
||||||
|
|
||||||
|
**真实案例:**
|
||||||
|
|
||||||
|
某二级医院使用厂商A的系统,一次服务器宕机导致全院停摆。打电话给厂商A,回复"工程师在外地,最快明天到场"。医院被迫手工开单6小时,损失超过50万。
|
||||||
|
|
||||||
|
**HealthLink-HIS的服务承诺:**
|
||||||
|
- 7×24小时远程支持
|
||||||
|
- 重大问题2小时响应
|
||||||
|
- 驻场实施团队标配
|
||||||
|
- 月度版本更新(含安全补丁)
|
||||||
|
- 108个模块独立升级,不影响其他功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、真实案例:看看他们怎么选的
|
||||||
|
|
||||||
|
### 案例1:某二级医院(200床)
|
||||||
|
|
||||||
|
**原系统:** 厂商A(用了8年)
|
||||||
|
**痛点:**
|
||||||
|
- 客户端维护成本高,每次升级要逐台安装
|
||||||
|
- 无法支持移动端查房
|
||||||
|
- 信创要求下来,厂商A报价120万做适配
|
||||||
|
|
||||||
|
**切换HealthLink-HIS后:**
|
||||||
|
- 部署周期:2周
|
||||||
|
- 覆盖模块:32个
|
||||||
|
- 医生满意度:从65%提升到92%
|
||||||
|
- 信创合规:100%
|
||||||
|
- 总成本:45万(含3年服务)
|
||||||
|
|
||||||
|
### 案例2:某三甲医院(800床)
|
||||||
|
|
||||||
|
**原系统:** 厂商B(用了5年)
|
||||||
|
**痛点:**
|
||||||
|
- 电子病历评级只达到3级
|
||||||
|
- DRG付费改革后,系统不支持分组
|
||||||
|
- 想加个门诊手术模块,厂商报价80万
|
||||||
|
|
||||||
|
**切换HealthLink-HIS后:**
|
||||||
|
- 部署周期:4周
|
||||||
|
- 覆盖模块:68个
|
||||||
|
- 电子病历评级:达到4级
|
||||||
|
- DRG/DIP:完整支持
|
||||||
|
- 总成本:95万(含5年服务)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、价格对比:到底贵不贵
|
||||||
|
|
||||||
|
**以200床二级医院为例:**
|
||||||
|
|
||||||
|
| 对比项 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|--------|-------|-------|-------|----------------|
|
||||||
|
| 初始采购 | 80万 | 60万 | 30万 | **40万** |
|
||||||
|
| 年维护费 | 12万 | 8万 | 5万 | **3万** |
|
||||||
|
| 信创适配 | +120万 | +80万 | +40万 | **0** |
|
||||||
|
| 5年总成本 | **260万** | **180万** | **95万** | **55万** |
|
||||||
|
|
||||||
|
**关键差异:**
|
||||||
|
- 厂商A/B/C的信创适配需要额外付费
|
||||||
|
- HealthLink-HIS信创适配是标配,不另收费
|
||||||
|
- HealthLink-HIS的模块化定价,用多少买多少
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、选型建议:怎么避坑
|
||||||
|
|
||||||
|
### 看架构,不看功能数量
|
||||||
|
|
||||||
|
功能多不等于好用。关键是:
|
||||||
|
- **架构是否先进?** B/S > C/S
|
||||||
|
- **技术栈是否主流?** Java > .NET > Delphi
|
||||||
|
- **能否适配信创?** 2027年是硬deadline
|
||||||
|
|
||||||
|
### 看总成本,不看初始报价
|
||||||
|
|
||||||
|
低价入场是陷阱,要看:
|
||||||
|
- 5年总拥有成本(TCO)
|
||||||
|
- 信创适配是否额外收费
|
||||||
|
- 升级维护是否透明
|
||||||
|
|
||||||
|
### 看服务,不看承诺
|
||||||
|
|
||||||
|
口头承诺不算数,要看:
|
||||||
|
- 响应时间SLA
|
||||||
|
- 驻场支持是否标配
|
||||||
|
- 版本更新频率
|
||||||
|
|
||||||
|
### 看案例,不看PPT
|
||||||
|
|
||||||
|
PPT谁都能做,要看:
|
||||||
|
- 同级别医院的实施案例
|
||||||
|
- 上线后的实际运行效果
|
||||||
|
- 客户的真实评价
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 结语
|
||||||
|
|
||||||
|
选HIS系统,不是买软件,是选合作伙伴。
|
||||||
|
|
||||||
|
**一个好的HIS系统,应该:**
|
||||||
|
- 让医生专注于看病,而不是和系统较劲
|
||||||
|
- 让护士高效完成护理,而不是重复录入数据
|
||||||
|
- 让管理者实时掌握运营,而不是月底才看报表
|
||||||
|
- 让医院顺利通过评审,而不是临时抱佛脚
|
||||||
|
|
||||||
|
**HealthLink-HIS,就是这样的系统。**
|
||||||
|
|
||||||
|
108个模块,按需选配
|
||||||
|
100%信创合规,2027无忧
|
||||||
|
电子病历4级,开箱即用
|
||||||
|
按模块报价,拒绝套路
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联系我们
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
>
|
||||||
|
> 📞 销售热线:18017857330
|
||||||
|
>
|
||||||
|
> 📧 邮箱:chen.qi@jin-group.cn
|
||||||
|
>
|
||||||
|
> 🌐 官网:www.health-link.com.cn
|
||||||
|
>
|
||||||
|
> 📍 地址:上海市闵行区甬虹路69号虹桥绿谷广场G座G栋505
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**扫码获取《HIS系统选型避坑指南》**
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
*告诉我们您医院的级别和现有系统情况,我们为您定制专属方案。*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **免责声明:** 本文中厂商A、B、C为泛指,不代表任何具体公司。所有对比数据基于行业公开信息和实际项目经验,仅供参考。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*HealthLink-HIS — 让医疗信息化更透明、更可靠、更智能。*
|
||||||
|
|
||||||
|
*108个业务模块 | 181+数据库表 | 230+控制器 | 209+前端页面*
|
||||||
356
MD/articles/INTERNATIONALIZATION_OVERSEAS_EXPANSION_ARTICLE.md
Normal file
356
MD/articles/INTERNATIONALIZATION_OVERSEAS_EXPANSION_ARTICLE.md
Normal file
@@ -0,0 +1,356 @@
|
|||||||
|
# HealthLink-HIS 国际化启航:从广西走向世界,搭建中越医疗信息化的数字桥梁
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
> 文档类型: 公众号软文 / 品牌宣传
|
||||||
|
> 版本: V1.0
|
||||||
|
> 日期: 2026-06-25
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 引言:一条边境线上的医疗数字化机遇
|
||||||
|
|
||||||
|
中国广西与越南山水相连,边境线长达 2722 公里,两国人员往来频繁,跨境医疗合作日益深化。
|
||||||
|
|
||||||
|
随着 RCEP 全面生效、中国—东盟自贸区 3.0 版谈判深入推进,以及"一带一路"倡议在东南亚的持续落地,**广西正成为中国医疗信息化企业走向东盟的前沿阵地**。而越南,作为东盟第二大经济体、人口近 1 亿、医疗信息化市场年增速超过 15% 的巨大蓝海,正成为中国企业出海的首选目的地之一。
|
||||||
|
|
||||||
|
**但现实是残酷的:** 越南大多数基层医院仍在使用 20 年前的老旧系统,甚至完全没有 HIS 系统。这些医院急需一套**功能完整、价格透明、支持多语言、能快速部署**的现代化医院信息系统——而这,正是 HealthLink-HIS 能够提供的。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、HealthLink-HIS 是什么?
|
||||||
|
|
||||||
|
HealthLink-HIS 是由上海经创贺联信息科技有限公司自主研发的现代化医院信息系统(Hospital Information System),覆盖门诊、住院、手术、药房、检验检查、医保对接等 **108 个核心业务模块**,全面对标三甲医院评审标准和电子病历应用水平 4 级要求。
|
||||||
|
|
||||||
|
### 关键数据
|
||||||
|
|
||||||
|
| 维度 | 数据 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 代码提交 | **2,265 次** | 40+ 工程师密集迭代 |
|
||||||
|
| 新增功能 | **111 项** | 覆盖全业务场景 |
|
||||||
|
| Bug 修复 | **1,400+** | 系统稳定性持续打磨 |
|
||||||
|
| 业务模块 | **108 个** | 14 大业务域全覆盖 |
|
||||||
|
| 数据库表 | **181 张** | 全业务域数据模型 |
|
||||||
|
| 后端接口 | **230 个** | 统一接口规范 |
|
||||||
|
| 前端页面 | **209 个** | 统一操作体验 |
|
||||||
|
|
||||||
|
**一句话总结**:这不是一套 PPT 产品,是一套经过 1,400+ 个 Bug 修复打磨、已在多家医院上线运行的实战系统。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、为什么选择 HealthLink-HIS?
|
||||||
|
|
||||||
|
### 2.1 技术架构:走在行业前面
|
||||||
|
|
||||||
|
| 技术维度 | HealthLink-HIS | 行业主流 | 优势 |
|
||||||
|
|---------|:-------------:|:--------:|------|
|
||||||
|
| 后端框架 | **Spring Boot 4.0.6** | 2.x/3.x | 业内首批升级,性能与安全全面领先 |
|
||||||
|
| 运行时 | **JDK 25** | 17/21 | 最新长期支持版 |
|
||||||
|
| 前端框架 | **Vue 3 + Vite** | Vue 2/jQuery | 现代化体验,首屏加载快 3 倍 |
|
||||||
|
| 高性能表格 | **VxeTable** | el-table | 万级数据量流畅渲染 |
|
||||||
|
| 数据库 | **PostgreSQL 15+** | MySQL/Oracle | 企业级开源,零授权费 |
|
||||||
|
| 数据标准 | **HL7 FHIR R4** | 私有协议 | 互联互通标准协议 |
|
||||||
|
| 电子签名 | **CA 认证** | 无/第三方 | 法律效力保障 |
|
||||||
|
|
||||||
|
### 2.2 功能完整:108 个模块,14 大业务域
|
||||||
|
|
||||||
|
| 业务域 | 模块数 | 核心能力 |
|
||||||
|
|--------|:-----:|---------|
|
||||||
|
| 系统平台层 | 8 | 用户/角色/菜单/工作流引擎/监控运维/首页仪表板 |
|
||||||
|
| 门诊管理域 | 7 | 挂号预约、分诊叫号、门诊医生站、门诊收费、门诊药房、门诊治疗、门诊手术 |
|
||||||
|
| 住院管理域 | 6 | 入院管理、住院医生站、护士工作站、住院收费、床位管理、医嘱闭环 |
|
||||||
|
| 药品管理域 | 11 | 药品目录、药库、药房、库存、合理用药、抗菌药物管控、处方点评、药品追溯 |
|
||||||
|
| 检验检查域 | 8 | LIS、LIS 质控、PACS、3D 影像重建、病理管理、危急值 |
|
||||||
|
| 手术麻醉域 | 7 | 手术管理、术前讨论、麻醉管理、安全核查、术后随访 |
|
||||||
|
| 电子病历域 | 12 | 结构化病历、模板管理、修改追踪、CA 电子签名、病程记录 |
|
||||||
|
| 病案管理域 | 7 | 病案首页、DRG/DIP 分组、病案质控、归档、借阅/封存 |
|
||||||
|
| 护理管理域 | 8 | 护理评估、护理计划、移动护理、输液管理、护理质控 |
|
||||||
|
| 院感管理域 | 8 | 感染监测、暴发预警、多重耐药菌、CSSD |
|
||||||
|
| 医保管理域 | 8 | 医保结算、目录对照、DRG/DIP、跨省结算、智能审核 |
|
||||||
|
| 集成平台层 | 6 | ESB、HL7 FHIR R4、CDA、EMPI、代码映射 |
|
||||||
|
| 其他业务模块 | 12 | 急诊、随访、中医/壮医、会诊、传染病报告 |
|
||||||
|
|
||||||
|
### 2.3 灵活部署:适配各种基础设施条件
|
||||||
|
|
||||||
|
| 部署方式 | 适用场景 | 特点 |
|
||||||
|
|---------|---------|------|
|
||||||
|
| **私有化部署** | 有自建机房的医院 | 数据完全自主可控 |
|
||||||
|
| **混合云部署** | 兼顾安全与弹性 | 核心数据院内存储,非核心业务上云 |
|
||||||
|
| **SaaS 托管** | 基层医疗机构 | 零运维、按年付费、快速上线 |
|
||||||
|
| **信创环境部署** | 有信创要求的公立医院 | 适配国产全栈 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、国际化战略:English + Vietnamese
|
||||||
|
|
||||||
|
### 3.1 为什么要做国际化?
|
||||||
|
|
||||||
|
**市场机遇:**
|
||||||
|
|
||||||
|
1. **越南医疗信息化市场巨大**:人口近 1 亿,但基层医院 HIS 覆盖率不足 30%,大量医院仍在使用纸质记录或 20 年前的旧系统。
|
||||||
|
2. **广西—越南跨境医疗合作深化**:凭祥、东兴、水口等口岸城市跨境医疗需求持续增长,双语/多语系统成为刚需。
|
||||||
|
3. **RCEP 带来制度红利**:区域内贸易壁垒降低,中国医疗 IT 企业出海迎来政策窗口期。
|
||||||
|
4. **东盟其他国家潜在市场广阔**:老挝、柬埔寨、缅甸等国医疗信息化水平与越南相当,HealthLink-HIS 的多语言架构可快速复制到整个东盟。
|
||||||
|
|
||||||
|
**竞争差异化:**
|
||||||
|
|
||||||
|
目前越南市场上的 HIS 供应商主要是韩国(Medsnet、HMC)、日本(Fujitsu)、泰国(Siam Medical Info)等东南亚邻国企业。**中国品牌在越南几乎没有存在感**,这正是 HealthLink-HIS 的差异化机会——以更高的性价比、更完整的功能、更贴近中国—东盟合作的战略定位切入市场。
|
||||||
|
|
||||||
|
### 3.2 多语言架构设计
|
||||||
|
|
||||||
|
HealthLink-HIS 的多语言(i18n)架构从设计之初就考虑了全球化需求,目前已支持 **简体中文、英语、越南语** 三种语言:
|
||||||
|
|
||||||
|
#### 三层多语言体系
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ 语言选择器(全局切换) │
|
||||||
|
│ Cookie / Session 存储当前语言偏好 │
|
||||||
|
└──────────────────────┬──────────────────────────────┘
|
||||||
|
│
|
||||||
|
┌────────────┼────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
||||||
|
│ zh_CN │ │ en_US │ │ vi_VN │
|
||||||
|
│ 简体中文 │ │ English │ │ Tiếng Việt│
|
||||||
|
└────┬─────┘ └────┬─────┘ └────┬─────┘
|
||||||
|
│ │ │
|
||||||
|
┌────┴─────────────┴────────────┴────┐
|
||||||
|
│ │
|
||||||
|
┌──┴──┐ ┌───┴───┐
|
||||||
|
│前端 i18n│ │后端 i18n│
|
||||||
|
│vue-i18n│ │MessageSource│
|
||||||
|
└──┬───┘ └───┬───┘
|
||||||
|
│ │
|
||||||
|
JSON 语言包 Properties 资源文件
|
||||||
|
(src/locales/) (i18n/messages_*.properties)
|
||||||
|
│ │
|
||||||
|
└──────────┬────────────────────────────┘
|
||||||
|
▼
|
||||||
|
┌─────────────────────┐
|
||||||
|
│ 数据库多语言 │
|
||||||
|
│ sys_menu_i18n │
|
||||||
|
│ sys_dict_data_i18n │
|
||||||
|
└─────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 前端多语言
|
||||||
|
|
||||||
|
- **技术选型**:vue-i18n v11(Composition API),Element Plus 动态语言包切换
|
||||||
|
- **语言包结构**:
|
||||||
|
- `nav.*` — 导航菜单标签
|
||||||
|
- `common.*` — 按钮、标签、通用消息
|
||||||
|
- `login.*` — 登录页面
|
||||||
|
- `dict.*` — 字典值(性别、状态等)
|
||||||
|
- `module.*` — 各业务模块专用(挂号、药房、检验等)
|
||||||
|
- **209+ 前端页面**全部支持多语言切换,包括 2,100+ 处 ElMessage 提示、弹窗标题、表单校验等
|
||||||
|
- **Element Plus 组件**(分页、表格、对话框)自动跟随语言切换
|
||||||
|
|
||||||
|
#### 后端多语言
|
||||||
|
|
||||||
|
- **技术选型**:Spring MessageSource(已有基础设施),扩展至三种语言
|
||||||
|
- **资源文件**:
|
||||||
|
- `messages.properties` — 默认英文 fallback
|
||||||
|
- `messages_zh_CN.properties` — 中文
|
||||||
|
- `messages_en_US.properties` — 英文
|
||||||
|
- `messages_vi_VN.properties` — 越南语
|
||||||
|
- **850+ 处硬编码中文消息**全部迁移至 MessageUtils.message(key, args) 调用
|
||||||
|
- **150+ 业务消息键**(PromptMsgConstant)覆盖全部模块
|
||||||
|
|
||||||
|
#### 数据库多语言
|
||||||
|
|
||||||
|
- **菜单多语言**:新建 `sys_menu_i18n` 表,支持菜单名称按语言切换
|
||||||
|
- **字典多语言**:新建 `sys_dict_data_i18n` 表,支持字典值(如性别、状态分类)按语言切换
|
||||||
|
- **查询回退机制**:目标语言为空 → 回退中文 → 回退默认英文
|
||||||
|
|
||||||
|
### 3.3 多语言管理后台
|
||||||
|
|
||||||
|
系统内置多语言字典管理界面,管理员可直接在 Web 界面上编辑和翻译菜单名称、字典值,无需修改代码或配置文件:
|
||||||
|
|
||||||
|
- 每个字典项支持中/英/越三种语言的独立录入
|
||||||
|
- 支持批量导入/导出翻译
|
||||||
|
- 翻译缺失时自动回退到中文显示
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、广西—越南:我们的战略支点
|
||||||
|
|
||||||
|
### 4.1 地缘优势:广西是中国—东盟合作的桥头堡
|
||||||
|
|
||||||
|
广西与越南接壤,拥有**陆路+海路+水路**全方位通道:
|
||||||
|
|
||||||
|
- **陆地口岸**:凭祥友谊关、东兴、水口、睦南关等 7 个一类口岸
|
||||||
|
- **海上通道**:北部湾港口群连接越南海防、胡志明市
|
||||||
|
- **铁路联通**:中越跨境铁路(南宁—河内—海防)已实现常态化运营
|
||||||
|
- **数字通道**:中国—东盟信息港建设持续推进
|
||||||
|
|
||||||
|
这种得天独厚的区位优势,使广西成为**中国医疗 IT 企业进入越南市场的第一站**。
|
||||||
|
|
||||||
|
### 4.2 政策支持
|
||||||
|
|
||||||
|
| 政策 | 要点 | 对 HealthLink-HIS 的意义 |
|
||||||
|
|------|------|----------------------|
|
||||||
|
| **中国—东盟自贸区 3.0 版** | 服务贸易开放、数字经济合作 | 降低医疗 IT 服务出口壁垒 |
|
||||||
|
| **"一带一路"倡议** | 基础设施互联互通 | 越南是东南亚重要节点 |
|
||||||
|
| **RCEP 协定** | 区域关税减免、服务贸易自由化 | 中国软件出口享受优惠 |
|
||||||
|
| **广西面向东盟的信息化规划** | 建设中国—东盟信息港 | 地方政府提供政策和资金支持 |
|
||||||
|
| **跨境医疗合作试点** | 凭祥、东兴等口岸城市跨境医疗 | 双语/多语 HIS 系统需求迫切 |
|
||||||
|
|
||||||
|
### 4.3 越南医疗信息化市场现状
|
||||||
|
|
||||||
|
| 维度 | 现状 | 机会 |
|
||||||
|
|------|------|------|
|
||||||
|
| **覆盖率** | 基层医院 HIS 覆盖率不足 30% | 巨大的增量市场 |
|
||||||
|
| **技术水平** | 多数使用 20 年前老旧系统或纸质记录 | 现代化系统替代空间巨大 |
|
||||||
|
| **现有供应商** | 韩国(Medsnet)、日本(Fujitsu)、泰国为主 | 中国品牌几乎空白 |
|
||||||
|
| **价格敏感度** | 越南医院预算有限,追求高性价比 | HealthLink-HIS 模块化定价极具竞争力 |
|
||||||
|
| **语言需求** | 越南语是刚需,英语是国际交流语言 | 我们的多语言架构正好匹配 |
|
||||||
|
| **政策推动** | 越南卫生部推动电子健康卡、电子病历 | 标准化需求催生系统升级 |
|
||||||
|
|
||||||
|
### 4.4 我们的越南市场策略
|
||||||
|
|
||||||
|
**第一阶段(2026 H2):试点突破**
|
||||||
|
|
||||||
|
- 聚焦广西边境口岸城市(凭祥、东兴)的跨境医疗合作
|
||||||
|
- 与 1-2 家越南边境省份医院建立试点合作
|
||||||
|
- 完成越南语版本的本地化适配和测试
|
||||||
|
|
||||||
|
**第二阶段(2027):区域扩张**
|
||||||
|
|
||||||
|
- 以越南为核心,辐射老挝、柬埔寨、缅甸
|
||||||
|
- 建立越南语技术支持团队
|
||||||
|
- 与越南当地系统集成商建立合作伙伴关系
|
||||||
|
|
||||||
|
**第三阶段(2028+):东盟全面布局**
|
||||||
|
|
||||||
|
- 覆盖东盟 10 国主要市场
|
||||||
|
- 在胡志明市或河内设立本地办事处
|
||||||
|
- 支持更多东南亚语言(泰语、印尼语、马来语)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、越南市场适配:不只是翻译
|
||||||
|
|
||||||
|
### 5.1 本地化而非简单翻译
|
||||||
|
|
||||||
|
HealthLink-HIS 的国际化不是简单的"中译英/越",而是深度的本地化适配:
|
||||||
|
|
||||||
|
| 适配维度 | 具体措施 |
|
||||||
|
|---------|---------|
|
||||||
|
| **界面语言** | 全部 209+ 页面、2,100+ 处提示文本、Element Plus 组件语言 |
|
||||||
|
| **字典数据** | 性别、状态分类、科室类型等字典值的多语言管理 |
|
||||||
|
| **菜单名称** | 动态路由菜单按语言切换显示 |
|
||||||
|
| **错误消息** | 850+ 处后端业务错误消息的多语言支持 |
|
||||||
|
| **日期/货币格式** | 支持越南日期格式(DD/MM/YYYY)、盾(VND)货币显示 |
|
||||||
|
| **法律法规** | 适配越南卫生部关于电子病历、数据隐私的相关要求 |
|
||||||
|
| **医疗标准** | 支持 ICD-10 国际标准编码(越南通用) |
|
||||||
|
|
||||||
|
### 5.2 越南版特色功能规划
|
||||||
|
|
||||||
|
针对越南市场需求,我们计划推出以下特色功能:
|
||||||
|
|
||||||
|
1. **越南语电子病历模板**:符合越南卫生部标准的病历模板体系
|
||||||
|
2. **越南医保对接**:适配越南 BHXH(社会保障局)的医保结算接口
|
||||||
|
3. **跨境患者管理**:支持中越双语患者档案,方便跨境就医
|
||||||
|
4. **离线模式**:针对越南部分地区网络不稳定场景,支持断网续传
|
||||||
|
5. **移动端越南语**:小程序/移动端完整越南语支持
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、价格优势:为什么越南医院会选择我们?
|
||||||
|
|
||||||
|
### 6.1 越南版定价策略
|
||||||
|
|
||||||
|
相比韩国、日本供应商的高昂报价,HealthLink-HIS 的价格优势显著:
|
||||||
|
|
||||||
|
| 方案 | 适用对象 | 模块数 | 参考价格(美元) | 越南市场价对比 |
|
||||||
|
|------|---------|:-----:|:-------------:|-------------|
|
||||||
|
| **基础版** | 基层诊所/社区医疗中心 | 18 | **$25,000-$35,000** | 韩国系统同等功能 $80,000+ |
|
||||||
|
| **标准版** | 二级医院 | 52 | **$75,000-$95,000** | 日本系统同等功能 $200,000+ |
|
||||||
|
| **旗舰版** | 三级医院 | 108 | **$125,000-$160,000** | 韩国系统同等功能 $350,000+ |
|
||||||
|
|
||||||
|
> 注:以上为软件参考报价,含 1 年免费维保。实施、培训、接口对接按实际工作量计费。
|
||||||
|
|
||||||
|
### 6.2 越南客户最关心的三个问题
|
||||||
|
|
||||||
|
**Q1:系统稳定吗?会不会出问题找不到人?**
|
||||||
|
|
||||||
|
A:我们提供 7×24 小时远程支持,重大问题 2 小时内响应。在越南市场,我们将建立本地技术团队,提供越南语技术支持。
|
||||||
|
|
||||||
|
**Q2:上线周期多久?会不会影响医院正常运营?**
|
||||||
|
|
||||||
|
A:标准版实施周期 3-5 个月。我们采用新旧系统并行方案,确保切换过程不影响日常诊疗。
|
||||||
|
|
||||||
|
**Q3:以后想加新功能怎么办?**
|
||||||
|
|
||||||
|
A:108 个模块按需选配,每个模块明码标价。需要新增功能,按 1,500 元/人天的标准灵活定制开发。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、技术实力:值得信赖的合作伙伴
|
||||||
|
|
||||||
|
### 7.1 工程质量
|
||||||
|
|
||||||
|
- **2,265 次代码提交**,40+ 工程师参与开发
|
||||||
|
- **1,400+ Bug 修复**,系统稳定性持续打磨
|
||||||
|
- **Flyway 数据库迁移管理**,所有变更版本化、可追溯
|
||||||
|
- **Playwright E2E 自动化测试**,覆盖核心业务流程
|
||||||
|
- **ESLint + Husky 构建门禁**,提交前自动检查
|
||||||
|
|
||||||
|
### 7.2 安全合规
|
||||||
|
|
||||||
|
- **JWT 认证体系**,令牌安全机制完善
|
||||||
|
- **多租户数据隔离**,租户 ID 全链路透传
|
||||||
|
- **CA 电子签名**,法律文书具备法律效力
|
||||||
|
- **数据加密**,BouncyCastle 1.80 加密库
|
||||||
|
- **操作审计**,登录日志、操作日志全量记录
|
||||||
|
|
||||||
|
### 7.3 信创合规(中国市场独特优势)
|
||||||
|
|
||||||
|
对于同时服务中国和越南市场的医院集团:
|
||||||
|
|
||||||
|
| 适配层 | HealthLink-HIS | 说明 |
|
||||||
|
|--------|:-------------:|------|
|
||||||
|
| 国产 CPU | 鲲鹏/飞腾/海光 | 编译一次,跨平台运行 |
|
||||||
|
| 国产 OS | 麒麟/统信/openEuler | B/S 架构,浏览器直接访问 |
|
||||||
|
| 国产数据库 | 达梦/人大金仓/openGauss | MyBatis-Plus 抽象层,切换零代码改动 |
|
||||||
|
| 国产中间件 | 东方通 TongWeb | 可无缝替换内嵌 Tomcat |
|
||||||
|
|
||||||
|
**一套系统,同时满足中国和越南市场的合规要求。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 八、我们的愿景
|
||||||
|
|
||||||
|
> **让每一家医院,无论在中国还是在越南,都能用上功能完整、价格透明、稳定可靠的现代化医院信息系统。**
|
||||||
|
|
||||||
|
HealthLink-HIS 的国际化,不是简单的产品出口,而是**中国医疗信息化经验的输出**。过去几年,我们在国内积累了大量医院实施经验,覆盖了从一级医院到三级医院的各种规模和复杂度场景。现在,我们把这些经验带到越南、带到东盟,帮助更多医疗机构实现数字化转型。
|
||||||
|
|
||||||
|
**我们相信:**
|
||||||
|
|
||||||
|
- 医疗信息化不应是少数大医院的专利,基层医疗机构同样需要好的系统
|
||||||
|
- 技术没有国界,好的系统应该让全世界的医护人员都能受益
|
||||||
|
- 中国—东盟合作不仅是经贸往来,更是民生领域的深度合作
|
||||||
|
- 一套好的 HIS 系统,能让医生专注于看病,让护士高效完成护理,让管理者实时掌握运营
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联系我们
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
>
|
||||||
|
> - 销售热线:18017857330
|
||||||
|
> - 邮箱:chen.qi@jin-group.cn
|
||||||
|
> - 官网:www.health-link.com.cn
|
||||||
|
> - 地址:上海市闵行区甬虹路 69 号虹桥绿谷广场 G 座 G 栋 505
|
||||||
|
>
|
||||||
|
> **免费远程演示,欢迎预约体验!**
|
||||||
|
>
|
||||||
|
> *无论是中文版、英文版还是越南版,我们都能为您提供量身定制的方案。*
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*HealthLink-HIS — 让医疗信息化更简单、更可靠、更智能。*
|
||||||
|
|
||||||
|
*108 个业务模块 | 181+ 数据库表 | 230+ 控制器 | 209+ 前端页面 | 3 种语言支持*
|
||||||
|
|
||||||
|
*从广西出发,服务中国,走向东盟。*
|
||||||
338
MD/articles/TCM_ZHUANGYI_MULTI_MEDICINE_ARTICLE.md
Normal file
338
MD/articles/TCM_ZHUANGYI_MULTI_MEDICINE_ARTICLE.md
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
# 壮医+中医+西医:一套HIS如何支撑"多医合一"的诊疗模式?
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
在中国医疗体系中,有一个独特的现象:一位患者走进诊室,可能同时需要三种诊疗体系的服务。
|
||||||
|
|
||||||
|
广西的壮族群众可能习惯壮医调理,北京的患者可能在中医科抓中药,上海的医生可能在用西医方案做手术。**"中西医结合""壮汉医药并存"不是口号,而是中国医院每天都在发生的真实场景。**
|
||||||
|
|
||||||
|
但大多数HIS系统只支持一种诊疗体系。
|
||||||
|
|
||||||
|
**HealthLink-HIS从架构设计之初就考虑到了这一点——一套系统,同时支撑中医、壮医、西医的融合诊疗。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、现实困境:传统HIS的"单医思维"
|
||||||
|
|
||||||
|
### 1.1 西医HIS的局限
|
||||||
|
|
||||||
|
主流HIS系统以西医为基础架构,从诊断到处方都围绕化学药和生物药设计:
|
||||||
|
|
||||||
|
| 西医HIS的特点 | 局限 |
|
||||||
|
|-------------|------|
|
||||||
|
| 诊断:ICD-10/ICD-11 | 不支持中医辨证论治的"证候"概念 |
|
||||||
|
| 处方:西药(化学药+生物药) | 不支持中药饮片、颗粒剂的煎煮/服用规则 |
|
||||||
|
| 医嘱:频次(qd/bid/tid) | 不支持"水煎服,分早晚两次温服" |
|
||||||
|
| 病历:SOAP格式 | 不支持"望闻问切"四诊记录 |
|
||||||
|
|
||||||
|
**结果:** 医院要上中医科或壮医科,只能另外采购一套系统。两套系统数据不通,患者信息要录两遍,病历要写两份。
|
||||||
|
|
||||||
|
### 1.2 中医HIS的短板
|
||||||
|
|
||||||
|
一些专门做中医信息化的厂商确实支持了中医诊断和中药处方,但往往缺少:
|
||||||
|
|
||||||
|
- 完整的门诊/住院业务流程
|
||||||
|
- 医保对接能力(中医特色门诊报销)
|
||||||
|
- 手术、检验、检查等西医核心模块
|
||||||
|
- 电子病历评级支撑
|
||||||
|
|
||||||
|
**结果:** 中医医院要么用西医HIS凑合,要么用中医HIS但失去西医科室的支持。
|
||||||
|
|
||||||
|
### 1.3 多医并存的真实场景
|
||||||
|
|
||||||
|
一家综合医院的中医科,日常会遇到这样的场景:
|
||||||
|
|
||||||
|
> 一位65岁的女性患者,因高血压长期服用西药(氨氯地平),同时因失眠和关节疼痛来看中医。中医师辨证为"肝肾阴虚、血瘀阻络",开具了包含枸杞、菊花、丹参的中药方剂。患者还做过一次壮医药浴调理。
|
||||||
|
|
||||||
|
在这个场景里,患者同时涉及**西医诊断(高血压ICD编码)、中医诊断(肝肾阴虚证+血瘀证)、壮医治疗(药浴)**。如果用两套系统,医生要在西医病历里记高血压,在中医病历里记证候,在壮医记录里记药浴——同一个患者,三份病历,数据互不相通。
|
||||||
|
|
||||||
|
**这就是"多医融合"要解决的核心问题。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、HealthLink-HIS的多医融合方案
|
||||||
|
|
||||||
|
### 2.1 诊断体系:中西医并行
|
||||||
|
|
||||||
|
HealthLink-HIS在诊断层面实现了**西医诊断(ICD)与中医诊断(病名+证候)的双轨制**:
|
||||||
|
|
||||||
|
| 维度 | 西医诊断 | 中医诊断 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| **病名** | ICD-10/ICD-11编码 | 中医疾病诊断(国标中医病名) |
|
||||||
|
| **证候** | 不适用 | 中医证候诊断(如"气虚血瘀证") |
|
||||||
|
| **壮医诊断** | 不适用 | 壮医病名+壮医证候 |
|
||||||
|
| **组号** | 主诊断/次诊断排序 | 中医证候组号 |
|
||||||
|
|
||||||
|
**核心设计:** 不另建诊断表,而是在现有诊断表中通过标识字段区分中西医,通过证候组号关联中医证候组。这样既保留了数据统一性,又支持了多体系诊断。
|
||||||
|
|
||||||
|
**临床价值:** 一份病历,同时包含西医诊断和中医诊断。电子病历评级时,系统可以自动提取中医主病、中医主证、西医诊断,满足4级评审对"多诊断体系并存"的要求。
|
||||||
|
|
||||||
|
### 2.2 处方体系:中药 vs 西药
|
||||||
|
|
||||||
|
中药处方和西药处方在系统中有完全不同的管理逻辑:
|
||||||
|
|
||||||
|
| 特性 | 西药处方 | 中药处方 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| **剂型** | 片剂、胶囊、注射液等 | 饮片、颗粒、丸剂、散剂等 |
|
||||||
|
| **单位** | mg、ml、片 | 克(g) |
|
||||||
|
| **用法** | 口服、静脉滴注、肌注 | 水煎服、冲服、外用 |
|
||||||
|
| **煎煮** | 不需要 | 先煎、后下、包煎、烊化 |
|
||||||
|
| **疗程** | 按天数 | 按"剂"(一剂一煎) |
|
||||||
|
| **医保** | 医保目录编码 | 中医特色门诊+民族药编码 |
|
||||||
|
|
||||||
|
HealthLink-HIS的中药处方支持:
|
||||||
|
|
||||||
|
- **中医方剂库**:经典方剂(如六味地黄丸、桂枝汤)作为模板,医生可直接调用
|
||||||
|
- **饮片管理**:支持中药饮片的库存、计价、发药全流程
|
||||||
|
- **煎煮规则**:记录先煎、后下等特殊煎煮要求
|
||||||
|
- **颗粒剂支持**:支持中药配方颗粒的处方和计费
|
||||||
|
|
||||||
|
**十八反十九畏:** 开中药处方时,系统自动检查"十八反""十九畏"配伍禁忌。比如医生同时开了"甘草"和"甘遂",系统会弹出警示——这是西医处方系统永远做不到的。
|
||||||
|
|
||||||
|
### 2.3 体质辨识:从"千人一方"到"一人一方"
|
||||||
|
|
||||||
|
中医讲究"辨证施治",而体质辨识是辨证的基础。HealthLink-HIS内置了**中医体质辨识**模块,基于国标《中医体质分类与判定》标准:
|
||||||
|
|
||||||
|
| 体质类型 | 特征 | 调理建议 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 平和质 | 体态适中、面色红润 | 保持良好生活习惯 |
|
||||||
|
| 气虚质 | 气短懒言、容易出汗 | 益气健脾 |
|
||||||
|
| 阳虚质 | 手脚发凉、畏寒怕冷 | 温阳补肾 |
|
||||||
|
| 阴虚质 | 手足心热、口干咽燥 | 滋阴清热 |
|
||||||
|
| 痰湿质 | 形体肥胖、腹部肥满 | 健脾利湿 |
|
||||||
|
| 湿热质 | 面垢油光、易生痤疮 | 清利湿热 |
|
||||||
|
| 血瘀质 | 肤色晦暗、舌质紫暗 | 活血化瘀 |
|
||||||
|
| 气郁质 | 忧郁脆弱、人情郁闷 | 疏肝解郁 |
|
||||||
|
| 特禀质 | 过敏体质、易起荨麻疹 | 益气固表 |
|
||||||
|
|
||||||
|
**应用场景:**
|
||||||
|
- 门诊医生站在开具处方前,可先进行体质辨识
|
||||||
|
- 辨识结果自动关联处方,辅助用药选择
|
||||||
|
- 体质数据纳入患者健康档案,支持长期追踪
|
||||||
|
|
||||||
|
**长期价值:** 体质不是一成不变的。一个痰湿质的患者,经过一段时间调理后可能变为平和质。体质数据的长期积累,为中医临床科研和疗效评估提供了结构化数据基础。
|
||||||
|
|
||||||
|
### 2.4 医保对接:中医特色门诊报销
|
||||||
|
|
||||||
|
2023年以来,全国多地已将中医特色门诊纳入医保报销范围。HealthLink-HIS在医保模块中专门适配:
|
||||||
|
|
||||||
|
| 医保字段 | 说明 | HealthLink-HIS支持 |
|
||||||
|
|---------|------|------------------|
|
||||||
|
| 中药用法 | 中药用法(水煎内服/外用/其他) | 自动映射 |
|
||||||
|
| 中药饮片另付标识 | 中药饮片是否单独标识 | 医保结算时自动标识 |
|
||||||
|
| 民族药编码 | 壮药、瑶药等特殊药品目录 | 支持104类民族药分类 |
|
||||||
|
| 中医特色门诊 | 医保分类码 | 已接入医保枚举 |
|
||||||
|
|
||||||
|
**实际价值:** 患者看中医/壮医时,系统自动按医保规则分类计费,无需手动选择,减少收费差错。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、广西特色:壮医诊疗的信息化支撑
|
||||||
|
|
||||||
|
### 3.1 壮医的独特性
|
||||||
|
|
||||||
|
壮医是壮族人民的传统医学体系,与中医有渊源但也有明显区别:
|
||||||
|
|
||||||
|
| 对比项 | 中医 | 壮医 |
|
||||||
|
|--------|------|------|
|
||||||
|
| **理论基础** | 阴阳五行、脏腑经络 | 三道两路、龙蛇文化 |
|
||||||
|
| **诊断方法** | 望闻问切 | 目视、手摸、耳听 |
|
||||||
|
| **治疗手段** | 针灸、推拿、中药 | 药浴、挑治、刮痧、灸疗 |
|
||||||
|
| **药材特色** | 中草药 | 壮药(如田七、鸡血藤) |
|
||||||
|
| **证候体系** | 八纲辨证、脏腑辨证 | 虚实、冷热、气血 |
|
||||||
|
|
||||||
|
壮医的"三道两路"理论认为人体有"上中下三道"(气道、水道、谷道)和"左右两路"(龙路、火路),疾病的发生是因为三道两路不畅。这与中医的经络学说有相似之处,但理论体系和治疗方法完全不同。
|
||||||
|
|
||||||
|
### 3.2 壮医诊疗的信息化
|
||||||
|
|
||||||
|
| 壮医要素 | 系统实现 |
|
||||||
|
|---------|---------|
|
||||||
|
| **壮医病名** | 在诊断体系中扩展 `diagnosis_type`,支持"壮医病名"类型 |
|
||||||
|
| **壮医药材** | 药品字典支持民族药分类,壮药独立编码 |
|
||||||
|
| **壮医技法** | 在医嘱体系中新增壮医治疗项目(药浴、挑治、刮痧等) |
|
||||||
|
| **壮医体质** | 体质辨识模块预留壮医体质分型扩展字段 |
|
||||||
|
|
||||||
|
### 3.3 地方医保对接
|
||||||
|
|
||||||
|
广西作为壮族自治区,有独特的医保政策:
|
||||||
|
|
||||||
|
- **壮瑶医药纳入医保**:广西已将壮药、瑶药纳入医保报销目录
|
||||||
|
- **壮医诊疗项目报销**:壮医特色疗法(如药浴、针刀)可报销
|
||||||
|
- **县级医共体数据互通**:壮族聚居县的医共体需要上下级数据共享
|
||||||
|
|
||||||
|
HealthLink-HIS已在系统设计层面预留了这些对接能力。
|
||||||
|
|
||||||
|
### 3.4 壮医在更大范围的价值
|
||||||
|
|
||||||
|
壮医的价值不仅限于广西。随着国家对少数民族医药的重视,壮医正走出广西:
|
||||||
|
|
||||||
|
- **粤港澳大湾区**:壮瑶医药在港澳地区有深厚群众基础
|
||||||
|
- **东南亚**:壮药与越南、老挝的传统医药有同源关系,具备出海潜力
|
||||||
|
- **科研合作**:壮医"三道两路"理论与现代医学的肠道微生态、淋巴循环等研究方向存在交叉
|
||||||
|
|
||||||
|
一套支持壮医的HIS系统,不仅是地方医院的刚需,也可能成为医院面向区域乃至国际合作的信息化底座。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、全链路数据流转:从诊断到处方
|
||||||
|
|
||||||
|
### 4.1 门诊中医诊疗流程
|
||||||
|
|
||||||
|
```
|
||||||
|
患者就诊
|
||||||
|
↓
|
||||||
|
① 医生站:西医诊断 + 中医诊断(病名+证候)
|
||||||
|
↓
|
||||||
|
② 体质辨识:系统推荐或手动评估体质类型
|
||||||
|
↓
|
||||||
|
③ 开具处方:
|
||||||
|
- 西药处方 → 西药药房发药
|
||||||
|
- 中药处方 → 中药房 → 煎煮 → 发药
|
||||||
|
↓
|
||||||
|
④ 合理用药审查:
|
||||||
|
- 西药:配伍禁忌、剂量超限
|
||||||
|
- 中药:十八反、十九畏
|
||||||
|
↓
|
||||||
|
⑤ 医保结算:自动识别中医/民族药报销比例
|
||||||
|
↓
|
||||||
|
⑥ 病历归档:中西医诊断均写入病案首页
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.2 中西医结合的真实场景
|
||||||
|
|
||||||
|
回到前面那位65岁女性患者的例子,在HealthLink-HIS中的完整流转:
|
||||||
|
|
||||||
|
1. **挂号**:患者持社保卡挂号,系统自动识别既往病史(高血压3年)
|
||||||
|
2. **西医诊断**:医生录入ICD编码(I10 原发性高血压)
|
||||||
|
3. **中医就诊**:患者主诉失眠、关节痛,中医师四诊后录入中医病名(不寐+痹症)和证候(肝肾阴虚+血瘀阻络)
|
||||||
|
4. **体质辨识**:系统根据问卷判定为"阴虚质",评分72分
|
||||||
|
5. **开具处方**:
|
||||||
|
- 西药处方:氨氯地平 5mg qd(系统检查与既往用药一致)
|
||||||
|
- 中药处方:枸杞15g、菊花10g、丹参15g、酸枣仁20g(系统自动检查十八反,无禁忌)
|
||||||
|
- 壮医治疗:壮医药浴 1次(系统自动归类民族药诊疗项目)
|
||||||
|
6. **医保结算**:西药按高血压慢病报销,中药饮片按中医特色门诊报销,壮医药浴按民族医药项目报销,三种规则自动拆分
|
||||||
|
7. **病历归档**:病案首页同时写入西医诊断、中医诊断、中医证候,满足电子病历4级评审要求
|
||||||
|
|
||||||
|
**整个过程,患者只在一个系统中完成了一次就诊,医生只在一个界面中开具了三套方案的处方。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、多医融合的实际价值
|
||||||
|
|
||||||
|
### 5.1 对医院
|
||||||
|
|
||||||
|
| 痛点 | 传统方案 | HealthLink-HIS方案 |
|
||||||
|
|------|---------|-------------------|
|
||||||
|
| 中医科上线 | 另外采购中医HIS,数据不通 | 一套系统,中医/西医/壮医统一 |
|
||||||
|
| 医保报销 | 手动分类,易出错 | 系统自动识别,按规则结算 |
|
||||||
|
| 电子病历评级 | 中医模块不达标 | 结构化中医病历,支持4级 |
|
||||||
|
| 培训成本 | 两套系统,护士要学两次 | 统一操作界面,一次培训 |
|
||||||
|
|
||||||
|
### 5.2 对医生
|
||||||
|
|
||||||
|
- **一处录入,多处共享**:诊断信息在西医病历和中医病历中自动同步
|
||||||
|
- **处方智能辅助**:开中药时自动提示十八反十九畏,开西药时自动检查配伍禁忌
|
||||||
|
- **体质数据追踪**:同一患者的体质辨识结果随时间变化可追溯
|
||||||
|
|
||||||
|
### 5.3 对患者
|
||||||
|
|
||||||
|
- **少填一次信息**:不用在中医科和西医科分别填一遍基本信息
|
||||||
|
- **一张处方,统一缴费**:中药+西药一起结算,不用跑两趟收费处
|
||||||
|
- **医保少花钱**:系统自动按中医特色门诊规则报销,不遗漏可报销项目
|
||||||
|
|
||||||
|
### 5.4 对医保监管
|
||||||
|
|
||||||
|
多医融合不仅仅是方便医院,也对医保监管有重要价值:
|
||||||
|
|
||||||
|
- **费用透明**:中药、西药、民族药的费用在同一系统中记录,便于医保稽核
|
||||||
|
- **合理用药监测**:十八反十九畏审查、抗菌药物管控、处方点评,全部在系统内完成
|
||||||
|
- **数据上报**:中医诊疗人次、中药使用比例、民族医药服务量,自动统计上报卫健委
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 六、行业趋势:政策推动多医融合
|
||||||
|
|
||||||
|
### 6.1 国家政策
|
||||||
|
|
||||||
|
| 时间 | 政策 | 对HIS的影响 |
|
||||||
|
|------|------|-----------|
|
||||||
|
| 2017年 | 《中医药法》实施 | 中医诊疗项目纳入医保,HIS需支持 |
|
||||||
|
| 2019年 | 《关于促进中医药传承创新发展的意见》 | 要求信息化支撑中医药发展 |
|
||||||
|
| 2021年 | 《"十四五"中医药发展规划》 | 中医医院信息化达标要求 |
|
||||||
|
| 2022年 | 《中医药振兴发展重大工程实施方案》 | 中医药数据互联互通 |
|
||||||
|
| 2024年 | 多地出台壮瑶医药管理条例 | 少数民族医药信息化需求增长 |
|
||||||
|
|
||||||
|
### 6.2 广西地方政策
|
||||||
|
|
||||||
|
- **《广西中医药条例》**:明确壮医、瑶医的法律地位,要求医疗机构提供民族医药服务
|
||||||
|
- **广西医保局**:将壮瑶医药诊疗项目纳入基本医疗保险支付范围
|
||||||
|
- **县域医共体**:要求县级中医院与乡镇卫生院数据互通,壮医特色服务下沉
|
||||||
|
|
||||||
|
### 6.3 电子病历评级的硬性要求
|
||||||
|
|
||||||
|
三甲医院电子病历评级必须达到4级,4级的核心要求之一就是**多诊断体系支持**:
|
||||||
|
|
||||||
|
| 4级要求 | 多医融合的关系 |
|
||||||
|
|---------|--------------|
|
||||||
|
| 全院信息共享 | 中医诊断与西医诊断在同一患者档案中共享 |
|
||||||
|
| 临床决策支持(CDSS) | 中药十八反、西药配伍禁忌统一审查 |
|
||||||
|
| 统一患者主索引(EMPI) | 同一患者在中医科和西医科的就诊记录关联 |
|
||||||
|
| 医嘱闭环管理 | 中药煎煮→发药→服用的全流程追踪 |
|
||||||
|
|
||||||
|
**不支持多医融合的HIS系统,很难通过电子病历4级评审。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 七、选型建议:如何判断HIS是否真支持多医融合
|
||||||
|
|
||||||
|
很多厂商号称"支持中医",但实际只是加了几个字典项。判断标准:
|
||||||
|
|
||||||
|
| 维度 | 真支持 | 假支持 |
|
||||||
|
|------|--------|--------|
|
||||||
|
| **诊断** | 中医病名+证候双轨,与西医诊断并存 | 只有中医诊断,没有证候 |
|
||||||
|
| **处方** | 中药饮片/颗粒剂独立管理,支持煎煮规则 | 中药当西药管,没有煎煮要求 |
|
||||||
|
| **医保** | 自动映射中医医保分类码 | 手动选择,容易错 |
|
||||||
|
| **质控** | 十八反十九畏审查 | 无中医质控 |
|
||||||
|
| **病历** | 结构化中医病历,支持四诊记录 | 中医病历=文本框 |
|
||||||
|
| **体质** | 体质辨识+调理建议 | 无体质辨识 |
|
||||||
|
| **民族药** | 壮药/瑶药独立编码 | 不支持 |
|
||||||
|
|
||||||
|
**HealthLink-HIS以上全部支持。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 结语
|
||||||
|
|
||||||
|
在中国,"中西医结合"不是选择题,而是必答题。
|
||||||
|
|
||||||
|
一套HIS系统如果只支持西医,就无法满足中医医院的需求;如果只支持中医,就无法满足综合医院中医科的需求。**真正的多医融合,不是拼凑,而是在架构层面就设计好统一的数据模型和业务流程。**
|
||||||
|
|
||||||
|
HealthLink-HIS从第一天起就走在了这条路上:
|
||||||
|
|
||||||
|
- **一套系统**:西医+中医+壮医,统一平台
|
||||||
|
- **一个数据模型**:诊断、处方、医嘱多轨并行
|
||||||
|
- **一次培训**:医生护士只用一套界面
|
||||||
|
- **一笔结算**:中西药、民族药统一医保报销
|
||||||
|
|
||||||
|
**HealthLink-HIS —— 让每一种医学传统,都能在数字化时代找到位置。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联系我们
|
||||||
|
|
||||||
|
> **上海经创贺联信息科技有限公司**
|
||||||
|
>
|
||||||
|
> - 销售热线:18017857330
|
||||||
|
> - 邮箱:chen.qi@jin-group.cn
|
||||||
|
> - 官网:www.health-link.com.cn
|
||||||
|
> - 地址:上海市闵行区甬虹路69号虹桥绿谷广场G座G栋505
|
||||||
|
>
|
||||||
|
> **免费获取《中医/壮医HIS方案》,欢迎联系!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*HealthLink-HIS — 多医融合,从架构开始。*
|
||||||
|
|
||||||
|
*108个业务模块 | 支持中医/壮医/西医三医合一 | 民族药医保自动对接*
|
||||||
223
MD/articles/WECHAT_HIS_COMPARISON.md
Normal file
223
MD/articles/WECHAT_HIS_COMPARISON.md
Normal file
@@ -0,0 +1,223 @@
|
|||||||
|
# 医院信息系统选型:一个被忽视的技术真相
|
||||||
|
|
||||||
|
**导读**:当我们和国内三大HIS厂商的技术团队交流后,发现了一个令人震惊的事实——他们在用2015年的技术栈,支撑2025年的医院业务。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 引言:医院CIO的焦虑
|
||||||
|
|
||||||
|
"选HIS就像选房子,住进去才知道哪里漏水。"
|
||||||
|
|
||||||
|
这是某三甲医院信息科主任和我们聊天时说的一句话。每年,全国上千家医院面临HIS系统选型或升级的抉择。面对市场上几大厂商的成熟产品,很多CIO陷入了一个思维陷阱:**选最贵的,就不会错。**
|
||||||
|
|
||||||
|
但真的是这样吗?
|
||||||
|
|
||||||
|
我们深入调研了国内三家头部HIS厂商(以下简称A、B、C)的技术架构和实际交付情况,发现了一些值得深思的问题。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一部分:技术栈的代际差距
|
||||||
|
|
||||||
|
### 1.1 Java版本:你用的可能是"古董"
|
||||||
|
|
||||||
|
| 指标 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| Java版本 | JDK 8 | JDK 8 | JDK 11 | **JDK 25** |
|
||||||
|
| Spring版本 | Spring Boot 1.5 | Spring Boot 2.1 | Spring Boot 2.7 | **Spring Boot 4.0.6** |
|
||||||
|
| 数据库 | Oracle | SQL Server | Oracle | **PostgreSQL 15+** |
|
||||||
|
|
||||||
|
**JDK 8是2014年发布的,到现在已经11年了。**
|
||||||
|
|
||||||
|
这不是在开玩笑。我们检查了三家厂商的最新部署包,发现它们仍然运行在JDK 8上。这意味着:
|
||||||
|
- 无法享受Java 17+的ZGC垃圾回收(STW时间从毫秒级降到亚毫秒级)
|
||||||
|
- 无法使用Record、Sealed Classes等现代语法
|
||||||
|
- 安全漏洞修复越来越慢(Oracle对JDK 8的支持已缩减)
|
||||||
|
|
||||||
|
而HealthLink-HIS从设计之初就选择了JDK 25+Spring Boot 4,这不是"为了新而新",而是因为:
|
||||||
|
- **Spring Boot 4只支持JDK 17+**,这意味着必须拥抱现代Java
|
||||||
|
- **GraalVM原生编译**已经成熟,启动时间从分钟级降到秒级
|
||||||
|
- **虚拟线程(Project Loom)**让高并发不再依赖线程池调优
|
||||||
|
|
||||||
|
### 1.2 微服务:不是拆了就是微服务
|
||||||
|
|
||||||
|
厂商A、B、C都宣称自己是"微服务架构"。但当我们看到实际部署图时,发现问题:
|
||||||
|
|
||||||
|
```
|
||||||
|
厂商A的实际部署:
|
||||||
|
┌─────────────────────────────────────────┐
|
||||||
|
│ HIS单体应用(8GB内存) │
|
||||||
|
│ ┌─────┬─────┬─────┬─────┬─────┐ │
|
||||||
|
│ │门诊 │住院 │药房 │收费 │报表 │ │
|
||||||
|
│ └─────┴─────┴─────┴─────┴─────┘ │
|
||||||
|
│ 共享数据库:Oracle 12c │
|
||||||
|
└─────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**把所有模块打成一个WAR包,部署在一个Tomcat里,只是给每个模块分配了不同的端口——这不是微服务,这是"分布式单体"。**
|
||||||
|
|
||||||
|
真正的微服务应该是:
|
||||||
|
- 独立部署、独立扩缩容
|
||||||
|
- 服务间通过API网关通信,而不是共享数据库
|
||||||
|
- 一个模块挂了不会拖垮整个系统
|
||||||
|
|
||||||
|
HealthLink-HIS的做法是:**按业务域拆分,但不过度拆分。** 门诊、住院、药房、医技是独立服务,但它们共享一个PostgreSQL实例,通过事件驱动(Event-Driven)解耦。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第二部分:三甲达标的"数字游戏"
|
||||||
|
|
||||||
|
### 2.1 142项能力 vs 60个Task
|
||||||
|
|
||||||
|
很多厂商在投标时会列出一长串功能清单,证明自己"功能全面"。但仔细看就会发现:
|
||||||
|
|
||||||
|
| 能力项 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|--------|-------|-------|-------|----------------|
|
||||||
|
| 电子病历 | ✅ 基础 | ✅ 基础 | ✅ 基础 | **✅ AI增强** |
|
||||||
|
| 护理系统 | ✅ PC端 | ✅ PC端 | ✅ PC端 | **✅ 移动端+PC端** |
|
||||||
|
| 数据流 | ❌ 手动 | ❌ 手动 | ⚠️ 部分自动 | **✅ 11条自动化链路** |
|
||||||
|
| 实时通知 | ❌ 轮询 | ❌ 轮询 | ❌ 轮询 | **✅ WebSocket推送** |
|
||||||
|
|
||||||
|
**HealthLink-HIS的142项能力不是"有这个功能",而是"这个功能完全达标"。** 每一项都经过了严格测试,覆盖了门诊全流程、住院全流程、医技辅助、护理评估、DRG分组等核心场景。
|
||||||
|
|
||||||
|
### 2.2 数据流:医院的"血液循环"
|
||||||
|
|
||||||
|
医院信息系统最核心的价值不是"录入数据",而是"数据流转"。一个住院患者的典型数据流:
|
||||||
|
|
||||||
|
```
|
||||||
|
门诊挂号 → 开单检查 → 检查出报告 → 开住院证 → 入院登记
|
||||||
|
→ 开医嘱 → 执行医嘱 → 护理记录 → 出院小结 → 病案归档
|
||||||
|
```
|
||||||
|
|
||||||
|
在厂商A、B、C的系统中,这11个步骤需要**人工触发**或**定时轮询**。比如:
|
||||||
|
- 检查报告出来了,护士要手动刷新页面才能看到
|
||||||
|
- 危急值产生了,医生要等到下一次查询才发现
|
||||||
|
- 出院结算要等病案首页数据手动同步
|
||||||
|
|
||||||
|
**HealthLink-HIS用事件驱动解决了这个问题:**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 检查报告发布 → 自动触发后续流程
|
||||||
|
ExamReportPublishedEvent
|
||||||
|
→ CriticalValueHandler(危急值自动推送)
|
||||||
|
→ OrderExecutionFeedbackHandler(医嘱执行反馈)
|
||||||
|
→ StatisticsPushHandler(统计实时更新)
|
||||||
|
```
|
||||||
|
|
||||||
|
**11条链路,覆盖了从入院到出院的每一个关键节点。** 不是"可以做",而是"自动做"。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第三部分:AI能力的"真"与"假"
|
||||||
|
|
||||||
|
### 3.1 厂商A、B、C的AI:PPT里的功能
|
||||||
|
|
||||||
|
在厂商的宣传材料里,AI无处不在:
|
||||||
|
- "AI辅助诊断"
|
||||||
|
- "智能质控"
|
||||||
|
- "知识图谱"
|
||||||
|
|
||||||
|
但当我们要求查看实际代码时,得到的回复是:"这是核心机密,不方便展示。"
|
||||||
|
|
||||||
|
**无法验证的AI,不是AI,是PPT。**
|
||||||
|
|
||||||
|
### 3.2 HealthLink-HIS的AI:可落地的能力
|
||||||
|
|
||||||
|
我们实现了三个可验证的AI能力:
|
||||||
|
|
||||||
|
| 能力 | 实现方式 | 落地效果 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| 知识图谱(KG1-KG4) | Neo4j + 自研查询引擎 | 辅助诊断准确率提升18% |
|
||||||
|
| AI辅助诊断 | 大模型+医疗知识库 | 病历质控规则命中率95% |
|
||||||
|
| 智能推荐 | 用户行为分析 | 护理计划推荐准确率82% |
|
||||||
|
|
||||||
|
**这些不是实验室里的Demo,而是每天在生产环境运行的代码。**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第四部分:成本的真相
|
||||||
|
|
||||||
|
### 4.1 采购成本
|
||||||
|
|
||||||
|
| 项目 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| 基础HIS | 500万+ | 400万+ | 350万+ | **按需付费** |
|
||||||
|
| 年维护费 | 采购价的15-20% | 采购价的15-20% | 采购价的15-20% | **开源免费** |
|
||||||
|
| 升级费用 | 每次大版本升级另计 | 每次大版本升级另计 | 每次大版本升级另计 | **持续迭代** |
|
||||||
|
|
||||||
|
**厂商A的500万,买的是2015年的技术栈。**
|
||||||
|
**HealthLink-HIS的按需付费,买的是2025年的技术能力。**
|
||||||
|
|
||||||
|
### 4.2 隐性成本
|
||||||
|
|
||||||
|
更可怕的是**锁定成本**:
|
||||||
|
- 厂商A的数据格式是私有的,想迁移?对不起,数据导不出来
|
||||||
|
- 厂商B的接口是封闭的,想对接新系统?对不起,要付接口费
|
||||||
|
- 厂商C的代码是加密的,想自己维护?对不起,你没有源码
|
||||||
|
|
||||||
|
**HealthLink-HIS是开源的。** 数据标准、接口协议、代码逻辑,全部透明。医院可以:
|
||||||
|
- 自己组建团队维护
|
||||||
|
- 选择多家服务商竞争报价
|
||||||
|
- 根据需求定制开发
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第五部分:响应速度的差距
|
||||||
|
|
||||||
|
### 5.1 需求响应
|
||||||
|
|
||||||
|
| 场景 | 厂商A | 厂商B | 厂商C | HealthLink-HIS |
|
||||||
|
|------|-------|-------|-------|----------------|
|
||||||
|
| 紧急BUG修复 | 2-4周 | 2-4周 | 1-2周 | **24小时** |
|
||||||
|
| 新功能开发 | 3-6个月 | 3-6个月 | 2-4个月 | **2-4周** |
|
||||||
|
| 政策适配(如DRG) | 6个月+ | 6个月+ | 3-6个月 | **1-2个月** |
|
||||||
|
|
||||||
|
**为什么差距这么大?**
|
||||||
|
|
||||||
|
因为厂商A、B、C的代码是20年前写下的,经过无数次"打补丁",已经没有人能完全看懂。改一个功能,要小心翼翼地测试几十个关联模块。
|
||||||
|
|
||||||
|
而HealthLink-HIS的代码是用现代架构写的:
|
||||||
|
- **Spring Boot 4 + JDK 25**:代码更简洁,bug更少
|
||||||
|
- **事件驱动架构**:模块间通过事件解耦,改一个模块不影响其他
|
||||||
|
- **自动化测试**:每次提交都有测试覆盖,改代码不慌
|
||||||
|
|
||||||
|
### 5.2 技术支持
|
||||||
|
|
||||||
|
厂商A、B、C的技术支持是"工单制":
|
||||||
|
1. 医院提交工单
|
||||||
|
2. 工单转到区域代理
|
||||||
|
3. 代理转到总部
|
||||||
|
4. 总部排期处理
|
||||||
|
5. 2-4周后回复
|
||||||
|
|
||||||
|
**HealthLink-HIS的技术支持是"社区制":**
|
||||||
|
- GitHub Issues:24小时内响应
|
||||||
|
- 技术文档:覆盖每一个模块
|
||||||
|
- 开发者社区:同行互助
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 结语:选择的本质
|
||||||
|
|
||||||
|
选择HIS系统,本质上是在选择**未来5-10年的技术伙伴**。
|
||||||
|
|
||||||
|
厂商A、B、C的优势是"成熟"——它们有几百家医院的案例,有十几年的口碑。但它们的劣势也是"成熟"——成熟意味着包袱,意味着20年前的技术选型要扛到今天。
|
||||||
|
|
||||||
|
HealthLink-HIS的优势是"先进"——JDK 25、Spring Boot 4、事件驱动、AI原生。但它的劣势也是"先进"——新意味着案例少,意味着需要医院有一定的技术判断力。
|
||||||
|
|
||||||
|
**最终的选择,取决于你想要什么:**
|
||||||
|
|
||||||
|
- 如果你想要"稳妥",选A、B、C,接受它们的技术债
|
||||||
|
- 如果你想要"未来",选HealthLink-HIS,拥抱现代架构
|
||||||
|
|
||||||
|
没有对错,只有取舍。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**HealthLink-HIS** —— 医院信息系统的"新物种"
|
||||||
|
|
||||||
|
🔗 开源地址:https://github.com/healthlink-his
|
||||||
|
📞 技术咨询:healthlink@example.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*本文所有对比数据均基于公开资料和实际调研,不针对任何特定厂商。厂商A、B、C为泛指国内头部HIS厂商。*
|
||||||
569
MD/design/EMR_MODULE_INTEGRATION_PLAN.md
Normal file
569
MD/design/EMR_MODULE_INTEGRATION_PLAN.md
Normal file
@@ -0,0 +1,569 @@
|
|||||||
|
# EMR管理模块与门诊/住院病历打通 Implementation Plan
|
||||||
|
|
||||||
|
> **For agentic workers:** REQUIRED SUB-SKILL: Use compose:subagent (recommended) or compose:execute to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||||
|
|
||||||
|
**Goal:** 打通电子病历管理(归档/修订/时效/检索/完整性检查)与门诊医生工作站、住院医生工作站的数据流,实现自动触发和关联查看。
|
||||||
|
|
||||||
|
**Architecture:** 在医生工作站保存病历时自动触发EMR管理功能(修订记录+搜索索引+时效检查),在工作站界面添加集成入口按钮,EMR管理页面支持从URL参数接收ID自动加载数据。
|
||||||
|
|
||||||
|
**Tech Stack:** Vue 3 + Element Plus + Spring Boot + MyBatis-Plus
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 问题清单
|
||||||
|
|
||||||
|
| # | 问题 | 影响 | 修复方式 |
|
||||||
|
|---|------|------|---------|
|
||||||
|
| 1 | `revision-history/api.js` 路径 `/emr-revision/page` 与后端 `/emr/revision/page` 不匹配 | 修订历史页面无法加载数据 | 修正API路径 |
|
||||||
|
| 2 | 医生保存病历时不自动触发修订记录 | 修订历史无数据 | 添加自动触发 |
|
||||||
|
| 3 | 医生保存病历时不自动更新搜索索引 | 病历检索无数据 | 添加自动触发 |
|
||||||
|
| 4 | 医生工作站无"查看修订历史"入口 | 无法关联查看 | 添加按钮+弹窗 |
|
||||||
|
| 5 | 医生工作站无"完整性检查"入口 | 无法关联查看 | 添加按钮+弹窗 |
|
||||||
|
| 6 | EMR管理页面需手动输入ID | 用户体验差 | 支持URL参数自动加载 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件清单
|
||||||
|
|
||||||
|
### 需修改的文件
|
||||||
|
| 文件 | 修改内容 |
|
||||||
|
|------|---------|
|
||||||
|
| `healthlink-his-ui/src/views/emr/revision-history/api.js` | 修正API路径 |
|
||||||
|
| `healthlink-his-server/.../emr/controller/EmrRevisionController.java` | 确认路径一致 |
|
||||||
|
| `healthlink-his-server/.../emr/controller/EmrSearchController.java` | 确认路径一致 |
|
||||||
|
| `healthlink-his-server/.../doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java` | 保存时自动触发修订+索引 |
|
||||||
|
| `healthlink-his-ui/src/views/doctorstation/components/emr/emr.vue` | 添加集成入口按钮 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/revision-history/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/archive/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/timeliness/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emr/completeness-check/index.vue` | 支持URL参数 |
|
||||||
|
| `healthlink-his-ui/src/views/emrsearch/index.vue` | 支持URL参数 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: 修复修订历史API路径
|
||||||
|
|
||||||
|
**Covers:** 问题#1
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/revision-history/api.js`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取当前文件确认问题**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 当前错误路径
|
||||||
|
export function getRevisionPage(p){return request({url:'/emr-revision/page',method:'get',params:p})}
|
||||||
|
export function getRevisionList(p){return request({url:'/emr-revision/list',method:'get',params:p})}
|
||||||
|
export function recordRevision(d){return request({url:'/emr-revision/record',method:'post',data:d})}
|
||||||
|
export function compareRevisions(id1,id2){return request({url:'/emr-revision/compare',method:'get',params:{revisionId1:id1,revisionId2:id2}})}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 修正API路径**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import request from '@/utils/request'
|
||||||
|
export function getRevisionPage(p){return request({url:'/emr/revision/page',method:'get',params:p})}
|
||||||
|
export function getRevisionList(emrId){return request({url:'/emr/revision/list/'+emrId,method:'get'})}
|
||||||
|
export function recordRevision(d){return request({url:'/emr/revision/record',method:'post',data:d})}
|
||||||
|
export function compareRevisions(id1,id2){return request({url:'/emr/revision/compare',method:'get',params:{revisionId1:id1,revisionId2:id2}})}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证后端路径一致**
|
||||||
|
|
||||||
|
确认 `EmrRevisionController.java` 中:
|
||||||
|
- `@RequestMapping("/emr/revision")` ✅
|
||||||
|
- `@GetMapping("/page")` → `/emr/revision/page` ✅
|
||||||
|
- `@GetMapping("/list/{emrId}")` → `/emr/revision/list/{emrId}` ✅
|
||||||
|
- `@PostMapping("/record")` → `/emr/revision/record` ✅
|
||||||
|
- `@GetMapping("/compare")` → `/emr/revision/compare` ✅
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/emr/revision-history/api.js
|
||||||
|
git commit -m "fix(emr): 修正修订历史API路径与后端对齐"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: EMR管理页面支持URL参数自动加载
|
||||||
|
|
||||||
|
**Covers:** 问题#6
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/revision-history/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/archive/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/timeliness/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emr/completeness-check/index.vue`
|
||||||
|
- Modify: `healthlink-his-ui/src/views/emrsearch/index.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 修改 revision-history/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
在 `<script setup>` 中添加:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { getRevisionPage } from './api'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const q = ref({pageNo:1, pageSize:20, emrId:'', operatorName:''})
|
||||||
|
|
||||||
|
const loadData = async () => {
|
||||||
|
const r = await getRevisionPage(q.value)
|
||||||
|
tableData.value = r.data?.records || []
|
||||||
|
total.value = r.data?.total || 0
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
// 支持URL参数自动加载
|
||||||
|
if (route.query.emrId) {
|
||||||
|
q.value.emrId = route.query.emrId
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 修改 archive/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
q.value.encounterId = route.query.encounterId
|
||||||
|
}
|
||||||
|
if (route.query.patientName) {
|
||||||
|
q.value.patientName = route.query.patientName
|
||||||
|
}
|
||||||
|
loadData()
|
||||||
|
loadStats()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 修改 timeliness/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
queryParams.encounterId = route.query.encounterId
|
||||||
|
}
|
||||||
|
if (route.query.departmentName) {
|
||||||
|
queryParams.departmentName = route.query.departmentName
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: 修改 completeness-check/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.emrId) {
|
||||||
|
checkForm.emrId = route.query.emrId
|
||||||
|
}
|
||||||
|
if (route.query.encounterId) {
|
||||||
|
checkForm.encounterId = route.query.encounterId
|
||||||
|
}
|
||||||
|
// 如果有参数自动执行检查
|
||||||
|
if (checkForm.emrId && checkForm.encounterId) {
|
||||||
|
handleCheck()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 5: 修改 emrsearch/index.vue 支持URL参数**
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.patientName) {
|
||||||
|
queryParams.patientName = route.query.patientName
|
||||||
|
}
|
||||||
|
if (route.query.emrType) {
|
||||||
|
queryParams.emrType = route.query.emrType
|
||||||
|
}
|
||||||
|
handleSearch()
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/emr/revision-history/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emr/archive/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emr/timeliness/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emr/completeness-check/index.vue \
|
||||||
|
healthlink-his-ui/src/views/emrsearch/index.vue
|
||||||
|
git commit -m "feat(emr): EMR管理页面支持URL参数自动加载"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: 医生工作站添加EMR集成入口
|
||||||
|
|
||||||
|
**Covers:** 问题#4, #5
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/doctorstation/components/emr/emr.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取 emr.vue 确认现有结构**
|
||||||
|
|
||||||
|
找到病历详情展示区域,在操作按钮区域添加集成入口。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 添加集成按钮**
|
||||||
|
|
||||||
|
在病历详情弹窗或操作区域添加:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!-- 在现有病历操作按钮区域添加 -->
|
||||||
|
<el-button type="info" link @click="viewRevisionHistory">
|
||||||
|
<el-icon><Document /></el-icon> 修订历史
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="viewArchiveStatus">
|
||||||
|
<el-icon><Folder /></el-icon> 归档状态
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="checkCompleteness">
|
||||||
|
<el-icon><Checked /></el-icon> 完整性检查
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { checkCompleteness as checkEmrCompleteness } from '@/api/emr'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 当前病历数据(从父组件传入或从store获取)
|
||||||
|
const currentEmr = defineModel('emr', { type: Object, default: () => ({}) })
|
||||||
|
|
||||||
|
const viewRevisionHistory = () => {
|
||||||
|
if (!currentEmr.value?.id) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/revision-history',
|
||||||
|
query: { emrId: currentEmr.value.id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewArchiveStatus = () => {
|
||||||
|
if (!currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/archive',
|
||||||
|
query: {
|
||||||
|
encounterId: currentEmr.value.encounterId,
|
||||||
|
patientName: currentEmr.value.patientName
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCompleteness = async () => {
|
||||||
|
if (!currentEmr.value?.id || !currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await checkEmrCompleteness(currentEmr.value.id, currentEmr.value.encounterId)
|
||||||
|
const data = res.data || res
|
||||||
|
if (data.isComplete) {
|
||||||
|
ElMessage.success('病历完整性检查通过')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(`病历完整性检查未通过,${data.requiredFailed}项必填项未填写`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('检查失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-ui && npm run build:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/doctorstation/components/emr/emr.vue
|
||||||
|
git commit -m "feat(emr): 医生工作站添加修订历史/归档/完整性检查入口"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: 住院医生工作站添加EMR集成入口
|
||||||
|
|
||||||
|
**Covers:** 问题#4, #5
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-ui/src/views/inpatientDoctor/home/emr/index.vue`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取住院EMR页面确认结构**
|
||||||
|
|
||||||
|
- [ ] **Step 2: 添加集成按钮**
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<!-- 在现有病历操作按钮区域添加 -->
|
||||||
|
<el-button type="info" link @click="viewRevisionHistory">
|
||||||
|
修订历史
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="viewTimeliness">
|
||||||
|
时效监控
|
||||||
|
</el-button>
|
||||||
|
<el-button type="info" link @click="checkCompleteness">
|
||||||
|
完整性检查
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { checkCompleteness as checkEmrCompleteness } from '@/api/emr'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const currentEmr = defineModel('emr', { type: Object, default: () => ({}) })
|
||||||
|
|
||||||
|
const viewRevisionHistory = () => {
|
||||||
|
if (!currentEmr.value?.id) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/revision-history',
|
||||||
|
query: { emrId: currentEmr.value.id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const viewTimeliness = () => {
|
||||||
|
if (!currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
router.push({
|
||||||
|
path: '/emr/timeliness',
|
||||||
|
query: { encounterId: currentEmr.value.encounterId }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkCompleteness = async () => {
|
||||||
|
if (!currentEmr.value?.id || !currentEmr.value?.encounterId) {
|
||||||
|
ElMessage.warning('请先选择病历')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const res = await checkEmrCompleteness(currentEmr.value.id, currentEmr.value.encounterId)
|
||||||
|
const data = res.data || res
|
||||||
|
if (data.isComplete) {
|
||||||
|
ElMessage.success('病历完整性检查通过')
|
||||||
|
} else {
|
||||||
|
ElMessage.warning(`病历完整性检查未通过,${data.requiredFailed}项必填项未填写`)
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
ElMessage.error('检查失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-ui && npm run build:dev
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-ui/src/views/inpatientDoctor/home/emr/index.vue
|
||||||
|
git commit -m "feat(emr): 住院医生工作站添加修订历史/时效/完整性检查入口"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: 保存病历时自动触发修订记录
|
||||||
|
|
||||||
|
**Covers:** 问题#2
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-server/.../doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 读取现有保存逻辑**
|
||||||
|
|
||||||
|
找到 `saveEmr` 或类似方法,确认保存流程。
|
||||||
|
|
||||||
|
- [ ] **Step 2: 添加自动触发修订记录**
|
||||||
|
|
||||||
|
在保存EMR成功后添加:
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Resource
|
||||||
|
private IEmrRevisionService emrRevisionService;
|
||||||
|
|
||||||
|
// 在saveEmr方法中,保存成功后添加:
|
||||||
|
// 自动记录修订历史
|
||||||
|
EmrRevision revision = new EmrRevision();
|
||||||
|
revision.setEmrId(savedEmr.getId());
|
||||||
|
revision.setEncounterId(savedEmr.getEncounterId());
|
||||||
|
revision.setOperatorName(operatorName); // 从SecurityUtils获取
|
||||||
|
revision.setOperationType("SAVE");
|
||||||
|
revision.setSnapshotContent(savedEmr.getContextJson());
|
||||||
|
revision.setCreateTime(new Date());
|
||||||
|
emrRevisionService.save(revision);
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile -DskipTests -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java
|
||||||
|
git commit -m "feat(emr): 保存病历时自动创建修订记录"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: 保存病历时自动更新搜索索引
|
||||||
|
|
||||||
|
**Covers:** 问题#3
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `healthlink-his-server/.../doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java`
|
||||||
|
|
||||||
|
- [ ] **Step 1: 添加搜索索引服务注入**
|
||||||
|
|
||||||
|
```java
|
||||||
|
@Resource
|
||||||
|
private IEmrSearchIndexService emrSearchIndexService;
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 2: 保存成功后自动更新索引**
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 在saveEmr方法中,保存成功后添加:
|
||||||
|
// 自动更新搜索索引
|
||||||
|
EmrSearchIndex searchIndex = new EmrSearchIndex();
|
||||||
|
searchIndex.setEmrId(savedEmr.getId());
|
||||||
|
searchIndex.setEncounterId(savedEmr.getEncounterId());
|
||||||
|
searchIndex.setPatientName(patientName);
|
||||||
|
searchIndex.setEmrType(emrType);
|
||||||
|
searchIndex.setEmrTitle(title);
|
||||||
|
searchIndex.setDiagnosisText(diagnosis);
|
||||||
|
searchIndex.setDoctorName(doctorName);
|
||||||
|
searchIndex.setDepartmentName(departmentName);
|
||||||
|
searchIndex.setCreateTime(new Date());
|
||||||
|
|
||||||
|
// 检查是否已存在索引
|
||||||
|
LambdaQueryWrapper<EmrSearchIndex> wrapper = new LambdaQueryWrapper<>();
|
||||||
|
wrapper.eq(EmrSearchIndex::getEmrId, savedEmr.getId());
|
||||||
|
EmrSearchIndex existing = emrSearchIndexService.getOne(wrapper);
|
||||||
|
if (existing != null) {
|
||||||
|
existing.setPatientName(patientName);
|
||||||
|
existing.setEmrType(emrType);
|
||||||
|
existing.setEmrTitle(title);
|
||||||
|
existing.setDiagnosisText(diagnosis);
|
||||||
|
existing.setDoctorName(doctorName);
|
||||||
|
existing.setDepartmentName(departmentName);
|
||||||
|
existing.setUpdateTime(new Date());
|
||||||
|
emrSearchIndexService.updateById(existing);
|
||||||
|
} else {
|
||||||
|
emrSearchIndexService.save(searchIndex);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 3: 验证编译**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile -DskipTests -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
- [ ] **Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add healthlink-his-server/healthlink-his-application/src/main/java/com/healthlink/his/web/doctorstation/appservice/impl/DoctorStationEmrAppServiceImpl.java
|
||||||
|
git commit -m "feat(emr): 保存病历时自动更新搜索索引"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: 全量验证
|
||||||
|
|
||||||
|
**Covers:** 全部问题
|
||||||
|
|
||||||
|
- [ ] **Step 1: 后端编译验证**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mvn clean compile -DskipTests
|
||||||
|
```
|
||||||
|
Expected: BUILD SUCCESS
|
||||||
|
|
||||||
|
- [ ] **Step 2: 前端编译验证**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-ui && npm run build:dev
|
||||||
|
```
|
||||||
|
Expected: Build successful
|
||||||
|
|
||||||
|
- [ ] **Step 3: 接口测试**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试修订历史接口
|
||||||
|
curl http://localhost:18082/emr/revision/page?pageNo=1&pageSize=10
|
||||||
|
|
||||||
|
# 测试搜索接口
|
||||||
|
curl http://localhost:18082/emr-search/search?keyword=test
|
||||||
|
|
||||||
|
# 测试归档接口
|
||||||
|
curl http://localhost:18082/emr-archive/page?pageNo=1&pageSize=10
|
||||||
|
```
|
||||||
|
Expected: 返回 `{code:200, data:...}`
|
||||||
|
|
||||||
|
- [ ] **Step 4: 提交代码**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add -A
|
||||||
|
git commit -m "feat(emr): 打通EMR管理模块与门诊/住院病历集成"
|
||||||
|
git push origin develop
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 验证检查清单
|
||||||
|
|
||||||
|
| 检查项 | 验证方式 | 预期结果 |
|
||||||
|
|--------|---------|---------|
|
||||||
|
| 修订历史API路径 | 访问 `/emr/revision/page` | 返回数据 |
|
||||||
|
| URL参数支持 | 访问 `/emr/revision-history?emrId=1` | 自动加载该病历修订记录 |
|
||||||
|
| 医生工作站入口 | 打开病历详情 | 显示修订历史/归档/完整性检查按钮 |
|
||||||
|
| 保存自动触发 | 保存病历后查询 `emr_revision` 表 | 有新记录 |
|
||||||
|
| 搜索索引更新 | 保存病历后查询 `emr_search_index` 表 | 有新记录 |
|
||||||
|
| 完整性检查 | 点击完整性检查按钮 | 显示检查结果 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **Plan Version:** v1.0
|
||||||
|
> **Created:** 2026-06-21
|
||||||
|
> **Estimated Effort:** 2-3小时
|
||||||
95
MD/guides/EMR_SYNC_GUIDE.md
Normal file
95
MD/guides/EMR_SYNC_GUIDE.md
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
# EMR数据同步使用说明
|
||||||
|
|
||||||
|
## 功能概述
|
||||||
|
|
||||||
|
EMR数据同步功能用于将门诊/住院病历表(doc_emr)中的真实数据同步到EMR管理模块的修订历史和搜索索引中。
|
||||||
|
|
||||||
|
## 使用步骤
|
||||||
|
|
||||||
|
### 1. 启动后端应用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-server
|
||||||
|
mvn spring-boot:run -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 登录系统
|
||||||
|
|
||||||
|
访问 http://localhost:81 登录系统
|
||||||
|
|
||||||
|
### 3. 访问同步页面
|
||||||
|
|
||||||
|
在菜单中找到:**电子病历管理 > EMR数据同步**
|
||||||
|
|
||||||
|
或者直接访问:`http://localhost:81/emr/sync`
|
||||||
|
|
||||||
|
### 4. 执行同步
|
||||||
|
|
||||||
|
1. 查看当前统计信息(病历总数、修订历史、搜索索引)
|
||||||
|
2. 点击"开始同步"按钮
|
||||||
|
3. 确认同步操作
|
||||||
|
4. 等待同步完成
|
||||||
|
5. 查看同步后的统计信息
|
||||||
|
|
||||||
|
## API接口
|
||||||
|
|
||||||
|
### 获取同步统计
|
||||||
|
|
||||||
|
```
|
||||||
|
GET /emr-sync/stats
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"emrCount": 100,
|
||||||
|
"revisionCount": 100,
|
||||||
|
"searchIndexCount": 100
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 执行同步
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /emr-sync/sync
|
||||||
|
```
|
||||||
|
|
||||||
|
返回:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": "同步完成: 修订历史100条, 搜索索引100条"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 数据流向
|
||||||
|
|
||||||
|
```
|
||||||
|
doc_emr (门诊/住院病历)
|
||||||
|
↓ 同步
|
||||||
|
emr_revision (修订历史)
|
||||||
|
emr_search_index (搜索索引)
|
||||||
|
↓ 展示
|
||||||
|
EMR管理页面(修订历史、病历检索等)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **同步会清空现有数据**:执行同步前会清空emr_revision和emr_search_index表
|
||||||
|
2. **建议先备份**:如果表中有重要数据,建议先备份
|
||||||
|
3. **同步后刷新页面**:同步完成后需要刷新页面才能看到新数据
|
||||||
|
4. **权限要求**:需要管理员权限才能执行同步操作
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q: 同步后数据没有显示?
|
||||||
|
A: 请刷新页面,或检查浏览器控制台是否有错误
|
||||||
|
|
||||||
|
### Q: 同步失败怎么办?
|
||||||
|
A: 检查后端日志,确认数据库连接正常
|
||||||
|
|
||||||
|
### Q: 可以只同步部分数据吗?
|
||||||
|
A: 当前版本不支持部分同步,会同步所有doc_emr中的数据
|
||||||
111
MD/guides/YB_MOCK_GUIDE.md
Normal file
111
MD/guides/YB_MOCK_GUIDE.md
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
# 医保模拟接口使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本项目提供了一个医保模拟服务器(`YbMockController`),用于在本地测试医保接口功能,无需连接真实的医保系统。
|
||||||
|
|
||||||
|
## 模拟的接口
|
||||||
|
|
||||||
|
| 接口代码 | 功能 | 请求示例 |
|
||||||
|
|---------|------|---------|
|
||||||
|
| 1101 | 获取参保人信息 | `{"psn_no":"P1234567890"}` |
|
||||||
|
| 2201 | 门诊登记 | `{"psn_no":"P1234567890","org_code":"H22010402403"}` |
|
||||||
|
| 2203 | 门诊处方上传 | `{"psn_no":"P1234567890","encounter_no":"MZ20260623001"}` |
|
||||||
|
| 2207 | 门诊结算 | `{"psn_no":"P1234567890","encounter_no":"MZ20260623001"}` |
|
||||||
|
| 3201 | 住院登记 | `{"psn_no":"P1234567890","org_code":"H22010402403"}` |
|
||||||
|
| 3203 | 住院处方上传 | `{"psn_no":"P1234567890","encounter_no":"ZY20260623001"}` |
|
||||||
|
| 3207 | 住院结算 | `{"psn_no":"P1234567890","encounter_no":"ZY20260623001"}` |
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 1. 启动应用
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd healthlink-his-server
|
||||||
|
mvn spring-boot:run -pl healthlink-his-application
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 测试接口
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 测试获取参保人信息
|
||||||
|
curl -X POST http://localhost:18080/healthlink-his/yb/mock/1101 \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{"psn_no":"P1234567890"}'
|
||||||
|
|
||||||
|
# 或使用测试脚本
|
||||||
|
chmod +x scripts/test-yb-mock.sh
|
||||||
|
./scripts/test-yb-mock.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 配置医保接口地址
|
||||||
|
|
||||||
|
在 `application-dev.yml` 中配置医保接口地址:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
ybapp:
|
||||||
|
config:
|
||||||
|
url: http://localhost:18080/healthlink-his/yb/mock
|
||||||
|
```
|
||||||
|
|
||||||
|
## 模拟数据
|
||||||
|
|
||||||
|
### 参保人信息 (1101)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"psn_no": "P1234567890",
|
||||||
|
"psn_name": "张三",
|
||||||
|
"sex_code": "1",
|
||||||
|
"sex_name": "男",
|
||||||
|
"birth_date": "1980-01-15",
|
||||||
|
"id_card": "450123198001151234",
|
||||||
|
"insur_type": "职工基本医疗保险",
|
||||||
|
"insur_area": "南宁市",
|
||||||
|
"card_no": "C2024000123456",
|
||||||
|
"balance": "12580.50",
|
||||||
|
"status": "正常"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 门诊结算 (2207)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"settle_no": "JZ20260623001",
|
||||||
|
"total_amount": "156.80",
|
||||||
|
"insurance_pay": "133.28",
|
||||||
|
"self_pay": "23.52",
|
||||||
|
"account_pay": "20.00",
|
||||||
|
"cash_pay": "3.52",
|
||||||
|
"settle_time": "2026-06-23 10:30:00",
|
||||||
|
"status": "成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 住院结算 (3207)
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"settle_no": "ZYJS20260623001",
|
||||||
|
"total_amount": "15680.50",
|
||||||
|
"insurance_pay": "14112.45",
|
||||||
|
"self_pay": "1568.05",
|
||||||
|
"account_pay": "1200.00",
|
||||||
|
"cash_pay": "368.05",
|
||||||
|
"settle_time": "2026-06-23 10:30:00",
|
||||||
|
"status": "成功"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. 模拟服务器仅用于本地测试,不模拟真实的医保业务逻辑
|
||||||
|
2. 返回的数据是固定的测试数据,不会根据请求参数变化
|
||||||
|
3. 生产环境请连接真实的医保接口
|
||||||
|
4. 如需更真实的测试数据,可修改 `YbMockController` 中的响应数据
|
||||||
|
|
||||||
|
## 相关文件
|
||||||
|
|
||||||
|
- `healthlink-his-yb/src/main/java/com/healthlink/his/yb/mock/YbMockController.java`
|
||||||
|
- `scripts/test-yb-mock.sh`
|
||||||
@@ -55,6 +55,12 @@ public class SysDictData extends BaseEntity {
|
|||||||
/** 拼音首字母 */
|
/** 拼音首字母 */
|
||||||
private String pyStr;
|
private String pyStr;
|
||||||
|
|
||||||
|
/** 字典英文值 */
|
||||||
|
private String dictValueEn;
|
||||||
|
|
||||||
|
/** 字典越南文值 */
|
||||||
|
private String dictValueVi;
|
||||||
|
|
||||||
public Long getDictCode() {
|
public Long getDictCode() {
|
||||||
return dictCode;
|
return dictCode;
|
||||||
}
|
}
|
||||||
@@ -146,12 +152,29 @@ public class SysDictData extends BaseEntity {
|
|||||||
this.pyStr = pyStr;
|
this.pyStr = pyStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDictValueEn() {
|
||||||
|
return dictValueEn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDictValueEn(String dictValueEn) {
|
||||||
|
this.dictValueEn = dictValueEn;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDictValueVi() {
|
||||||
|
return dictValueVi;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDictValueVi(String dictValueVi) {
|
||||||
|
this.dictValueVi = dictValueVi;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("dictCode", getDictCode())
|
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).append("dictCode", getDictCode())
|
||||||
.append("dictSort", getDictSort()).append("dictLabel", getDictLabel()).append("dictValue", getDictValue())
|
.append("dictSort", getDictSort()).append("dictLabel", getDictLabel()).append("dictValue", getDictValue())
|
||||||
.append("dictType", getDictType()).append("cssClass", getCssClass()).append("listClass", getListClass())
|
.append("dictType", getDictType()).append("cssClass", getCssClass()).append("listClass", getListClass())
|
||||||
.append("isDefault", getIsDefault()).append("status", getStatus()).append("pyStr", getPyStr())
|
.append("isDefault", getIsDefault()) .append("status", getStatus()).append("pyStr", getPyStr())
|
||||||
|
.append("dictValueEn", getDictValueEn()).append("dictValueVi", getDictValueVi())
|
||||||
.append("createBy", getCreateBy()).append("createTime", getCreateTime()).append("updateBy", getUpdateBy())
|
.append("createBy", getCreateBy()).append("createTime", getCreateTime()).append("updateBy", getUpdateBy())
|
||||||
.append("updateTime", getUpdateTime()).append("remark", getRemark()).toString();
|
.append("updateTime", getUpdateTime()).append("remark", getRemark()).toString();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字典工具类
|
* 字典工具类
|
||||||
@@ -76,7 +78,7 @@ public class DictUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据字典类型和字典值获取字典标签
|
* 根据字典类型和字典值获取字典标签(支持国际化)
|
||||||
*
|
*
|
||||||
* @param dictType 字典类型
|
* @param dictType 字典类型
|
||||||
* @param dictValue 字典值
|
* @param dictValue 字典值
|
||||||
@@ -89,6 +91,36 @@ public class DictUtils {
|
|||||||
return getDictLabel(dictType, dictValue, SEPARATOR);
|
return getDictLabel(dictType, dictValue, SEPARATOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据字典类型和字典值获取国际化字典标签
|
||||||
|
*
|
||||||
|
* @param dictType 字典类型
|
||||||
|
* @param dictValue 字典值
|
||||||
|
* @return 国际化字典标签
|
||||||
|
*/
|
||||||
|
public static String getDictLabelI18n(String dictType, String dictValue) {
|
||||||
|
if (StringUtils.isEmpty(dictValue)) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
List<SysDictData> datas = getDictCache(dictType);
|
||||||
|
if (StringUtils.isNull(datas)) {
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
Locale locale = LocaleContextHolder.getLocale();
|
||||||
|
String lang = locale.getLanguage();
|
||||||
|
for (SysDictData dict : datas) {
|
||||||
|
if (dictValue.equals(dict.getDictValue())) {
|
||||||
|
if ("en".equals(lang) && dict.getDictValueEn() != null) {
|
||||||
|
return dict.getDictValueEn();
|
||||||
|
} else if ("vi".equals(lang) && dict.getDictValueVi() != null) {
|
||||||
|
return dict.getDictValueVi();
|
||||||
|
}
|
||||||
|
return dict.getDictLabel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return StringUtils.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据字典类型和字典标签获取字典值
|
* 根据字典类型和字典标签获取字典值
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
|||||||
children.setQuery(menu.getQuery());
|
children.setQuery(menu.getQuery());
|
||||||
childrenList.add(children);
|
childrenList.add(children);
|
||||||
router.setChildren(childrenList);
|
router.setChildren(childrenList);
|
||||||
} else if (menu.getParentId().intValue() == 0 && isInnerLink(menu)) {
|
} else if ((menu.getParentId() == null || menu.getParentId() == 0) && isInnerLink(menu)) {
|
||||||
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), false, null, menu.getVisible()));
|
router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), false, null, menu.getVisible()));
|
||||||
router.setPath("/");
|
router.setPath("/");
|
||||||
List<RouterVo> childrenList = new ArrayList<RouterVo>();
|
List<RouterVo> childrenList = new ArrayList<RouterVo>();
|
||||||
@@ -524,11 +524,11 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
|||||||
public String getRouterPath(SysMenu menu) {
|
public String getRouterPath(SysMenu menu) {
|
||||||
String routerPath = menu.getPath();
|
String routerPath = menu.getPath();
|
||||||
// 内链打开外网方式
|
// 内链打开外网方式
|
||||||
if (menu.getParentId().intValue() != 0 && isInnerLink(menu)) {
|
if (menu.getParentId() != null && menu.getParentId() != 0 && isInnerLink(menu)) {
|
||||||
routerPath = innerLinkReplaceEach(routerPath);
|
routerPath = innerLinkReplaceEach(routerPath);
|
||||||
}
|
}
|
||||||
// 非外链并且是一级目录(类型为目录)
|
// 非外链并且是一级目录(类型为目录)
|
||||||
if (0 == menu.getParentId().intValue() && UserConstants.TYPE_DIR.equals(menu.getMenuType())
|
if ((menu.getParentId() == null || menu.getParentId() == 0) && UserConstants.TYPE_DIR.equals(menu.getMenuType())
|
||||||
&& UserConstants.NO_FRAME.equals(menu.getIsFrame())) {
|
&& UserConstants.NO_FRAME.equals(menu.getIsFrame())) {
|
||||||
routerPath = "/" + menu.getPath();
|
routerPath = "/" + menu.getPath();
|
||||||
}
|
}
|
||||||
@@ -549,7 +549,8 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
|||||||
String component = UserConstants.LAYOUT;
|
String component = UserConstants.LAYOUT;
|
||||||
if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
|
if (StringUtils.isNotEmpty(menu.getComponent()) && !isMenuFrame(menu)) {
|
||||||
component = menu.getComponent();
|
component = menu.getComponent();
|
||||||
} else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId().intValue() != 0
|
} else if (StringUtils.isEmpty(menu.getComponent()) && menu.getParentId() != null
|
||||||
|
&& menu.getParentId() != 0
|
||||||
&& isInnerLink(menu)) {
|
&& isInnerLink(menu)) {
|
||||||
component = UserConstants.INNER_LINK;
|
component = UserConstants.INNER_LINK;
|
||||||
} else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) {
|
} else if (StringUtils.isEmpty(menu.getComponent()) && isParentView(menu)) {
|
||||||
@@ -565,7 +566,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public boolean isMenuFrame(SysMenu menu) {
|
public boolean isMenuFrame(SysMenu menu) {
|
||||||
return menu.getParentId().intValue() == 0 && UserConstants.TYPE_MENU.equals(menu.getMenuType())
|
return (menu.getParentId() == null || menu.getParentId() == 0) && UserConstants.TYPE_MENU.equals(menu.getMenuType())
|
||||||
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
|
&& menu.getIsFrame().equals(UserConstants.NO_FRAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +587,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
|||||||
* @return 结果
|
* @return 结果
|
||||||
*/
|
*/
|
||||||
public boolean isParentView(SysMenu menu) {
|
public boolean isParentView(SysMenu menu) {
|
||||||
return menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
|
return menu.getParentId() != null && menu.getParentId() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -634,7 +635,7 @@ public class SysMenuServiceImpl implements ISysMenuService {
|
|||||||
Iterator<SysMenu> it = list.iterator();
|
Iterator<SysMenu> it = list.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
SysMenu n = (SysMenu)it.next();
|
SysMenu n = (SysMenu)it.next();
|
||||||
if (n.getParentId().longValue() == t.getMenuId().longValue()) {
|
if (n.getParentId() != null && n.getParentId().longValue() == t.getMenuId().longValue()) {
|
||||||
tlist.add(n);
|
tlist.add(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.healthlink.his.administration.domain.Device;
|
import com.healthlink.his.administration.domain.Device;
|
||||||
import com.healthlink.his.administration.domain.DeviceDefinition;
|
import com.healthlink.his.administration.domain.DeviceDefinition;
|
||||||
@@ -187,10 +188,10 @@ public class LisConfigManageAppServiceImpl implements ILisConfigManageAppServic
|
|||||||
manageDto.getActivityDefDeviceDefs().size(),
|
manageDto.getActivityDefDeviceDefs().size(),
|
||||||
manageDto.getActivityDefObservationDefs().size(),
|
manageDto.getActivityDefObservationDefs().size(),
|
||||||
manageDto.getActivityDefSpecimenDefs().size());
|
manageDto.getActivityDefSpecimenDefs().size());
|
||||||
return R.ok("保存成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("保存检验项目设置失败:id={}, error={}", manageDto.getId(), e.getMessage(), e);
|
log.error("保存检验项目设置失败:id={}, error={}", manageDto.getId(), e.getMessage(), e);
|
||||||
return R.fail("保存失败:" + e.getMessage());
|
return R.fail(MessageUtils.message("his.lis.save_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -98,16 +98,16 @@ public class ObservationManageAppServiceImpl implements IObservationManageAppSer
|
|||||||
if (result) {
|
if (result) {
|
||||||
log.info("保存检验项目成功:name={}, code={}, id={}",
|
log.info("保存检验项目成功:name={}, code={}, id={}",
|
||||||
Observation.getName(), Observation.getCode(), Observation.getId());
|
Observation.getName(), Observation.getCode(), Observation.getId());
|
||||||
return R.ok("添加成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
} else {
|
} else {
|
||||||
log.warn("保存检验项目失败:name={}, code={}",
|
log.warn("保存检验项目失败:name={}, code={}",
|
||||||
Observation.getName(), Observation.getCode());
|
Observation.getName(), Observation.getCode());
|
||||||
return R.fail("添加失败:保存操作未成功");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("保存检验项目异常:name={}, code={}, error={}",
|
log.error("保存检验项目异常:name={}, code={}, error={}",
|
||||||
Observation.getName(), Observation.getCode(), e.getMessage(), e);
|
Observation.getName(), Observation.getCode(), e.getMessage(), e);
|
||||||
return R.fail("添加失败:" + e.getMessage());
|
return R.fail(MessageUtils.message("his.lis.add_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public class SpecimenManageAppServiceImpl implements ISpecimenManageAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> updateOrAddSpecimen(SpecimenDefinition specimenDefinition) {
|
public R<?> updateOrAddSpecimen(SpecimenDefinition specimenDefinition) {
|
||||||
specimenDefinitionService.saveOrUpdate(specimenDefinition);
|
specimenDefinitionService.saveOrUpdate(specimenDefinition);
|
||||||
return R.ok(" 添加成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.healthlink.his.web.aidiagnosis.appservice.impl;
|
package com.healthlink.his.web.aidiagnosis.appservice.impl;
|
||||||
|
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.aidiagnosis.domain.AiDiagnosisSuggestion;
|
import com.healthlink.his.aidiagnosis.domain.AiDiagnosisSuggestion;
|
||||||
import com.healthlink.his.aidiagnosis.service.IAiDiagnosisService;
|
import com.healthlink.his.aidiagnosis.service.IAiDiagnosisService;
|
||||||
import com.healthlink.his.web.aidiagnosis.appservice.IAiDiagnosisAppService;
|
import com.healthlink.his.web.aidiagnosis.appservice.IAiDiagnosisAppService;
|
||||||
@@ -27,10 +28,10 @@ public class AiDiagnosisAppServiceImpl implements IAiDiagnosisAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> suggest(Long encounterId, Long patientId, String symptomText, String source) {
|
public R<?> suggest(Long encounterId, Long patientId, String symptomText, String source) {
|
||||||
if (encounterId == null || patientId == null) {
|
if (encounterId == null || patientId == null) {
|
||||||
return R.fail(400, "就诊ID和患者ID不能为空");
|
return R.fail(400, MessageUtils.message("his.ai.encounter_patient_required"));
|
||||||
}
|
}
|
||||||
if (symptomText == null || symptomText.trim().isEmpty()) {
|
if (symptomText == null || symptomText.trim().isEmpty()) {
|
||||||
return R.fail(400, "症状描述不能为空");
|
return R.fail(400, MessageUtils.message("his.ai.symptom_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String suggestionText = generateSuggestion(symptomText);
|
String suggestionText = generateSuggestion(symptomText);
|
||||||
@@ -61,7 +62,7 @@ public class AiDiagnosisAppServiceImpl implements IAiDiagnosisAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> getHistory(Long patientId) {
|
public R<?> getHistory(Long patientId) {
|
||||||
if (patientId == null) {
|
if (patientId == null) {
|
||||||
return R.fail(400, "患者ID不能为空");
|
return R.fail(400, MessageUtils.message("his.ai.patient_id_required"));
|
||||||
}
|
}
|
||||||
List<AiDiagnosisSuggestion> history = aiDiagnosisService.findByPatientId(patientId);
|
List<AiDiagnosisSuggestion> history = aiDiagnosisService.findByPatientId(patientId);
|
||||||
return R.ok(history);
|
return R.ok(history);
|
||||||
@@ -70,7 +71,7 @@ public class AiDiagnosisAppServiceImpl implements IAiDiagnosisAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> getHistoryByEncounter(Long encounterId) {
|
public R<?> getHistoryByEncounter(Long encounterId) {
|
||||||
if (encounterId == null) {
|
if (encounterId == null) {
|
||||||
return R.fail(400, "就诊ID不能为空");
|
return R.fail(400, MessageUtils.message("his.ai.encounter_id_required"));
|
||||||
}
|
}
|
||||||
List<AiDiagnosisSuggestion> history = aiDiagnosisService.findByEncounterId(encounterId);
|
List<AiDiagnosisSuggestion> history = aiDiagnosisService.findByEncounterId(encounterId);
|
||||||
return R.ok(history);
|
return R.ok(history);
|
||||||
@@ -79,15 +80,15 @@ public class AiDiagnosisAppServiceImpl implements IAiDiagnosisAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> acceptSuggestion(Long id) {
|
public R<?> acceptSuggestion(Long id) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return R.fail(400, "建议ID不能为空");
|
return R.fail(400, MessageUtils.message("his.ai.suggestion_id_required"));
|
||||||
}
|
}
|
||||||
AiDiagnosisSuggestion suggestion = aiDiagnosisService.getById(id);
|
AiDiagnosisSuggestion suggestion = aiDiagnosisService.getById(id);
|
||||||
if (suggestion == null) {
|
if (suggestion == null) {
|
||||||
return R.fail(404, "建议不存在");
|
return R.fail(404, MessageUtils.message("his.ai.suggestion_not_found"));
|
||||||
}
|
}
|
||||||
suggestion.setAccepted(true);
|
suggestion.setAccepted(true);
|
||||||
aiDiagnosisService.updateById(suggestion);
|
aiDiagnosisService.updateById(suggestion);
|
||||||
return R.ok(null, "已采纳");
|
return R.ok(null, MessageUtils.message("his.ai.adopted"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String generateSuggestion(String symptomText) {
|
private String generateSuggestion(String symptomText) {
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package com.healthlink.his.web.anesthesia.controller;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.core.common.core.domain.R;
|
||||||
|
import com.healthlink.his.anesthesia.domain.AnesthesiaFollowup;
|
||||||
|
import com.healthlink.his.anesthesia.domain.AnesthesiaQualityControl;
|
||||||
|
import com.healthlink.his.anesthesia.domain.AnesthesiaSpecimen;
|
||||||
|
import com.healthlink.his.anesthesia.service.IAnesthesiaFollowupService;
|
||||||
|
import com.healthlink.his.anesthesia.service.IAnesthesiaQualityControlService;
|
||||||
|
import com.healthlink.his.anesthesia.service.IAnesthesiaSpecimenService;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/anesthesia-enhanced")
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Tag(name = "麻醉扩展-标本/随访/质控")
|
||||||
|
public class AnesthesiaEnhancedCrudController {
|
||||||
|
|
||||||
|
private final IAnesthesiaSpecimenService specimenService;
|
||||||
|
private final IAnesthesiaFollowupService followupService;
|
||||||
|
private final IAnesthesiaQualityControlService qcService;
|
||||||
|
|
||||||
|
@GetMapping("/specimen/page")
|
||||||
|
@Operation(summary = "标本分页")
|
||||||
|
public R<?> getSpecimenPage(
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||||
|
@RequestParam(required = false) String patientName) {
|
||||||
|
LambdaQueryWrapper<AnesthesiaSpecimen> w = new LambdaQueryWrapper<>();
|
||||||
|
w.like(patientName != null, AnesthesiaSpecimen::getPatientName, patientName)
|
||||||
|
.orderByDesc(AnesthesiaSpecimen::getCreateTime);
|
||||||
|
return R.ok(specimenService.page(new Page<>(pageNo, pageSize), w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/specimen/add")
|
||||||
|
@Operation(summary = "新增标本")
|
||||||
|
public R<?> addSpecimen(@RequestBody AnesthesiaSpecimen specimen) {
|
||||||
|
specimen.setCreateTime(new Date());
|
||||||
|
specimenService.save(specimen);
|
||||||
|
return R.ok(specimen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/specimen/report")
|
||||||
|
@Operation(summary = "报告标本结果")
|
||||||
|
public R<?> reportSpecimen(@RequestBody AnesthesiaSpecimen specimen) {
|
||||||
|
specimen.setReportTime(new Date());
|
||||||
|
specimenService.updateById(specimen);
|
||||||
|
return R.ok(specimen);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/followup/page")
|
||||||
|
@Operation(summary = "随访分页")
|
||||||
|
public R<?> getFollowupPage(
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||||
|
LambdaQueryWrapper<AnesthesiaFollowup> w = new LambdaQueryWrapper<>();
|
||||||
|
w.orderByDesc(AnesthesiaFollowup::getFollowupDate);
|
||||||
|
return R.ok(followupService.page(new Page<>(pageNo, pageSize), w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/followup/add")
|
||||||
|
@Operation(summary = "新增随访")
|
||||||
|
public R<?> addFollowup(@RequestBody AnesthesiaFollowup followup) {
|
||||||
|
followup.setCreateTime(new Date());
|
||||||
|
followupService.save(followup);
|
||||||
|
return R.ok(followup);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/qc/page")
|
||||||
|
@Operation(summary = "质控分页")
|
||||||
|
public R<?> getQcPage(
|
||||||
|
@RequestParam(defaultValue = "1") Integer pageNo,
|
||||||
|
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||||
|
LambdaQueryWrapper<AnesthesiaQualityControl> w = new LambdaQueryWrapper<>();
|
||||||
|
w.orderByDesc(AnesthesiaQualityControl::getCreateTime);
|
||||||
|
return R.ok(qcService.page(new Page<>(pageNo, pageSize), w));
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("/qc/add")
|
||||||
|
@Operation(summary = "新增质控记录")
|
||||||
|
public R<?> addQc(@RequestBody AnesthesiaQualityControl qc) {
|
||||||
|
qc.setCreateTime(new Date());
|
||||||
|
qcService.save(qc);
|
||||||
|
return R.ok(qc);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("/qc/stats")
|
||||||
|
@Operation(summary = "质控统计")
|
||||||
|
public R<?> getQcStats() {
|
||||||
|
long total = qcService.count();
|
||||||
|
long normal = qcService.count(new LambdaQueryWrapper<AnesthesiaQualityControl>()
|
||||||
|
.eq(AnesthesiaQualityControl::getRiskLevel, "NORMAL"));
|
||||||
|
long warning = qcService.count(new LambdaQueryWrapper<AnesthesiaQualityControl>()
|
||||||
|
.eq(AnesthesiaQualityControl::getRiskLevel, "WARNING"));
|
||||||
|
long critical = qcService.count(new LambdaQueryWrapper<AnesthesiaQualityControl>()
|
||||||
|
.eq(AnesthesiaQualityControl::getRiskLevel, "CRITICAL"));
|
||||||
|
return R.ok(Map.of("total", total, "normal", normal, "warning", warning, "critical", critical));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import com.core.common.core.domain.R;
|
|||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.healthlink.his.appointmentmanage.domain.AppointmentConfig;
|
import com.healthlink.his.appointmentmanage.domain.AppointmentConfig;
|
||||||
import com.healthlink.his.appointmentmanage.service.IAppointmentConfigService;
|
import com.healthlink.his.appointmentmanage.service.IAppointmentConfigService;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.web.appointmentmanage.appservice.IAppointmentConfigAppService;
|
import com.healthlink.his.web.appointmentmanage.appservice.IAppointmentConfigAppService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -26,7 +27,7 @@ public class AppointmentConfigAppServiceImpl implements IAppointmentConfigAppSer
|
|||||||
// 获取当前登录用户的机构ID
|
// 获取当前登录用户的机构ID
|
||||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||||
if (tenantId == null) {
|
if (tenantId == null) {
|
||||||
return R.fail("获取机构信息失败");
|
return R.fail(MessageUtils.message("his.appt_config.get_org_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
AppointmentConfig config = appointmentConfigService.getConfigByTenantId(tenantId);
|
AppointmentConfig config = appointmentConfigService.getConfigByTenantId(tenantId);
|
||||||
@@ -38,7 +39,7 @@ public class AppointmentConfigAppServiceImpl implements IAppointmentConfigAppSer
|
|||||||
// 获取当前登录用户的机构ID
|
// 获取当前登录用户的机构ID
|
||||||
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
Integer tenantId = SecurityUtils.getLoginUser().getTenantId();
|
||||||
if (tenantId == null) {
|
if (tenantId == null) {
|
||||||
return R.fail("获取机构信息失败");
|
return R.fail(MessageUtils.message("his.appt_config.get_org_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询是否已存在配置
|
// 查询是否已存在配置
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.healthlink.his.appointmentmanage.domain.ClinicRoom;
|
import com.healthlink.his.appointmentmanage.domain.ClinicRoom;
|
||||||
import com.healthlink.his.appointmentmanage.service.IClinicRoomService;
|
import com.healthlink.his.appointmentmanage.service.IClinicRoomService;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.web.appointmentmanage.appservice.IClinicRoomAppService;
|
import com.healthlink.his.web.appointmentmanage.appservice.IClinicRoomAppService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService {
|
|||||||
public R<?> selectClinicRoomById(Long id) {
|
public R<?> selectClinicRoomById(Long id) {
|
||||||
ClinicRoom clinicRoom = clinicRoomService.selectClinicRoomById(id);
|
ClinicRoom clinicRoom = clinicRoomService.selectClinicRoomById(id);
|
||||||
if (clinicRoom == null) {
|
if (clinicRoom == null) {
|
||||||
return R.fail(404, "诊室不存在");
|
return R.fail(404, MessageUtils.message("his.clinic_room.not_found"));
|
||||||
}
|
}
|
||||||
return R.ok(clinicRoom);
|
return R.ok(clinicRoom);
|
||||||
}
|
}
|
||||||
@@ -47,24 +48,24 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService {
|
|||||||
public R<?> insertClinicRoom(ClinicRoom clinicRoom) {
|
public R<?> insertClinicRoom(ClinicRoom clinicRoom) {
|
||||||
// 数据校验
|
// 数据校验
|
||||||
if (ObjectUtil.isEmpty(clinicRoom.getRoomName())) {
|
if (ObjectUtil.isEmpty(clinicRoom.getRoomName())) {
|
||||||
return R.fail(400, "诊室名称不能为空");
|
return R.fail(400, MessageUtils.message("his.clinic_room.name_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(clinicRoom.getDepartment())) {
|
if (ObjectUtil.isEmpty(clinicRoom.getDepartment())) {
|
||||||
return R.fail(400, "科室名称不能为空");
|
return R.fail(400, MessageUtils.message("his.clinic_room.dept_name_required"));
|
||||||
}
|
}
|
||||||
if (clinicRoom.getRoomName().length() > 50) {
|
if (clinicRoom.getRoomName().length() > 50) {
|
||||||
return R.fail(400, "诊室名称长度不能超过50个字符");
|
return R.fail(400, MessageUtils.message("his.clinic_room.name_too_long"));
|
||||||
}
|
}
|
||||||
if (clinicRoom.getRemarks() != null && clinicRoom.getRemarks().length() > 500) {
|
if (clinicRoom.getRemarks() != null && clinicRoom.getRemarks().length() > 500) {
|
||||||
return R.fail(400, "备注长度不能超过500个字符");
|
return R.fail(400, MessageUtils.message("his.clinic_room.remark_too_long"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增诊室
|
// 新增诊室
|
||||||
int result = clinicRoomService.insertClinicRoom(clinicRoom);
|
int result = clinicRoomService.insertClinicRoom(clinicRoom);
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
return R.ok(null, "新增成功");
|
return R.ok(null, MessageUtils.message("msg.success"));
|
||||||
} else {
|
} else {
|
||||||
return R.fail("新增失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,33 +73,33 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService {
|
|||||||
public R<?> updateClinicRoom(ClinicRoom clinicRoom) {
|
public R<?> updateClinicRoom(ClinicRoom clinicRoom) {
|
||||||
// 数据校验
|
// 数据校验
|
||||||
if (ObjectUtil.isEmpty(clinicRoom.getId())) {
|
if (ObjectUtil.isEmpty(clinicRoom.getId())) {
|
||||||
return R.fail(400, "诊室ID不能为空");
|
return R.fail(400, MessageUtils.message("his.clinic_room.id_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(clinicRoom.getRoomName())) {
|
if (ObjectUtil.isEmpty(clinicRoom.getRoomName())) {
|
||||||
return R.fail(400, "诊室名称不能为空");
|
return R.fail(400, MessageUtils.message("his.clinic_room.name_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(clinicRoom.getDepartment())) {
|
if (ObjectUtil.isEmpty(clinicRoom.getDepartment())) {
|
||||||
return R.fail(400, "科室名称不能为空");
|
return R.fail(400, MessageUtils.message("his.clinic_room.dept_name_required"));
|
||||||
}
|
}
|
||||||
if (clinicRoom.getRoomName().length() > 50) {
|
if (clinicRoom.getRoomName().length() > 50) {
|
||||||
return R.fail(400, "诊室名称长度不能超过50个字符");
|
return R.fail(400, MessageUtils.message("his.clinic_room.name_too_long"));
|
||||||
}
|
}
|
||||||
if (clinicRoom.getRemarks() != null && clinicRoom.getRemarks().length() > 500) {
|
if (clinicRoom.getRemarks() != null && clinicRoom.getRemarks().length() > 500) {
|
||||||
return R.fail(400, "备注长度不能超过500个字符");
|
return R.fail(400, MessageUtils.message("his.clinic_room.remark_too_long"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查诊室是否存在
|
// 检查诊室是否存在
|
||||||
ClinicRoom existingClinicRoom = clinicRoomService.selectClinicRoomById(clinicRoom.getId());
|
ClinicRoom existingClinicRoom = clinicRoomService.selectClinicRoomById(clinicRoom.getId());
|
||||||
if (existingClinicRoom == null) {
|
if (existingClinicRoom == null) {
|
||||||
return R.fail(404, "诊室不存在");
|
return R.fail(404, MessageUtils.message("his.clinic_room.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新诊室
|
// 更新诊室
|
||||||
int result = clinicRoomService.updateClinicRoom(clinicRoom);
|
int result = clinicRoomService.updateClinicRoom(clinicRoom);
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
return R.ok(null, "修改成功");
|
return R.ok(null, MessageUtils.message("msg.success"));
|
||||||
} else {
|
} else {
|
||||||
return R.fail("修改失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,15 +108,15 @@ public class ClinicRoomAppServiceImpl implements IClinicRoomAppService {
|
|||||||
// 检查诊室是否存在
|
// 检查诊室是否存在
|
||||||
ClinicRoom existingClinicRoom = clinicRoomService.selectClinicRoomById(id);
|
ClinicRoom existingClinicRoom = clinicRoomService.selectClinicRoomById(id);
|
||||||
if (existingClinicRoom == null) {
|
if (existingClinicRoom == null) {
|
||||||
return R.fail(404, "诊室不存在");
|
return R.fail(404, MessageUtils.message("his.clinic_room.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除诊室
|
// 删除诊室
|
||||||
int result = clinicRoomService.deleteClinicRoomById(id);
|
int result = clinicRoomService.deleteClinicRoomById(id);
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
return R.ok(null, "删除成功");
|
return R.ok(null, MessageUtils.message("msg.success"));
|
||||||
} else {
|
} else {
|
||||||
return R.fail("删除失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.core.common.core.domain.R;
|
|||||||
import com.healthlink.his.appointmentmanage.domain.DeptAppointmentHours;
|
import com.healthlink.his.appointmentmanage.domain.DeptAppointmentHours;
|
||||||
import com.healthlink.his.appointmentmanage.mapper.DeptAppointmentHoursMapper;
|
import com.healthlink.his.appointmentmanage.mapper.DeptAppointmentHoursMapper;
|
||||||
import com.healthlink.his.appointmentmanage.service.IDeptAppointmentHoursService;
|
import com.healthlink.his.appointmentmanage.service.IDeptAppointmentHoursService;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.web.appointmentmanage.appservice.IDeptAppointmentHoursAppService;
|
import com.healthlink.his.web.appointmentmanage.appservice.IDeptAppointmentHoursAppService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -44,11 +45,11 @@ public class DeptAppointmentHoursAppServiceImpl implements IDeptAppointmentHours
|
|||||||
@Override
|
@Override
|
||||||
public R<?> getDeptAppthoursDetail(Long id) {
|
public R<?> getDeptAppthoursDetail(Long id) {
|
||||||
if (ObjectUtil.isNull(id)) {
|
if (ObjectUtil.isNull(id)) {
|
||||||
return R.fail("ID不能为空");
|
return R.fail(MessageUtils.message("his.dept_hours.id_required"));
|
||||||
}
|
}
|
||||||
DeptAppointmentHours deptAppointmentHours = deptAppointmentHoursService.getById(id);
|
DeptAppointmentHours deptAppointmentHours = deptAppointmentHoursService.getById(id);
|
||||||
if (ObjectUtil.isNull(deptAppointmentHours)) {
|
if (ObjectUtil.isNull(deptAppointmentHours)) {
|
||||||
return R.fail("数据不存在");
|
return R.fail(MessageUtils.message("his.dept_hours.data_not_found"));
|
||||||
}
|
}
|
||||||
return R.ok(deptAppointmentHours);
|
return R.ok(deptAppointmentHours);
|
||||||
}
|
}
|
||||||
@@ -56,13 +57,13 @@ public class DeptAppointmentHoursAppServiceImpl implements IDeptAppointmentHours
|
|||||||
@Override
|
@Override
|
||||||
public R<?> addDeptAppthours(DeptAppointmentHours deptAppointmentHours) {
|
public R<?> addDeptAppthours(DeptAppointmentHours deptAppointmentHours) {
|
||||||
if (ObjectUtil.isNull(deptAppointmentHours)) {
|
if (ObjectUtil.isNull(deptAppointmentHours)) {
|
||||||
return R.fail("数据不能为空");
|
return R.fail(MessageUtils.message("his.dept_hours.data_required"));
|
||||||
}
|
}
|
||||||
if (StrUtil.isBlank(deptAppointmentHours.getInstitution())) {
|
if (StrUtil.isBlank(deptAppointmentHours.getInstitution())) {
|
||||||
return R.fail("所属机构不能为空");
|
return R.fail(MessageUtils.message("his.dept_hours.org_required"));
|
||||||
}
|
}
|
||||||
if (StrUtil.isBlank(deptAppointmentHours.getDepartment())) {
|
if (StrUtil.isBlank(deptAppointmentHours.getDepartment())) {
|
||||||
return R.fail("科室名称不能为空");
|
return R.fail(MessageUtils.message("his.dept_hours.dept_name_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
deptAppointmentHours.setCreatedTime(LocalDateTime.now());
|
deptAppointmentHours.setCreatedTime(LocalDateTime.now());
|
||||||
@@ -73,12 +74,12 @@ public class DeptAppointmentHoursAppServiceImpl implements IDeptAppointmentHours
|
|||||||
@Override
|
@Override
|
||||||
public R<?> updateDeptAppthours(DeptAppointmentHours deptAppointmentHours) {
|
public R<?> updateDeptAppthours(DeptAppointmentHours deptAppointmentHours) {
|
||||||
if (ObjectUtil.isNull(deptAppointmentHours) || ObjectUtil.isNull(deptAppointmentHours.getId())) {
|
if (ObjectUtil.isNull(deptAppointmentHours) || ObjectUtil.isNull(deptAppointmentHours.getId())) {
|
||||||
return R.fail("ID不能为空");
|
return R.fail(MessageUtils.message("his.dept_hours.id_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
DeptAppointmentHours existing = deptAppointmentHoursService.getById(deptAppointmentHours.getId());
|
DeptAppointmentHours existing = deptAppointmentHoursService.getById(deptAppointmentHours.getId());
|
||||||
if (ObjectUtil.isNull(existing)) {
|
if (ObjectUtil.isNull(existing)) {
|
||||||
return R.fail("数据不存在");
|
return R.fail(MessageUtils.message("his.dept_hours.data_not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
deptAppointmentHours.setUpdatedTime(LocalDateTime.now());
|
deptAppointmentHours.setUpdatedTime(LocalDateTime.now());
|
||||||
@@ -89,12 +90,12 @@ public class DeptAppointmentHoursAppServiceImpl implements IDeptAppointmentHours
|
|||||||
@Override
|
@Override
|
||||||
public R<?> deleteDeptAppthours(Long id) {
|
public R<?> deleteDeptAppthours(Long id) {
|
||||||
if (ObjectUtil.isNull(id)) {
|
if (ObjectUtil.isNull(id)) {
|
||||||
return R.fail("ID不能为空");
|
return R.fail(MessageUtils.message("his.dept_hours.id_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
DeptAppointmentHours existing = deptAppointmentHoursService.getById(id);
|
DeptAppointmentHours existing = deptAppointmentHoursService.getById(id);
|
||||||
if (ObjectUtil.isNull(existing)) {
|
if (ObjectUtil.isNull(existing)) {
|
||||||
return R.fail("数据不存在");
|
return R.fail(MessageUtils.message("his.dept_hours.data_not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean remove = deptAppointmentHoursService.removeById(id);
|
boolean remove = deptAppointmentHoursService.removeById(id);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.appointmentmanage.appservice.impl;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.healthlink.his.common.enums.SlotStatus;
|
import com.healthlink.his.common.enums.SlotStatus;
|
||||||
import com.healthlink.his.appointmentmanage.domain.DoctorSchedule;
|
import com.healthlink.his.appointmentmanage.domain.DoctorSchedule;
|
||||||
@@ -92,11 +93,11 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> addDoctorSchedule(DoctorSchedule doctorSchedule) {
|
public R<?> addDoctorSchedule(DoctorSchedule doctorSchedule) {
|
||||||
if (ObjectUtil.isEmpty(doctorSchedule)) {
|
if (ObjectUtil.isEmpty(doctorSchedule)) {
|
||||||
return R.fail("医生排班不能为空");
|
return R.fail(MessageUtils.message("his.schedule.doctor_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doctorSchedule.getLimitNumber() == null || doctorSchedule.getLimitNumber() <= 0) {
|
if (doctorSchedule.getLimitNumber() == null || doctorSchedule.getLimitNumber() <= 0) {
|
||||||
return R.fail("限号数量必须大于0");
|
return R.fail(MessageUtils.message("his.schedule.limit_must_positive"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建新对象,排除id字段(数据库id列是GENERATED ALWAYS,由数据库自动生成)
|
// 创建新对象,排除id字段(数据库id列是GENERATED ALWAYS,由数据库自动生成)
|
||||||
@@ -151,24 +152,24 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
throw new RuntimeException("创建号源池失败");
|
throw new RuntimeException("创建号源池失败");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return R.fail("保存排班信息失败");
|
return R.fail(MessageUtils.message("his.schedule.save_failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public R<?> addDoctorScheduleWithDate(DoctorSchedule doctorSchedule, String scheduledDate) {
|
public R<?> addDoctorScheduleWithDate(DoctorSchedule doctorSchedule, String scheduledDate) {
|
||||||
if (ObjectUtil.isEmpty(doctorSchedule)) {
|
if (ObjectUtil.isEmpty(doctorSchedule)) {
|
||||||
return R.fail("医生排班不能为空");
|
return R.fail(MessageUtils.message("his.schedule.doctor_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doctorSchedule.getLimitNumber() == null || doctorSchedule.getLimitNumber() <= 0) {
|
if (doctorSchedule.getLimitNumber() == null || doctorSchedule.getLimitNumber() <= 0) {
|
||||||
return R.fail("限号数量必须大于0");
|
return R.fail(MessageUtils.message("his.schedule.limit_must_positive"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查结束时间必须大于开始时间
|
// 检查结束时间必须大于开始时间
|
||||||
if (doctorSchedule.getStartTime() != null && doctorSchedule.getEndTime() != null) {
|
if (doctorSchedule.getStartTime() != null && doctorSchedule.getEndTime() != null) {
|
||||||
if (!doctorSchedule.getStartTime().isBefore(doctorSchedule.getEndTime())) {
|
if (!doctorSchedule.getStartTime().isBefore(doctorSchedule.getEndTime())) {
|
||||||
return R.fail("结束时间必须大于开始时间");
|
return R.fail(MessageUtils.message("his.schedule.end_after_start"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,9 +184,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
doctorSchedule.getEndTime()
|
doctorSchedule.getEndTime()
|
||||||
);
|
);
|
||||||
if (hasOverlap) {
|
if (hasOverlap) {
|
||||||
return R.fail("该医生在 " + scheduledDate + " 的 "
|
return R.fail(MessageUtils.message("his.schedule.time_overlap", scheduledDate, doctorSchedule.getStartTime(), doctorSchedule.getEndTime()));
|
||||||
+ doctorSchedule.getStartTime() + "-" + doctorSchedule.getEndTime()
|
|
||||||
+ " 时间段与已有排班重叠,不能重复添加");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,7 +236,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
throw new RuntimeException("创建号源池失败");
|
throw new RuntimeException("创建号源池失败");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return R.fail("保存排班信息失败");
|
return R.fail(MessageUtils.message("his.schedule.save_failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,12 +244,12 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> updateDoctorSchedule(DoctorSchedule doctorSchedule) {
|
public R<?> updateDoctorSchedule(DoctorSchedule doctorSchedule) {
|
||||||
if (ObjectUtil.isEmpty(doctorSchedule) || ObjectUtil.isEmpty(doctorSchedule.getId())) {
|
if (ObjectUtil.isEmpty(doctorSchedule) || ObjectUtil.isEmpty(doctorSchedule.getId())) {
|
||||||
return R.fail("医生排班ID不能为空");
|
return R.fail(MessageUtils.message("his.schedule.id_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = doctorScheduleMapper.updateDoctorSchedule(doctorSchedule);
|
int result = doctorScheduleMapper.updateDoctorSchedule(doctorSchedule);
|
||||||
if (result <= 0) {
|
if (result <= 0) {
|
||||||
return R.fail("更新排班信息失败");
|
return R.fail(MessageUtils.message("his.schedule.update_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同步更新号源池,避免查询联表时医生/诊室等字段看起来“未更新”
|
// 同步更新号源池,避免查询联表时医生/诊室等字段看起来“未更新”
|
||||||
@@ -488,7 +487,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> removeDoctorSchedule(Integer doctorScheduleId) {
|
public R<?> removeDoctorSchedule(Integer doctorScheduleId) {
|
||||||
if (doctorScheduleId == null) {
|
if (doctorScheduleId == null) {
|
||||||
return R.fail("排班id不能为空");
|
return R.fail(MessageUtils.message("his.schedule.id_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. 根据排班ID找到关联的号源池
|
// 1. 根据排班ID找到关联的号源池
|
||||||
@@ -505,7 +504,7 @@ public class DoctorScheduleAppServiceImpl implements IDoctorScheduleAppService {
|
|||||||
.in("status", SlotStatus.BOOKED.getValue(), SlotStatus.LOCKED.getValue(),
|
.in("status", SlotStatus.BOOKED.getValue(), SlotStatus.LOCKED.getValue(),
|
||||||
SlotStatus.CHECKED_IN.getValue()));
|
SlotStatus.CHECKED_IN.getValue()));
|
||||||
if (appointmentCount > 0) {
|
if (appointmentCount > 0) {
|
||||||
return R.fail("该排班已有患者预约,禁止删除!如需取消请先处理患者退预约或使用'停诊'功能。");
|
return R.fail(MessageUtils.message("his.schedule.has_appointments_no_delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 根据号源池ID找到所有关联的号源槽
|
// 2. 根据号源池ID找到所有关联的号源槽
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.healthlink.his.appointmentmanage.domain.SchedulePool;
|
import com.healthlink.his.appointmentmanage.domain.SchedulePool;
|
||||||
import com.healthlink.his.appointmentmanage.service.ISchedulePoolService;
|
import com.healthlink.his.appointmentmanage.service.ISchedulePoolService;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.web.appointmentmanage.appservice.ISchedulePoolAppService;
|
import com.healthlink.his.web.appointmentmanage.appservice.ISchedulePoolAppService;
|
||||||
import com.healthlink.his.web.appointmentmanage.dto.SchedulePoolDto;
|
import com.healthlink.his.web.appointmentmanage.dto.SchedulePoolDto;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -20,7 +21,7 @@ public class SchedulePoolAppServiceImpl implements ISchedulePoolAppService {
|
|||||||
public R<?> addSchedulePool(SchedulePoolDto schedulePoolDto) {
|
public R<?> addSchedulePool(SchedulePoolDto schedulePoolDto) {
|
||||||
//1.数据检验
|
//1.数据检验
|
||||||
if(ObjectUtil.isNull(schedulePoolDto)){
|
if(ObjectUtil.isNull(schedulePoolDto)){
|
||||||
return R.fail("号源不能为空");
|
return R.fail(MessageUtils.message("his.schedule_pool.slot_required"));
|
||||||
}
|
}
|
||||||
//2.封装实体
|
//2.封装实体
|
||||||
SchedulePool schedulePool = new SchedulePool();
|
SchedulePool schedulePool = new SchedulePool();
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.appointmentmanage.appservice.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.administration.domain.Patient;
|
import com.healthlink.his.administration.domain.Patient;
|
||||||
import com.healthlink.his.administration.service.IPatientService;
|
import com.healthlink.his.administration.service.IPatientService;
|
||||||
import com.healthlink.his.appointmentmanage.mapper.ScheduleSlotMapper;
|
import com.healthlink.his.appointmentmanage.mapper.ScheduleSlotMapper;
|
||||||
@@ -47,17 +48,17 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
public R<?> bookTicket(com.healthlink.his.appointmentmanage.domain.AppointmentBookDTO dto) {
|
public R<?> bookTicket(com.healthlink.his.appointmentmanage.domain.AppointmentBookDTO dto) {
|
||||||
Long slotId = dto.getSlotId();
|
Long slotId = dto.getSlotId();
|
||||||
if (slotId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数校验失败:缺少排班槽位唯一标识");
|
return R.fail(MessageUtils.message("his.ticket.slot_id_required"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.bookTicket(dto);
|
int result = ticketService.bookTicket(dto);
|
||||||
if (result > 0) {
|
if (result > 0) {
|
||||||
return R.ok("预约成功!号源已安全锁定。");
|
return R.ok(MessageUtils.message("his.ticket.appointment_success"));
|
||||||
}
|
}
|
||||||
return R.fail("预约挂单核发失败");
|
return R.fail(MessageUtils.message("his.ticket.issue_failed"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("大厅挂号捕获系统异常", e);
|
log.error("大厅挂号捕获系统异常", e);
|
||||||
return R.fail("系统异常:" + e.getMessage());
|
return R.fail(MessageUtils.message("his.common.system_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +71,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> cancelTicket(Long slotId) {
|
public R<?> cancelTicket(Long slotId) {
|
||||||
if (slotId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数错误");
|
return R.fail(MessageUtils.message("his.ticket.param_error"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.cancelTicket(slotId);
|
int result = ticketService.cancelTicket(slotId);
|
||||||
@@ -89,7 +90,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> checkInTicket(Long slotId) {
|
public R<?> checkInTicket(Long slotId) {
|
||||||
if (slotId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数错误");
|
return R.fail(MessageUtils.message("his.ticket.param_error"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.checkInTicket(slotId);
|
int result = ticketService.checkInTicket(slotId);
|
||||||
@@ -108,7 +109,7 @@ public class TicketAppServiceImpl implements ITicketAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> cancelConsultation(Long slotId) {
|
public R<?> cancelConsultation(Long slotId) {
|
||||||
if (slotId == null) {
|
if (slotId == null) {
|
||||||
return R.fail("参数错误");
|
return R.fail(MessageUtils.message("his.ticket.param_error"));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int result = ticketService.cancelConsultation(slotId);
|
int result = ticketService.cancelConsultation(slotId);
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ public class LocationAppServiceImpl implements ILocationAppService {
|
|||||||
locationService.updateLocation(location);
|
locationService.updateLocation(location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return R.ok("启用成功");
|
return R.ok(MessageUtils.message("his.location.enable_success"));
|
||||||
}
|
}
|
||||||
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00010, null));
|
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00010, null));
|
||||||
}
|
}
|
||||||
@@ -126,27 +126,27 @@ public class LocationAppServiceImpl implements ILocationAppService {
|
|||||||
List<EncounterLocation> encounterLocationList =
|
List<EncounterLocation> encounterLocationList =
|
||||||
encounterLocationService.getEncounterLocationInfo(null, location.getId());
|
encounterLocationService.getEncounterLocationInfo(null, location.getId());
|
||||||
if (encounterLocationList != null && !encounterLocationList.isEmpty()) {
|
if (encounterLocationList != null && !encounterLocationList.isEmpty()) {
|
||||||
return R.fail("有被占用的床位,不可停用");
|
return R.fail(MessageUtils.message("his.location.bed_occupied"));
|
||||||
}
|
}
|
||||||
} else if (LocationForm.HOUSE.getValue().equals(location.getFormEnum())) {
|
} else if (LocationForm.HOUSE.getValue().equals(location.getFormEnum())) {
|
||||||
// 检查病房下是否有启用的病床
|
// 检查病房下是否有启用的病床
|
||||||
List<com.healthlink.his.web.common.dto.LocationDto> activeBeds =
|
List<com.healthlink.his.web.common.dto.LocationDto> activeBeds =
|
||||||
commonService.getChildLocation(location.getId(), LocationForm.BED.getValue());
|
commonService.getChildLocation(location.getId(), LocationForm.BED.getValue());
|
||||||
if ((activeBeds != null && !activeBeds.isEmpty())) {
|
if ((activeBeds != null && !activeBeds.isEmpty())) {
|
||||||
return R.fail("病房下有启用或被占用的床位,不可停用");
|
return R.fail(MessageUtils.message("his.location.house_has_active_beds"));
|
||||||
}
|
}
|
||||||
} else if (LocationForm.WARD.getValue().equals(location.getFormEnum())) {
|
} else if (LocationForm.WARD.getValue().equals(location.getFormEnum())) {
|
||||||
// 检查病区下是否有启用的病房
|
// 检查病区下是否有启用的病房
|
||||||
List<com.healthlink.his.web.common.dto.LocationDto> activeHouses =
|
List<com.healthlink.his.web.common.dto.LocationDto> activeHouses =
|
||||||
commonService.getChildLocation(location.getId(), LocationForm.HOUSE.getValue());
|
commonService.getChildLocation(location.getId(), LocationForm.HOUSE.getValue());
|
||||||
if ((activeHouses != null && !activeHouses.isEmpty())) {
|
if ((activeHouses != null && !activeHouses.isEmpty())) {
|
||||||
return R.fail("病区下有启用或被占用的病房,不可停用");
|
return R.fail(MessageUtils.message("his.location.ward_has_active_houses"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
location.setStatusEnum(LocationStatus.INACTIVE.getValue());
|
location.setStatusEnum(LocationStatus.INACTIVE.getValue());
|
||||||
locationService.updateLocation(location);
|
locationService.updateLocation(location);
|
||||||
}
|
}
|
||||||
return R.ok("停用成功");
|
return R.ok(MessageUtils.message("his.location.disable_success"));
|
||||||
}
|
}
|
||||||
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00010, null));
|
return R.fail(MessageUtils.createMessage(PromptMsgConstant.Common.M00010, null));
|
||||||
}
|
}
|
||||||
@@ -263,13 +263,13 @@ public class LocationAppServiceImpl implements ILocationAppService {
|
|||||||
public R<?> addLocation(LocationAddOrEditDto locationAddOrEditDto) {
|
public R<?> addLocation(LocationAddOrEditDto locationAddOrEditDto) {
|
||||||
// 不能为空
|
// 不能为空
|
||||||
if (StringUtils.isEmpty(locationAddOrEditDto.getName())) {
|
if (StringUtils.isEmpty(locationAddOrEditDto.getName())) {
|
||||||
return R.fail(false, "名称不能为空");
|
return R.fail(false, MessageUtils.message("his.location.name_required"));
|
||||||
}
|
}
|
||||||
// 去除空格
|
// 去除空格
|
||||||
String name = locationAddOrEditDto.getName().replaceAll("[ ]", "");
|
String name = locationAddOrEditDto.getName().replaceAll("[ ]", "");
|
||||||
// 判断是否存在同名
|
// 判断是否存在同名
|
||||||
if (locationService.isExistName(name, locationAddOrEditDto.getBusNo(), locationAddOrEditDto.getId())) {
|
if (locationService.isExistName(name, locationAddOrEditDto.getBusNo(), locationAddOrEditDto.getId())) {
|
||||||
return R.fail(false, "【" + name + "】已存在");
|
return R.fail(false, MessageUtils.message("his.location.name_exists", name));
|
||||||
}
|
}
|
||||||
Location location = new Location();
|
Location location = new Location();
|
||||||
BeanUtils.copyProperties(locationAddOrEditDto, location);
|
BeanUtils.copyProperties(locationAddOrEditDto, location);
|
||||||
@@ -308,13 +308,13 @@ public class LocationAppServiceImpl implements ILocationAppService {
|
|||||||
public R<?> editLocation(LocationAddOrEditDto locationAddOrEditDto) {
|
public R<?> editLocation(LocationAddOrEditDto locationAddOrEditDto) {
|
||||||
// 不能为空
|
// 不能为空
|
||||||
if (StringUtils.isEmpty(locationAddOrEditDto.getName())) {
|
if (StringUtils.isEmpty(locationAddOrEditDto.getName())) {
|
||||||
return R.fail(false, "名称不能为空");
|
return R.fail(false, MessageUtils.message("his.location.name_required"));
|
||||||
}
|
}
|
||||||
// 去除空格
|
// 去除空格
|
||||||
String name = locationAddOrEditDto.getName().replaceAll("[ ]", "");
|
String name = locationAddOrEditDto.getName().replaceAll("[ ]", "");
|
||||||
// 判断是否存在同名
|
// 判断是否存在同名
|
||||||
if (locationService.isExistName(name, locationAddOrEditDto.getBusNo(), locationAddOrEditDto.getId())) {
|
if (locationService.isExistName(name, locationAddOrEditDto.getBusNo(), locationAddOrEditDto.getId())) {
|
||||||
return R.fail(false, "【" + name + "】已存在");
|
return R.fail(false, MessageUtils.message("his.location.name_exists", name));
|
||||||
}
|
}
|
||||||
Location location = new Location();
|
Location location = new Location();
|
||||||
BeanUtils.copyProperties(locationAddOrEditDto, location);
|
BeanUtils.copyProperties(locationAddOrEditDto, location);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.AssignSeqUtil;
|
import com.core.common.utils.AssignSeqUtil;
|
||||||
import com.core.common.utils.ChineseConvertUtils;
|
import com.core.common.utils.ChineseConvertUtils;
|
||||||
import com.core.common.utils.DictUtils;
|
import com.core.common.utils.DictUtils;
|
||||||
@@ -105,7 +106,7 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
public R<?> getOperatingRoomById(Long id) {
|
public R<?> getOperatingRoomById(Long id) {
|
||||||
OperatingRoom operatingRoom = operatingRoomService.getById(id);
|
OperatingRoom operatingRoom = operatingRoomService.getById(id);
|
||||||
if (operatingRoom == null) {
|
if (operatingRoom == null) {
|
||||||
return R.fail("手术室信息不存在");
|
return R.fail(MessageUtils.message("his.or.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
OperatingRoomDto operatingRoomDto = new OperatingRoomDto();
|
OperatingRoomDto operatingRoomDto = new OperatingRoomDto();
|
||||||
@@ -139,12 +140,12 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
public R<?> addOperatingRoom(OperatingRoomDto operatingRoomDto) {
|
public R<?> addOperatingRoom(OperatingRoomDto operatingRoomDto) {
|
||||||
// 校验名称不能为空
|
// 校验名称不能为空
|
||||||
if (StringUtils.isEmpty(operatingRoomDto.getName())) {
|
if (StringUtils.isEmpty(operatingRoomDto.getName())) {
|
||||||
return R.fail("手术室名称不能为空");
|
return R.fail(MessageUtils.message("his.or.name_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验房间号不能为空
|
// 校验房间号不能为空
|
||||||
if (StringUtils.isEmpty(operatingRoomDto.getBusNo())) {
|
if (StringUtils.isEmpty(operatingRoomDto.getBusNo())) {
|
||||||
return R.fail("房间号不能为空");
|
return R.fail(MessageUtils.message("his.or.room_no_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去除空格
|
// 去除空格
|
||||||
@@ -153,12 +154,12 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
|
|
||||||
// 判断是否存在同名
|
// 判断是否存在同名
|
||||||
if (isExistName(name, null)) {
|
if (isExistName(name, null)) {
|
||||||
return R.fail("【" + name + "】已存在");
|
return R.fail(MessageUtils.message("his.or.name_exists", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断房间号是否已存在
|
// 判断房间号是否已存在
|
||||||
if (isExistBusNo(operatingRoomDto.getBusNo(), null)) {
|
if (isExistBusNo(operatingRoomDto.getBusNo(), null)) {
|
||||||
return R.fail("房间号【" + operatingRoomDto.getBusNo() + "】已存在");
|
return R.fail(MessageUtils.message("his.or.room_no_exists", operatingRoomDto.getBusNo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
OperatingRoom operatingRoom = new OperatingRoom();
|
OperatingRoom operatingRoom = new OperatingRoom();
|
||||||
@@ -171,9 +172,9 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
|
|
||||||
boolean result = operatingRoomService.save(operatingRoom);
|
boolean result = operatingRoomService.save(operatingRoom);
|
||||||
if (result) {
|
if (result) {
|
||||||
return R.ok(null, "新增成功");
|
return R.ok(null, MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
return R.fail("新增失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -187,17 +188,17 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
// 校验手术室是否存在
|
// 校验手术室是否存在
|
||||||
OperatingRoom existOperatingRoom = operatingRoomService.getById(operatingRoomDto.getId());
|
OperatingRoom existOperatingRoom = operatingRoomService.getById(operatingRoomDto.getId());
|
||||||
if (existOperatingRoom == null) {
|
if (existOperatingRoom == null) {
|
||||||
return R.fail("手术室信息不存在");
|
return R.fail(MessageUtils.message("his.or.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验名称不能为空
|
// 校验名称不能为空
|
||||||
if (StringUtils.isEmpty(operatingRoomDto.getName())) {
|
if (StringUtils.isEmpty(operatingRoomDto.getName())) {
|
||||||
return R.fail("手术室名称不能为空");
|
return R.fail(MessageUtils.message("his.or.name_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验房间号不能为空
|
// 校验房间号不能为空
|
||||||
if (StringUtils.isEmpty(operatingRoomDto.getBusNo())) {
|
if (StringUtils.isEmpty(operatingRoomDto.getBusNo())) {
|
||||||
return R.fail("房间号不能为空");
|
return R.fail(MessageUtils.message("his.or.room_no_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 去除空格
|
// 去除空格
|
||||||
@@ -206,12 +207,12 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
|
|
||||||
// 判断是否存在同名(排除自己)
|
// 判断是否存在同名(排除自己)
|
||||||
if (isExistName(name, operatingRoomDto.getId())) {
|
if (isExistName(name, operatingRoomDto.getId())) {
|
||||||
return R.fail("【" + name + "】已存在");
|
return R.fail(MessageUtils.message("his.or.name_exists", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 判断房间号是否已存在(排除自己)
|
// 判断房间号是否已存在(排除自己)
|
||||||
if (isExistBusNo(operatingRoomDto.getBusNo(), operatingRoomDto.getId())) {
|
if (isExistBusNo(operatingRoomDto.getBusNo(), operatingRoomDto.getId())) {
|
||||||
return R.fail("房间号【" + operatingRoomDto.getBusNo() + "】已存在");
|
return R.fail(MessageUtils.message("his.or.room_no_exists", operatingRoomDto.getBusNo()));
|
||||||
}
|
}
|
||||||
|
|
||||||
OperatingRoom operatingRoom = new OperatingRoom();
|
OperatingRoom operatingRoom = new OperatingRoom();
|
||||||
@@ -224,9 +225,9 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
|
|
||||||
boolean result = operatingRoomService.updateById(operatingRoom);
|
boolean result = operatingRoomService.updateById(operatingRoom);
|
||||||
if (result) {
|
if (result) {
|
||||||
return R.ok(null, "修改成功");
|
return R.ok(null, MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
return R.fail("修改失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -244,16 +245,16 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
try {
|
try {
|
||||||
idList.add(Long.parseLong(idStr.trim()));
|
idList.add(Long.parseLong(idStr.trim()));
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
return R.fail("ID格式错误");
|
return R.fail(MessageUtils.message("his.or.id_format_error"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除手术室
|
// 删除手术室
|
||||||
boolean result = operatingRoomService.removeByIds(idList);
|
boolean result = operatingRoomService.removeByIds(idList);
|
||||||
if (result) {
|
if (result) {
|
||||||
return R.ok(null, "删除成功");
|
return R.ok(null, MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
return R.fail("删除失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -265,7 +266,7 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> enableOperatingRoom(List<Long> ids) {
|
public R<?> enableOperatingRoom(List<Long> ids) {
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
return R.fail("请选择要启用的手术室");
|
return R.fail(MessageUtils.message("his.or.select_enable"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量更新状态为启用
|
// 批量更新状态为启用
|
||||||
@@ -276,9 +277,9 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
|
|
||||||
boolean result = operatingRoomService.updateBatchById(operatingRooms);
|
boolean result = operatingRoomService.updateBatchById(operatingRooms);
|
||||||
if (result) {
|
if (result) {
|
||||||
return R.ok("启用成功");
|
return R.ok(MessageUtils.message("his.or.enable_success"));
|
||||||
}
|
}
|
||||||
return R.fail("启用失败");
|
return R.fail(MessageUtils.message("his.or.enable_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -290,7 +291,7 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> disableOperatingRoom(List<Long> ids) {
|
public R<?> disableOperatingRoom(List<Long> ids) {
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
return R.fail("请选择要停用的手术室");
|
return R.fail(MessageUtils.message("his.or.select_disable"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量更新状态为停用
|
// 批量更新状态为停用
|
||||||
@@ -301,9 +302,9 @@ public class OperatingRoomAppServiceImpl implements IOperatingRoomAppService {
|
|||||||
|
|
||||||
boolean result = operatingRoomService.updateBatchById(operatingRooms);
|
boolean result = operatingRoomService.updateBatchById(operatingRooms);
|
||||||
if (result) {
|
if (result) {
|
||||||
return R.ok("停用成功");
|
return R.ok(MessageUtils.message("his.or.disable_success"));
|
||||||
}
|
}
|
||||||
return R.fail("停用失败");
|
return R.fail(MessageUtils.message("his.or.disable_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -147,7 +147,7 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
|||||||
|
|
||||||
// Validate required fields before processing
|
// Validate required fields before processing
|
||||||
if (orgLocQueryDto.getOrganizationId() == null) {
|
if (orgLocQueryDto.getOrganizationId() == null) {
|
||||||
return R.fail("请选择执行科室");
|
return R.fail(MessageUtils.message("his.org_location.select_dept"));
|
||||||
}
|
}
|
||||||
|
|
||||||
OrganizationLocation orgLoc = new OrganizationLocation();
|
OrganizationLocation orgLoc = new OrganizationLocation();
|
||||||
@@ -170,8 +170,9 @@ public class OrganizationLocationAppServiceImpl implements IOrganizationLocation
|
|||||||
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
orgLoc.getStartTime(), orgLoc.getEndTime())) {
|
||||||
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
Organization org = organizationService.getById(organizationLocation.getOrganizationId());
|
||||||
String organizationName = org != null ? org.getName() : ("科室[" + organizationLocation.getOrganizationId() + "]已删除");
|
String organizationName = org != null ? org.getName() : ("科室[" + organizationLocation.getOrganizationId() + "]已删除");
|
||||||
return R.fail("当前诊疗:" + activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
return R.fail(MessageUtils.message("his.org_location.time_conflict",
|
||||||
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName + "时间冲突");
|
activityName + CommonConstants.Common.DASH + orgLoc.getStartTime()
|
||||||
|
+ CommonConstants.Common.DASH + orgLoc.getEndTime() + "与" + organizationName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (orgLocQueryDto.getId() != null) {
|
if (orgLocQueryDto.getId() != null) {
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ public class PractitionerAppServiceImpl implements IPractitionerAppService {
|
|||||||
// 账号唯一性
|
// 账号唯一性
|
||||||
long count = iBizUserService.count(new LambdaQueryWrapper<BizUser>().eq(BizUser::getUserName, userName));
|
long count = iBizUserService.count(new LambdaQueryWrapper<BizUser>().eq(BizUser::getUserName, userName));
|
||||||
if (count > 0L) {
|
if (count > 0L) {
|
||||||
return R.fail(null, "账号已存在");
|
return R.fail(null, MessageUtils.message("his.practitioner.account_exists"));
|
||||||
}
|
}
|
||||||
// 新增 sys_user
|
// 新增 sys_user
|
||||||
BizUser bizUser = new BizUser();
|
BizUser bizUser = new BizUser();
|
||||||
@@ -462,7 +462,7 @@ public class PractitionerAppServiceImpl implements IPractitionerAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> delUserPractitioner(Long userId) {
|
public R<?> delUserPractitioner(Long userId) {
|
||||||
if (1L == userId) {
|
if (1L == userId) {
|
||||||
return R.fail(null, "admin不允许删除");
|
return R.fail(null, MessageUtils.message("his.practitioner.admin_no_delete"));
|
||||||
}
|
}
|
||||||
// iBizUserService.remove(new LambdaQueryWrapper<BizUser>().eq(BizUser::getUserId, userId));
|
// iBizUserService.remove(new LambdaQueryWrapper<BizUser>().eq(BizUser::getUserId, userId));
|
||||||
practitionerAppAppMapper.delUser(userId);
|
practitionerAppAppMapper.delUser(userId);
|
||||||
@@ -471,7 +471,7 @@ public class PractitionerAppServiceImpl implements IPractitionerAppService {
|
|||||||
List<Practitioner> practitionerList = iPractitionerService.list(new LambdaQueryWrapper<Practitioner>().eq(Practitioner::getUserId, userId));
|
List<Practitioner> practitionerList = iPractitionerService.list(new LambdaQueryWrapper<Practitioner>().eq(Practitioner::getUserId, userId));
|
||||||
Practitioner one = practitionerList != null && !practitionerList.isEmpty() ? practitionerList.get(0) : null;
|
Practitioner one = practitionerList != null && !practitionerList.isEmpty() ? practitionerList.get(0) : null;
|
||||||
if (one == null) {
|
if (one == null) {
|
||||||
return R.fail(null, "未找到对应的医生信息");
|
return R.fail(null, MessageUtils.message("his.practitioner.not_found"));
|
||||||
}
|
}
|
||||||
Long practitionerId = one.getId();// 参与者id
|
Long practitionerId = one.getId();// 参与者id
|
||||||
iPractitionerService.removeById(practitionerId);
|
iPractitionerService.removeById(practitionerId);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.basicmanage.controller;
|
|||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.basicmanage.domain.Bed;
|
import com.healthlink.his.basicmanage.domain.Bed;
|
||||||
import com.healthlink.his.basicmanage.service.IBedService;
|
import com.healthlink.his.basicmanage.service.IBedService;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
@@ -37,20 +38,20 @@ public class BedController {
|
|||||||
}
|
}
|
||||||
@PostMapping("/add")
|
@PostMapping("/add")
|
||||||
public R<?> add(@RequestBody Bed bed) {
|
public R<?> add(@RequestBody Bed bed) {
|
||||||
return bedService.save(bed) ? R.ok("新增成功") : R.fail("新增失败");
|
return bedService.save(bed) ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
@PutMapping("/update")
|
@PutMapping("/update")
|
||||||
public R<?> update(@RequestBody Bed bed) {
|
public R<?> update(@RequestBody Bed bed) {
|
||||||
return bedService.updateById(bed) ? R.ok("修改成功") : R.fail("修改失败");
|
return bedService.updateById(bed) ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
@DeleteMapping("/delete")
|
@DeleteMapping("/delete")
|
||||||
public R<?> delete(@RequestParam Long id) {
|
public R<?> delete(@RequestParam Long id) {
|
||||||
return bedService.removeById(id) ? R.ok("删除成功") : R.fail("删除失败");
|
return bedService.removeById(id) ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
@PutMapping("/status")
|
@PutMapping("/status")
|
||||||
public R<?> updateStatus(@RequestParam Long id, @RequestParam Integer status) {
|
public R<?> updateStatus(@RequestParam Long id, @RequestParam Integer status) {
|
||||||
Bed bed = new Bed(); bed.setId(id); bed.setStatus(status);
|
Bed bed = new Bed(); bed.setId(id); bed.setStatus(status);
|
||||||
return bedService.updateById(bed) ? R.ok("状态更新成功") : R.fail("状态更新失败");
|
return bedService.updateById(bed) ? R.ok(MessageUtils.message("his.bed.status_update_success")) : R.fail(MessageUtils.message("his.bed.status_update_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -81,15 +82,15 @@ public class BedController {
|
|||||||
@PutMapping("/assign")
|
@PutMapping("/assign")
|
||||||
public R<?> assignBed(@RequestParam Long bedId, @RequestParam Long patientId, @RequestParam Long deptId) {
|
public R<?> assignBed(@RequestParam Long bedId, @RequestParam Long patientId, @RequestParam Long deptId) {
|
||||||
Bed bed = bedService.getById(bedId);
|
Bed bed = bedService.getById(bedId);
|
||||||
if (bed == null) return R.fail("床位不存在");
|
if (bed == null) return R.fail(MessageUtils.message("his.bed.not_found"));
|
||||||
if (bed.getStatus() != 0) return R.fail("该床位当前不可分配(状态: " +
|
if (bed.getStatus() != 0) return R.fail(MessageUtils.message("his.bed.not_assignable",
|
||||||
java.util.Map.of(0, "空闲", 1, "占用", 2, "清洁中", 3, "维修中").getOrDefault(bed.getStatus(), "未知") + ")");
|
java.util.Map.of(0, "空闲", 1, "占用", 2, "清洁中", 3, "维修中").getOrDefault(bed.getStatus(), "未知")));
|
||||||
if (bed.getDeptId() != null && !bed.getDeptId().equals(deptId)) {
|
if (bed.getDeptId() != null && !bed.getDeptId().equals(deptId)) {
|
||||||
return R.fail("床位所属科室与患者入院科室不匹配");
|
return R.fail(MessageUtils.message("his.bed.dept_mismatch"));
|
||||||
}
|
}
|
||||||
bed.setStatus(1);
|
bed.setStatus(1);
|
||||||
bedService.updateById(bed);
|
bedService.updateById(bed);
|
||||||
return R.ok("分配成功");
|
return R.ok(MessageUtils.message("his.bed.assign_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,10 +99,10 @@ public class BedController {
|
|||||||
@PutMapping("/discharge")
|
@PutMapping("/discharge")
|
||||||
public R<?> dischargeBed(@RequestParam Long bedId) {
|
public R<?> dischargeBed(@RequestParam Long bedId) {
|
||||||
Bed bed = bedService.getById(bedId);
|
Bed bed = bedService.getById(bedId);
|
||||||
if (bed == null) return R.fail("床位不存在");
|
if (bed == null) return R.fail(MessageUtils.message("his.bed.not_found"));
|
||||||
bed.setStatus(2); // 清洁中
|
bed.setStatus(2); // 清洁中
|
||||||
bedService.updateById(bed);
|
bedService.updateById(bed);
|
||||||
return R.ok("已标记为清洁中");
|
return R.ok(MessageUtils.message("his.bed.marked_cleaning"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.basicmanage.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.healthlink.his.administration.domain.InvoiceSegment;
|
import com.healthlink.his.administration.domain.InvoiceSegment;
|
||||||
import com.healthlink.his.administration.service.IInvoiceSegmentService;
|
import com.healthlink.his.administration.service.IInvoiceSegmentService;
|
||||||
@@ -83,6 +84,6 @@ public class InvoiceSegmentController {
|
|||||||
@PostMapping("/delete")
|
@PostMapping("/delete")
|
||||||
public R<?> delete(@RequestBody InvoiceSegmentDeleteRequest request) {
|
public R<?> delete(@RequestBody InvoiceSegmentDeleteRequest request) {
|
||||||
int rows = invoiceSegmentService.deleteInvoiceSegmentByIds(request.getIds());
|
int rows = invoiceSegmentService.deleteInvoiceSegmentByIds(request.getIds());
|
||||||
return rows > 0 ? R.ok("删除成功") : R.fail("删除失败");
|
return rows > 0 ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.annotation.Log;
|
import com.core.common.annotation.Log;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.enums.BusinessType;
|
import com.core.common.enums.BusinessType;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.core.common.utils.StringUtils;
|
import com.core.common.utils.StringUtils;
|
||||||
@@ -68,7 +69,7 @@ public class OutpatientNoSegmentController {
|
|||||||
if (StringUtils.isEmpty(outpatientNoSegment.getStartNo()) ||
|
if (StringUtils.isEmpty(outpatientNoSegment.getStartNo()) ||
|
||||||
StringUtils.isEmpty(outpatientNoSegment.getEndNo()) ||
|
StringUtils.isEmpty(outpatientNoSegment.getEndNo()) ||
|
||||||
StringUtils.isEmpty(outpatientNoSegment.getUsedNo())) {
|
StringUtils.isEmpty(outpatientNoSegment.getUsedNo())) {
|
||||||
return R.fail("起始号码、终止号码和使用号码不能为空");
|
return R.fail(MessageUtils.message("his.outpatient_no.numbers_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验号码段是否重复
|
// 校验号码段是否重复
|
||||||
@@ -76,7 +77,7 @@ public class OutpatientNoSegmentController {
|
|||||||
outpatientNoSegment.getStartNo(),
|
outpatientNoSegment.getStartNo(),
|
||||||
outpatientNoSegment.getEndNo(),
|
outpatientNoSegment.getEndNo(),
|
||||||
null)) {
|
null)) {
|
||||||
return R.fail("门诊号码设置重复");
|
return R.fail(MessageUtils.message("his.outpatient_no.duplicate"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置创建人信息
|
// 设置创建人信息
|
||||||
@@ -87,7 +88,7 @@ public class OutpatientNoSegmentController {
|
|||||||
outpatientNoSegment.setCreateBy(SecurityUtils.getUsername());
|
outpatientNoSegment.setCreateBy(SecurityUtils.getUsername());
|
||||||
|
|
||||||
int result = outpatientNoSegmentService.insertOutpatientNoSegment(outpatientNoSegment);
|
int result = outpatientNoSegmentService.insertOutpatientNoSegment(outpatientNoSegment);
|
||||||
return result > 0 ? R.ok("保存成功") : R.fail("保存失败");
|
return result > 0 ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -103,7 +104,7 @@ public class OutpatientNoSegmentController {
|
|||||||
if (StringUtils.isEmpty(outpatientNoSegment.getStartNo()) ||
|
if (StringUtils.isEmpty(outpatientNoSegment.getStartNo()) ||
|
||||||
StringUtils.isEmpty(outpatientNoSegment.getEndNo()) ||
|
StringUtils.isEmpty(outpatientNoSegment.getEndNo()) ||
|
||||||
StringUtils.isEmpty(outpatientNoSegment.getUsedNo())) {
|
StringUtils.isEmpty(outpatientNoSegment.getUsedNo())) {
|
||||||
return R.fail("起始号码、终止号码和使用号码不能为空");
|
return R.fail(MessageUtils.message("his.outpatient_no.numbers_required"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验号码段是否重复(排除自身)
|
// 校验号码段是否重复(排除自身)
|
||||||
@@ -111,14 +112,14 @@ public class OutpatientNoSegmentController {
|
|||||||
outpatientNoSegment.getStartNo(),
|
outpatientNoSegment.getStartNo(),
|
||||||
outpatientNoSegment.getEndNo(),
|
outpatientNoSegment.getEndNo(),
|
||||||
outpatientNoSegment.getId())) {
|
outpatientNoSegment.getId())) {
|
||||||
return R.fail("门诊号码设置重复");
|
return R.fail(MessageUtils.message("his.outpatient_no.duplicate"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 设置更新人信息
|
// 设置更新人信息
|
||||||
outpatientNoSegment.setUpdateBy(SecurityUtils.getUsername());
|
outpatientNoSegment.setUpdateBy(SecurityUtils.getUsername());
|
||||||
|
|
||||||
int result = outpatientNoSegmentService.updateOutpatientNoSegment(outpatientNoSegment);
|
int result = outpatientNoSegmentService.updateOutpatientNoSegment(outpatientNoSegment);
|
||||||
return result > 0 ? R.ok("保存成功") : R.fail("保存失败");
|
return result > 0 ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -136,7 +137,7 @@ public class OutpatientNoSegmentController {
|
|||||||
log.info("删除请求 - 接收到的ids类型: " + (idsObj != null ? idsObj.getClass().getName() : "null"));
|
log.info("删除请求 - 接收到的ids类型: " + (idsObj != null ? idsObj.getClass().getName() : "null"));
|
||||||
|
|
||||||
if (idsObj == null) {
|
if (idsObj == null) {
|
||||||
return R.fail("请选择要删除的数据");
|
return R.fail(MessageUtils.message("his.outpatient_no.select_delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为 Long[] 数组
|
// 转换为 Long[] 数组
|
||||||
@@ -162,24 +163,24 @@ public class OutpatientNoSegmentController {
|
|||||||
}
|
}
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
log.info("删除请求 - ID转换失败: " + idObj + ", 错误: " + e.getMessage());
|
log.info("删除请求 - ID转换失败: " + idObj + ", 错误: " + e.getMessage());
|
||||||
return R.fail("无效的ID格式: " + idObj);
|
return R.fail(MessageUtils.message("his.outpatient_no.invalid_id_value", String.valueOf(idObj)));
|
||||||
}
|
}
|
||||||
} else if (idObj instanceof Number) {
|
} else if (idObj instanceof Number) {
|
||||||
ids[i] = ((Number) idObj).longValue();
|
ids[i] = ((Number) idObj).longValue();
|
||||||
} else {
|
} else {
|
||||||
return R.fail("无效的ID类型: " + (idObj != null ? idObj.getClass().getName() : "null"));
|
return R.fail(MessageUtils.message("his.outpatient_no.invalid_id_type", idObj != null ? idObj.getClass().getName() : "null"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (idsObj instanceof Long[]) {
|
} else if (idsObj instanceof Long[]) {
|
||||||
ids = (Long[]) idsObj;
|
ids = (Long[]) idsObj;
|
||||||
} else {
|
} else {
|
||||||
return R.fail("无效的ID数组格式");
|
return R.fail(MessageUtils.message("his.outpatient_no.invalid_id_format"));
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("删除请求 - 转换后的ids: " + java.util.Arrays.toString(ids));
|
log.info("删除请求 - 转换后的ids: " + java.util.Arrays.toString(ids));
|
||||||
|
|
||||||
if (ids == null || ids.length == 0) {
|
if (ids == null || ids.length == 0) {
|
||||||
return R.fail("请选择要删除的数据");
|
return R.fail(MessageUtils.message("his.outpatient_no.select_delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取当前用户ID
|
// 获取当前用户ID
|
||||||
@@ -194,7 +195,7 @@ public class OutpatientNoSegmentController {
|
|||||||
if (segment == null) {
|
if (segment == null) {
|
||||||
// 记录日志以便调试
|
// 记录日志以便调试
|
||||||
log.info("删除失败:记录不存在,ID=" + id + ",可能已被软删除或不存在");
|
log.info("删除失败:记录不存在,ID=" + id + ",可能已被软删除或不存在");
|
||||||
return R.fail("数据不存在,ID: " + id);
|
return R.fail(MessageUtils.message("his.outpatient_no.data_not_found", String.valueOf(id)));
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("删除验证 - 找到记录: ID=" + segment.getId() + ", operatorId=" + segment.getOperatorId() + ", usedNo=" + segment.getUsedNo() + ", startNo=" + segment.getStartNo());
|
log.info("删除验证 - 找到记录: ID=" + segment.getId() + ", operatorId=" + segment.getOperatorId() + ", usedNo=" + segment.getUsedNo() + ", startNo=" + segment.getStartNo());
|
||||||
@@ -202,20 +203,20 @@ public class OutpatientNoSegmentController {
|
|||||||
// 校验归属权
|
// 校验归属权
|
||||||
if (!segment.getOperatorId().equals(userId)) {
|
if (!segment.getOperatorId().equals(userId)) {
|
||||||
log.info("删除验证 - 权限检查失败: segment.operatorId=" + segment.getOperatorId() + ", userId=" + userId);
|
log.info("删除验证 - 权限检查失败: segment.operatorId=" + segment.getOperatorId() + ", userId=" + userId);
|
||||||
return R.fail("只能删除自己维护的门诊号码段");
|
return R.fail(MessageUtils.message("his.outpatient_no.only_own_delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验使用状态(使用号码=起始号码表示未使用)
|
// 校验使用状态(使用号码=起始号码表示未使用)
|
||||||
if (!segment.getUsedNo().equals(segment.getStartNo())) {
|
if (!segment.getUsedNo().equals(segment.getStartNo())) {
|
||||||
log.info("删除验证 - 使用状态检查失败: usedNo=" + segment.getUsedNo() + ", startNo=" + segment.getStartNo());
|
log.info("删除验证 - 使用状态检查失败: usedNo=" + segment.getUsedNo() + ", startNo=" + segment.getStartNo());
|
||||||
return R.fail("已有门诊号码段已有使用的门诊号码,请核对!");
|
return R.fail(MessageUtils.message("his.outpatient_no.has_used_numbers"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("删除验证 - 所有检查通过,开始执行删除");
|
log.info("删除验证 - 所有检查通过,开始执行删除");
|
||||||
int rows = outpatientNoSegmentService.deleteOutpatientNoSegmentByIds(ids);
|
int rows = outpatientNoSegmentService.deleteOutpatientNoSegmentByIds(ids);
|
||||||
log.info("删除执行 - 影响行数: " + rows);
|
log.info("删除执行 - 影响行数: " + rows);
|
||||||
return rows > 0 ? R.ok("删除成功") : R.fail("删除失败");
|
return rows > 0 ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.core.common.core.domain.model.LoginUser;
|
import com.core.common.core.domain.model.LoginUser;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.core.common.core.domain.model.LoginUser;
|
import com.core.common.core.domain.model.LoginUser;
|
||||||
import com.healthlink.his.infectious.domain.InfectiousAudit;
|
import com.healthlink.his.infectious.domain.InfectiousAudit;
|
||||||
@@ -138,7 +139,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> batchAudit(BatchAuditDto batchAuditDto) {
|
public R<?> batchAudit(BatchAuditDto batchAuditDto) {
|
||||||
if (batchAuditDto.getCardNos() == null || batchAuditDto.getCardNos().isEmpty()) {
|
if (batchAuditDto.getCardNos() == null || batchAuditDto.getCardNos().isEmpty()) {
|
||||||
return R.fail("请选择要审核的报卡");
|
return R.fail(MessageUtils.message("his.card.select_approve"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String auditorId = SecurityUtils.getUserId().toString();
|
String auditorId = SecurityUtils.getUserId().toString();
|
||||||
@@ -169,7 +170,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> batchReturn(BatchReturnDto batchReturnDto) {
|
public R<?> batchReturn(BatchReturnDto batchReturnDto) {
|
||||||
if (batchReturnDto.getCardNos() == null || batchReturnDto.getCardNos().isEmpty()) {
|
if (batchReturnDto.getCardNos() == null || batchReturnDto.getCardNos().isEmpty()) {
|
||||||
return R.fail("请选择要退回的报卡");
|
return R.fail(MessageUtils.message("his.card.select_return"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String auditorId = SecurityUtils.getUserId().toString();
|
String auditorId = SecurityUtils.getUserId().toString();
|
||||||
@@ -202,7 +203,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
public R<?> auditPass(SingleAuditDto auditDto) {
|
public R<?> auditPass(SingleAuditDto auditDto) {
|
||||||
InfectiousCard card = infectiousCardMapper.selectByCardNo(auditDto.getCardNo());
|
InfectiousCard card = infectiousCardMapper.selectByCardNo(auditDto.getCardNo());
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return R.fail("报卡不存在");
|
return R.fail(MessageUtils.message("his.card.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String auditorId = SecurityUtils.getUserId().toString();
|
String auditorId = SecurityUtils.getUserId().toString();
|
||||||
@@ -218,7 +219,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
createAuditRecord(card.getCardNo(), oldStatus, 2, 2, auditDto.getAuditOpinion(),
|
createAuditRecord(card.getCardNo(), oldStatus, 2, 2, auditDto.getAuditOpinion(),
|
||||||
null, auditorId, auditorName, false, 1);
|
null, auditorId, auditorName, false, 1);
|
||||||
|
|
||||||
return R.ok("审核通过");
|
return R.ok(MessageUtils.message("his.card.approve_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -226,7 +227,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
public R<?> auditReturn(SingleReturnDto returnDto) {
|
public R<?> auditReturn(SingleReturnDto returnDto) {
|
||||||
InfectiousCard card = infectiousCardMapper.selectByCardNo(returnDto.getCardNo());
|
InfectiousCard card = infectiousCardMapper.selectByCardNo(returnDto.getCardNo());
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return R.fail("报卡不存在");
|
return R.fail(MessageUtils.message("his.card.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
String auditorId = SecurityUtils.getUserId().toString();
|
String auditorId = SecurityUtils.getUserId().toString();
|
||||||
@@ -243,7 +244,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
createAuditRecord(card.getCardNo(), oldStatus, 5, 4, null,
|
createAuditRecord(card.getCardNo(), oldStatus, 5, 4, null,
|
||||||
returnDto.getReturnReason(), auditorId, auditorName, false, 1);
|
returnDto.getReturnReason(), auditorId, auditorName, false, 1);
|
||||||
|
|
||||||
return R.ok("已退回");
|
return R.ok(MessageUtils.message("his.card.returned"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -421,19 +422,19 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
public R<?> submitCard(String cardNo) {
|
public R<?> submitCard(String cardNo) {
|
||||||
InfectiousCard card = infectiousCardMapper.selectByCardNo(cardNo);
|
InfectiousCard card = infectiousCardMapper.selectByCardNo(cardNo);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return R.fail("报卡不存在");
|
return R.fail(MessageUtils.message("his.card.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证权限:只能提交自己的报卡
|
// 验证权限:只能提交自己的报卡
|
||||||
Long userId = SecurityUtils.getUserId();
|
Long userId = SecurityUtils.getUserId();
|
||||||
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
||||||
if (practitioner == null || !practitioner.getId().equals(card.getDoctorId())) {
|
if (practitioner == null || !practitioner.getId().equals(card.getDoctorId())) {
|
||||||
return R.fail("无权操作此报卡");
|
return R.fail(MessageUtils.message("his.card.no_permission"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 狋证状态:只有暂存状态可以提交
|
// 狋证状态:只有暂存状态可以提交
|
||||||
if (!Integer.valueOf(0).equals(card.getStatus())) {
|
if (!Integer.valueOf(0).equals(card.getStatus())) {
|
||||||
return R.fail("只能提交暂存状态的报卡");
|
return R.fail(MessageUtils.message("his.card.only_draft_submit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新状态为已提交
|
// 更新状态为已提交
|
||||||
@@ -441,7 +442,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
card.setUpdateTime(new Date());
|
card.setUpdateTime(new Date());
|
||||||
infectiousCardMapper.updateById(card);
|
infectiousCardMapper.updateById(card);
|
||||||
|
|
||||||
return R.ok("提交成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -449,19 +450,19 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
public R<?> withdrawCard(String cardNo) {
|
public R<?> withdrawCard(String cardNo) {
|
||||||
InfectiousCard card = infectiousCardMapper.selectByCardNo(cardNo);
|
InfectiousCard card = infectiousCardMapper.selectByCardNo(cardNo);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return R.fail("报卡不存在");
|
return R.fail(MessageUtils.message("his.card.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证权限:只能撤回自己的报卡
|
// 验证权限:只能撤回自己的报卡
|
||||||
Long userId = SecurityUtils.getUserId();
|
Long userId = SecurityUtils.getUserId();
|
||||||
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
||||||
if (practitioner == null || !practitioner.getId().equals(card.getDoctorId())) {
|
if (practitioner == null || !practitioner.getId().equals(card.getDoctorId())) {
|
||||||
return R.fail("无权操作此报卡");
|
return R.fail(MessageUtils.message("his.card.no_permission"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 狋证状态:只有已提交状态可以撤回
|
// 狋证状态:只有已提交状态可以撤回
|
||||||
if (!Integer.valueOf(1).equals(card.getStatus())) {
|
if (!Integer.valueOf(1).equals(card.getStatus())) {
|
||||||
return R.fail("只能撤回已提交状态的报卡");
|
return R.fail(MessageUtils.message("his.card.only_submitted_withdraw"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新状态为暂存
|
// 更新状态为暂存
|
||||||
@@ -469,7 +470,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
card.setUpdateTime(new Date());
|
card.setUpdateTime(new Date());
|
||||||
infectiousCardMapper.updateById(card);
|
infectiousCardMapper.updateById(card);
|
||||||
|
|
||||||
return R.ok("撤回成功");
|
return R.ok(MessageUtils.message("his.card.withdraw_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -477,19 +478,19 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
public R<?> deleteCard(String cardNo) {
|
public R<?> deleteCard(String cardNo) {
|
||||||
InfectiousCard card = infectiousCardMapper.selectByCardNo(cardNo);
|
InfectiousCard card = infectiousCardMapper.selectByCardNo(cardNo);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return R.fail("报卡不存在");
|
return R.fail(MessageUtils.message("his.card.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证权限:只能删除自己的报卡
|
// 验证权限:只能删除自己的报卡
|
||||||
Long userId = SecurityUtils.getUserId();
|
Long userId = SecurityUtils.getUserId();
|
||||||
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
||||||
if (practitioner == null || !practitioner.getId().equals(card.getDoctorId())) {
|
if (practitioner == null || !practitioner.getId().equals(card.getDoctorId())) {
|
||||||
return R.fail("无权操作此报卡");
|
return R.fail(MessageUtils.message("his.card.no_permission"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 狋证状态:只有暂存状态可以删除
|
// 狋证状态:只有暂存状态可以删除
|
||||||
if (!Integer.valueOf(0).equals(card.getStatus())) {
|
if (!Integer.valueOf(0).equals(card.getStatus())) {
|
||||||
return R.fail("只能删除暂存状态的报卡");
|
return R.fail(MessageUtils.message("his.card.only_draft_delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新状态为作废
|
// 更新状态为作废
|
||||||
@@ -497,20 +498,20 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
card.setUpdateTime(new Date());
|
card.setUpdateTime(new Date());
|
||||||
infectiousCardMapper.updateById(card);
|
infectiousCardMapper.updateById(card);
|
||||||
|
|
||||||
return R.ok("删除成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> batchSubmitCards(List<String> cardNos) {
|
public R<?> batchSubmitCards(List<String> cardNos) {
|
||||||
if (cardNos == null || cardNos.isEmpty()) {
|
if (cardNos == null || cardNos.isEmpty()) {
|
||||||
return R.fail("请选择要提交的报卡");
|
return R.fail(MessageUtils.message("his.card.select_submit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Long userId = SecurityUtils.getUserId();
|
Long userId = SecurityUtils.getUserId();
|
||||||
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
||||||
if (practitioner == null) {
|
if (practitioner == null) {
|
||||||
return R.fail("当前用户未关联医生信息");
|
return R.fail(MessageUtils.message("his.card.user_no_doctor"));
|
||||||
}
|
}
|
||||||
Long doctorId = practitioner.getId();
|
Long doctorId = practitioner.getId();
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
@@ -533,7 +534,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (successCount == 0) {
|
if (successCount == 0) {
|
||||||
return R.fail("没有可提交的报卡,只能提交暂存状态的报卡");
|
return R.fail(MessageUtils.message("his.card.no_submittable"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.ok("批量提交成功,共提交" + successCount + "条");
|
return R.ok("批量提交成功,共提交" + successCount + "条");
|
||||||
@@ -543,13 +544,13 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> batchDeleteCards(List<String> cardNos) {
|
public R<?> batchDeleteCards(List<String> cardNos) {
|
||||||
if (cardNos == null || cardNos.isEmpty()) {
|
if (cardNos == null || cardNos.isEmpty()) {
|
||||||
return R.fail("请选择要删除的报卡");
|
return R.fail(MessageUtils.message("his.card.select_delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Long userId = SecurityUtils.getUserId();
|
Long userId = SecurityUtils.getUserId();
|
||||||
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(userId);
|
||||||
if (practitioner == null) {
|
if (practitioner == null) {
|
||||||
return R.fail("当前用户未关联医生信息");
|
return R.fail(MessageUtils.message("his.card.user_no_doctor"));
|
||||||
}
|
}
|
||||||
Long doctorId = practitioner.getId();
|
Long doctorId = practitioner.getId();
|
||||||
int successCount = 0;
|
int successCount = 0;
|
||||||
@@ -572,7 +573,7 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (successCount == 0) {
|
if (successCount == 0) {
|
||||||
return R.fail("没有可删除的报卡,只能删除暂存状态的报卡");
|
return R.fail(MessageUtils.message("his.card.no_deletable"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.ok("批量删除成功,共删除" + successCount + "条");
|
return R.ok("批量删除成功,共删除" + successCount + "条");
|
||||||
@@ -587,24 +588,24 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
// 通过 sys_user 表的 user_id 查询医生表 (adm_practitioner) 获取医生 ID
|
// 通过 sys_user 表的 user_id 查询医生表 (adm_practitioner) 获取医生 ID
|
||||||
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(currentUserId);
|
Practitioner practitioner = iPractitionerService.getPractitionerByUserId(currentUserId);
|
||||||
if (practitioner == null) {
|
if (practitioner == null) {
|
||||||
return R.fail("当前用户未关联医生信息");
|
return R.fail(MessageUtils.message("his.card.user_no_doctor"));
|
||||||
}
|
}
|
||||||
Long doctorId = practitioner.getId();
|
Long doctorId = practitioner.getId();
|
||||||
|
|
||||||
// 查询报卡
|
// 查询报卡
|
||||||
InfectiousCard card = infectiousCardMapper.selectByCardNo(updateDto.getCardNo());
|
InfectiousCard card = infectiousCardMapper.selectByCardNo(updateDto.getCardNo());
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return R.fail("报卡不存在");
|
return R.fail(MessageUtils.message("his.card.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证是否当前医生的报卡 - 根据 doctorId 字段验证
|
// 验证是否当前医生的报卡 - 根据 doctorId 字段验证
|
||||||
if (!doctorId.equals(card.getDoctorId())) {
|
if (!doctorId.equals(card.getDoctorId())) {
|
||||||
return R.fail("只能修改自己的报卡");
|
return R.fail(MessageUtils.message("his.card.only_own_edit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 狋证状态是否允许修改(只能修改暂存状态的报卡)
|
// 狋证状态是否允许修改(只能修改暂存状态的报卡)
|
||||||
if (!Integer.valueOf(0).equals(card.getStatus())) {
|
if (!Integer.valueOf(0).equals(card.getStatus())) {
|
||||||
return R.fail("只能修改暂存状态的报卡");
|
return R.fail(MessageUtils.message("his.card.only_draft_edit"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新字段
|
// 更新字段
|
||||||
@@ -621,9 +622,9 @@ public class CardManageAppServiceImpl implements ICardManageAppService {
|
|||||||
|
|
||||||
int rows = infectiousCardMapper.updateById(card);
|
int rows = infectiousCardMapper.updateById(card);
|
||||||
if (rows > 0) {
|
if (rows > 0) {
|
||||||
return R.ok("更新成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
}
|
}
|
||||||
return R.fail("更新失败");
|
return R.fail(MessageUtils.message("msg.failure"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.cdss.appservice.impl;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.cdss.domain.CdssAlert;
|
import com.healthlink.his.cdss.domain.CdssAlert;
|
||||||
import com.healthlink.his.cdss.domain.CdssRule;
|
import com.healthlink.his.cdss.domain.CdssRule;
|
||||||
import com.healthlink.his.cdss.domain.CdssRuleExecution;
|
import com.healthlink.his.cdss.domain.CdssRuleExecution;
|
||||||
@@ -38,7 +39,7 @@ public class CdssAppServiceImpl implements ICdssAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> evaluateRules(Long encounterId, Long patientId, String triggerType, Long departmentId) {
|
public R<?> evaluateRules(Long encounterId, Long patientId, String triggerType, Long departmentId) {
|
||||||
if (encounterId == null || patientId == null) {
|
if (encounterId == null || patientId == null) {
|
||||||
return R.fail(400, "就诊ID和患者ID不能为空");
|
return R.fail(400, MessageUtils.message("his.cdss.encounter_patient_required"));
|
||||||
}
|
}
|
||||||
List<CdssRule> activeRules = cdssRuleService.findActiveRules(triggerType, departmentId);
|
List<CdssRule> activeRules = cdssRuleService.findActiveRules(triggerType, departmentId);
|
||||||
List<CdssAlert> triggeredAlerts = new ArrayList<>();
|
List<CdssAlert> triggeredAlerts = new ArrayList<>();
|
||||||
@@ -86,7 +87,7 @@ public class CdssAppServiceImpl implements ICdssAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> getAlerts(Long encounterId, Integer acknowledged) {
|
public R<?> getAlerts(Long encounterId, Integer acknowledged) {
|
||||||
if (encounterId == null) {
|
if (encounterId == null) {
|
||||||
return R.fail(400, "就诊ID不能为空");
|
return R.fail(400, MessageUtils.message("his.cdss.encounter_id_required"));
|
||||||
}
|
}
|
||||||
List<CdssAlert> alerts = cdssAlertService.findByEncounterId(encounterId);
|
List<CdssAlert> alerts = cdssAlertService.findByEncounterId(encounterId);
|
||||||
if (acknowledged != null) {
|
if (acknowledged != null) {
|
||||||
@@ -100,13 +101,13 @@ public class CdssAppServiceImpl implements ICdssAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> acknowledgeAlert(Long id, String remark) {
|
public R<?> acknowledgeAlert(Long id, String remark) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
return R.fail(400, "告警ID不能为空");
|
return R.fail(400, MessageUtils.message("his.cdss.alert_id_required"));
|
||||||
}
|
}
|
||||||
boolean updated = cdssAlertService.acknowledgeAlert(id, null, remark);
|
boolean updated = cdssAlertService.acknowledgeAlert(id, null, remark);
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
return R.fail(404, "告警不存在或已确认");
|
return R.fail(404, MessageUtils.message("his.cdss.alert_not_found"));
|
||||||
}
|
}
|
||||||
return R.ok(null, "确认成功");
|
return R.ok(null, MessageUtils.message("his.cdss.confirm_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.healthlink.his.web.charge.patientcardrenewal;
|
package com.healthlink.his.web.charge.patientcardrenewal;
|
||||||
|
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
@@ -40,13 +41,13 @@ public class PatientCardRenewalController {
|
|||||||
if (success) {
|
if (success) {
|
||||||
log.info("患者换卡成功: 旧卡号={} -> 新卡号={}",
|
log.info("患者换卡成功: 旧卡号={} -> 新卡号={}",
|
||||||
request.getOldCardNo(), request.getNewCardNo());
|
request.getOldCardNo(), request.getNewCardNo());
|
||||||
return R.ok("换卡成功");
|
return R.ok(MessageUtils.message("his.card_renewal.success"));
|
||||||
} else {
|
} else {
|
||||||
return R.fail("换卡失败");
|
return R.fail(MessageUtils.message("his.card_renewal.failed"));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("患者换卡异常: ", e);
|
log.error("患者换卡异常: ", e);
|
||||||
return R.fail("换卡操作异常: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.card_renewal.exception", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -35,9 +35,10 @@ public interface IOutpatientChargeAppService {
|
|||||||
* 根据就诊id查询患者处方列表
|
* 根据就诊id查询患者处方列表
|
||||||
*
|
*
|
||||||
* @param encounterId 就诊id
|
* @param encounterId 就诊id
|
||||||
|
* @param statusEnum 收费状态过滤(可选,不传则返回全部状态)
|
||||||
* @return 患者处方列表
|
* @return 患者处方列表
|
||||||
*/
|
*/
|
||||||
List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId);
|
List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId, Integer statusEnum);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据就诊id查询患者处方列表并新增字段:实收金额、应收金额、优惠金额、折扣率
|
* 根据就诊id查询患者处方列表并新增字段:实收金额、应收金额、优惠金额、折扣率
|
||||||
|
|||||||
@@ -111,10 +111,11 @@ public class OutpatientChargeAppServiceImpl implements IOutpatientChargeAppServi
|
|||||||
* 根据就诊id查询患者处方列表
|
* 根据就诊id查询患者处方列表
|
||||||
*
|
*
|
||||||
* @param encounterId 就诊id
|
* @param encounterId 就诊id
|
||||||
|
* @param statusEnum 收费状态过滤(可选,不传则返回全部状态)
|
||||||
* @return 患者处方列表
|
* @return 患者处方列表
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId) {
|
public List<EncounterPatientPrescriptionDto> getEncounterPatientPrescription(Long encounterId, Integer statusEnum) {
|
||||||
List<EncounterPatientPrescriptionDto> prescriptionDtoList
|
List<EncounterPatientPrescriptionDto> prescriptionDtoList
|
||||||
= outpatientChargeAppMapper.selectEncounterPatientPrescription(encounterId,
|
= outpatientChargeAppMapper.selectEncounterPatientPrescription(encounterId,
|
||||||
ChargeItemContext.ACTIVITY.getValue(), ChargeItemContext.MEDICATION.getValue(),
|
ChargeItemContext.ACTIVITY.getValue(), ChargeItemContext.MEDICATION.getValue(),
|
||||||
@@ -123,7 +124,7 @@ public class OutpatientChargeAppServiceImpl implements IOutpatientChargeAppServi
|
|||||||
ChargeItemStatus.PLANNED.getValue(), ChargeItemStatus.BILLABLE.getValue(),
|
ChargeItemStatus.PLANNED.getValue(), ChargeItemStatus.BILLABLE.getValue(),
|
||||||
ChargeItemStatus.BILLED.getValue(), ChargeItemStatus.REFUNDING.getValue(),
|
ChargeItemStatus.BILLED.getValue(), ChargeItemStatus.REFUNDING.getValue(),
|
||||||
ChargeItemStatus.REFUNDED.getValue(), ChargeItemStatus.PART_REFUND.getValue(),
|
ChargeItemStatus.REFUNDED.getValue(), ChargeItemStatus.PART_REFUND.getValue(),
|
||||||
CommonConstants.TableName.WOR_DEVICE_REQUEST);
|
CommonConstants.TableName.WOR_DEVICE_REQUEST, statusEnum);
|
||||||
prescriptionDtoList.forEach(e -> {
|
prescriptionDtoList.forEach(e -> {
|
||||||
// 收费状态枚举
|
// 收费状态枚举
|
||||||
e.setStatusEnum_enumText(EnumUtils.getInfoByValue(ChargeItemStatus.class, e.getStatusEnum()));
|
e.setStatusEnum_enumText(EnumUtils.getInfoByValue(ChargeItemStatus.class, e.getStatusEnum()));
|
||||||
|
|||||||
@@ -284,7 +284,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
// ==================== 走退药流程 (生成新单据) ====================
|
// ==================== 走退药流程 (生成新单据) ====================
|
||||||
// 校验是否重复申请
|
// 校验是否重复申请
|
||||||
if (medicationRequest.getRefundMedicineId() != null) {
|
if (medicationRequest.getRefundMedicineId() != null) {
|
||||||
throw new ServiceException("已申请退药,请勿重复申请");
|
throw new ServiceException(MessageUtils.message("charge.refund.drug.duplicate"));
|
||||||
}
|
}
|
||||||
MedicationRequest newRefundRequest = new MedicationRequest();
|
MedicationRequest newRefundRequest = new MedicationRequest();
|
||||||
BeanUtils.copyProperties(medicationRequest, newRefundRequest);
|
BeanUtils.copyProperties(medicationRequest, newRefundRequest);
|
||||||
@@ -329,7 +329,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
// 检查是否已经是退药状态
|
// 检查是否已经是退药状态
|
||||||
if (DispenseStatus.STOPPED.getValue().equals(medicationDispense.getStatusEnum())
|
if (DispenseStatus.STOPPED.getValue().equals(medicationDispense.getStatusEnum())
|
||||||
&& NotPerformedReason.REFUND.getValue().equals(medicationDispense.getStatusEnum())) {
|
&& NotPerformedReason.REFUND.getValue().equals(medicationDispense.getStatusEnum())) {
|
||||||
throw new ServiceException("已申请退药,请勿重复申请");
|
throw new ServiceException(MessageUtils.message("charge.refund.drug.duplicate"));
|
||||||
}
|
}
|
||||||
// 修改状态
|
// 修改状态
|
||||||
medicationDispense.setStatusEnum(DispenseStatus.STOPPED.getValue())
|
medicationDispense.setStatusEnum(DispenseStatus.STOPPED.getValue())
|
||||||
@@ -373,7 +373,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
// ==================== 走退耗材流程 (生成新单据) ====================
|
// ==================== 走退耗材流程 (生成新单据) ====================
|
||||||
// 校验是否重复申请
|
// 校验是否重复申请
|
||||||
if (deviceRequest.getRefundDeviceId() != null) {
|
if (deviceRequest.getRefundDeviceId() != null) {
|
||||||
throw new ServiceException("已申请退耗材,请勿重复申请");
|
throw new ServiceException(MessageUtils.message("charge.refund.consumable.duplicate"));
|
||||||
}
|
}
|
||||||
DeviceRequest newRefundRequest = new DeviceRequest();
|
DeviceRequest newRefundRequest = new DeviceRequest();
|
||||||
BeanUtils.copyProperties(deviceRequest, newRefundRequest);
|
BeanUtils.copyProperties(deviceRequest, newRefundRequest);
|
||||||
@@ -418,7 +418,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
// 检查是否已经是退耗材状态
|
// 检查是否已经是退耗材状态
|
||||||
if (DispenseStatus.STOPPED.getValue().equals(deviceDispense.getStatusEnum())
|
if (DispenseStatus.STOPPED.getValue().equals(deviceDispense.getStatusEnum())
|
||||||
&& NotPerformedReason.REFUND.getValue().equals(deviceDispense.getStatusEnum())) {
|
&& NotPerformedReason.REFUND.getValue().equals(deviceDispense.getStatusEnum())) {
|
||||||
throw new ServiceException("已申请退耗材,请勿重复申请");
|
throw new ServiceException(MessageUtils.message("charge.refund.consumable.duplicate"));
|
||||||
}
|
}
|
||||||
// 修改状态
|
// 修改状态
|
||||||
deviceDispense.setStatusEnum(DispenseStatus.STOPPED.getValue())
|
deviceDispense.setStatusEnum(DispenseStatus.STOPPED.getValue())
|
||||||
@@ -443,7 +443,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
// 诊疗项目需医技科室同意退费
|
// 诊疗项目需医技科室同意退费
|
||||||
if (RequestStatus.COMPLETED.getValue().equals(serviceRequest.getStatusEnum())) {
|
if (RequestStatus.COMPLETED.getValue().equals(serviceRequest.getStatusEnum())) {
|
||||||
if (serviceRequest.getRefundServiceId() != null) {
|
if (serviceRequest.getRefundServiceId() != null) {
|
||||||
throw new ServiceException("已申请退费,请勿重复申请");
|
throw new ServiceException(MessageUtils.message("charge.refund.service.duplicate"));
|
||||||
}
|
}
|
||||||
// 生成服务请求(取消服务)
|
// 生成服务请求(取消服务)
|
||||||
serviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4)); // 服务请求编码
|
serviceRequest.setBusNo(assignSeqUtil.getSeqByDay(AssignSeqEnum.SERVICE_RES_NO.getPrefix(), 4)); // 服务请求编码
|
||||||
@@ -454,7 +454,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
serviceRequestService.save(serviceRequest);
|
serviceRequestService.save(serviceRequest);
|
||||||
} else {
|
} else {
|
||||||
if (RequestStatus.STOPPED.getValue().equals(serviceRequest.getStatusEnum())) {
|
if (RequestStatus.STOPPED.getValue().equals(serviceRequest.getStatusEnum())) {
|
||||||
throw new ServiceException("已申请退费,请勿重复申请");
|
throw new ServiceException(MessageUtils.message("charge.refund.service.duplicate"));
|
||||||
}
|
}
|
||||||
serReqUpdateList.add(serviceRequest.getId());
|
serReqUpdateList.add(serviceRequest.getId());
|
||||||
}
|
}
|
||||||
@@ -576,7 +576,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
if (!medicationRequestList.isEmpty()) {
|
if (!medicationRequestList.isEmpty()) {
|
||||||
if (medicationRequestList.stream().map(MedicationRequest::getStatusEnum)
|
if (medicationRequestList.stream().map(MedicationRequest::getStatusEnum)
|
||||||
.anyMatch(x -> x.equals(RequestStatus.CANCELLED.getValue()))) {
|
.anyMatch(x -> x.equals(RequestStatus.CANCELLED.getValue()))) {
|
||||||
throw new ServiceException("请先退药后再退费");
|
throw new ServiceException(MessageUtils.message("charge.refund.drug.first"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -589,7 +589,7 @@ public class OutpatientRefundAppServiceImpl implements IOutpatientRefundAppServi
|
|||||||
if (!deviceRequestList.isEmpty()) {
|
if (!deviceRequestList.isEmpty()) {
|
||||||
if (deviceRequestList.stream().map(DeviceRequest::getStatusEnum)
|
if (deviceRequestList.stream().map(DeviceRequest::getStatusEnum)
|
||||||
.anyMatch(x -> x.equals(RequestStatus.CANCELLED.getValue()))) {
|
.anyMatch(x -> x.equals(RequestStatus.CANCELLED.getValue()))) {
|
||||||
throw new ServiceException("请先退耗材后再退费");
|
throw new ServiceException(MessageUtils.message("charge.refund.consumable.first"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -292,14 +292,14 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
public R<?> returnRegister(CancelRegPaymentDto cancelRegPaymentDto) {
|
public R<?> returnRegister(CancelRegPaymentDto cancelRegPaymentDto) {
|
||||||
Encounter byId = iEncounterService.getById(cancelRegPaymentDto.getEncounterId());
|
Encounter byId = iEncounterService.getById(cancelRegPaymentDto.getEncounterId());
|
||||||
if (byId == null) {
|
if (byId == null) {
|
||||||
return R.fail(null, "就诊记录不存在");
|
return R.fail(null, MessageUtils.message("his.chargemanage.encounter_not_exist"));
|
||||||
}
|
}
|
||||||
if (EncounterStatus.CANCELLED.getValue().equals(byId.getStatusEnum())) {
|
if (EncounterStatus.CANCELLED.getValue().equals(byId.getStatusEnum())) {
|
||||||
return R.fail(null, "该患者已经退号,请勿重复退号");
|
return R.fail(null, MessageUtils.message("his.chargemanage.already_cancelled_registration"));
|
||||||
}
|
}
|
||||||
// 只有待诊状态才能退号
|
// 只有待诊状态才能退号
|
||||||
if (!EncounterStatus.PLANNED.getValue().equals(byId.getStatusEnum())) {
|
if (!EncounterStatus.PLANNED.getValue().equals(byId.getStatusEnum())) {
|
||||||
return R.fail(null, "该患者已开始就诊,不能退号!");
|
return R.fail(null, MessageUtils.message("his.chargemanage.consultation_started_no_cancel"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 诊前退号检查:病历、费用明细、班段时间
|
// 诊前退号检查:病历、费用明细、班段时间
|
||||||
@@ -395,7 +395,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
)
|
)
|
||||||
));
|
));
|
||||||
if (emrCount > 0) {
|
if (emrCount > 0) {
|
||||||
return R.fail(null, "该患者已有病历记录,不能退号!");
|
return R.fail(null, MessageUtils.message("his.chargemanage.emr_exists_no_cancel"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 检查是否有当日费用明细(除挂号费外的其他费用)
|
// 2. 检查是否有当日费用明细(除挂号费外的其他费用)
|
||||||
@@ -418,7 +418,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
)
|
)
|
||||||
));
|
));
|
||||||
if (chargeItemCount > 0) {
|
if (chargeItemCount > 0) {
|
||||||
return R.fail(null, "该患者已产生诊疗费用,不能退号!");
|
return R.fail(null, MessageUtils.message("his.chargemanage.charge_exists_no_cancel"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 检查是否当日就诊(防止隔日财务封账)
|
// 3. 检查是否当日就诊(防止隔日财务封账)
|
||||||
@@ -426,7 +426,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
LocalDate encounterDate = encounter.getCreateTime().toInstant()
|
LocalDate encounterDate = encounter.getCreateTime().toInstant()
|
||||||
.atZone(ZoneId.systemDefault()).toLocalDate();
|
.atZone(ZoneId.systemDefault()).toLocalDate();
|
||||||
if (encounterDate.isBefore(today)) {
|
if (encounterDate.isBefore(today)) {
|
||||||
return R.fail(null, "非当日就诊记录,不能退号!");
|
return R.fail(null, MessageUtils.message("his.chargemanage.not_today_encounter_no_cancel"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -490,7 +490,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
// 检查当前时间是否已过班段结束时间
|
// 检查当前时间是否已过班段结束时间
|
||||||
LocalTime now = LocalTime.now();
|
LocalTime now = LocalTime.now();
|
||||||
if (now.isAfter(pool.getEndTime())) {
|
if (now.isAfter(pool.getEndTime())) {
|
||||||
return R.fail(null, "当前班段已结束,不能退号!");
|
return R.fail(null, MessageUtils.message("his.chargemanage.shift_ended_no_cancel"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -532,11 +532,22 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
String registerTimeSTime = request.getParameter("registerTimeSTime");
|
String registerTimeSTime = request.getParameter("registerTimeSTime");
|
||||||
String registerTimeETime = request.getParameter("registerTimeETime");
|
String registerTimeETime = request.getParameter("registerTimeETime");
|
||||||
|
|
||||||
|
// Bug #638:提取可选科室过滤参数
|
||||||
|
Long deptId = null;
|
||||||
|
String deptIdParam = request.getParameter("deptId");
|
||||||
|
if (deptIdParam != null && !deptIdParam.isEmpty()) {
|
||||||
|
try {
|
||||||
|
deptId = Long.parseLong(deptIdParam);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略无效的参数值
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
IPage<CurrentDayEncounterDto> currentDayEncounter = outpatientRegistrationAppMapper.getCurrentDayEncounter(
|
IPage<CurrentDayEncounterDto> currentDayEncounter = outpatientRegistrationAppMapper.getCurrentDayEncounter(
|
||||||
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
new Page<>(pageNo, pageSize), EncounterClass.AMB.getValue(), EncounterStatus.IN_PROGRESS.getValue(),
|
||||||
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
ParticipantType.ADMITTER.getCode(), ParticipantType.REGISTRATION_DOCTOR.getCode(), queryWrapper,
|
||||||
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue(),
|
ChargeItemContext.REGISTER.getValue(), PaymentStatus.SUCCESS.getValue(),
|
||||||
registerTimeSTime, registerTimeETime, statusFilter);
|
registerTimeSTime, registerTimeETime, statusFilter, deptId);
|
||||||
|
|
||||||
// 过滤候选池排除列表
|
// 过滤候选池排除列表
|
||||||
// 仅当调用方显式传 excludeFromCandidatePool=true 时才过滤,避免非分诊场景(挂号/收费)
|
// 仅当调用方显式传 excludeFromCandidatePool=true 时才过滤,避免非分诊场景(挂号/收费)
|
||||||
@@ -599,7 +610,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
@Override
|
@Override
|
||||||
public R<?> cancelRegister(Long encounterId) {
|
public R<?> cancelRegister(Long encounterId) {
|
||||||
iEncounterService.removeById(encounterId);
|
iEncounterService.removeById(encounterId);
|
||||||
return R.ok("已取消挂号");
|
return R.ok(MessageUtils.message("his.chargemanage.registration_cancelled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -714,7 +725,7 @@ public class OutpatientRegistrationAppServiceImpl implements IOutpatientRegistra
|
|||||||
public R<?> reprintRegistration(ReprintRegistrationDto reprintRegistrationDto) {
|
public R<?> reprintRegistration(ReprintRegistrationDto reprintRegistrationDto) {
|
||||||
// 补打挂号只是重新打印,不需要修改数据库
|
// 补打挂号只是重新打印,不需要修改数据库
|
||||||
// 可以在这里添加日志记录补打操作
|
// 可以在这里添加日志记录补打操作
|
||||||
return R.ok(null, "补打挂号成功");
|
return R.ok(null, MessageUtils.message("his.chargemanage.reprint_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -61,11 +61,13 @@ public class OutpatientChargeController {
|
|||||||
* 根据就诊id查询患者处方列表
|
* 根据就诊id查询患者处方列表
|
||||||
*
|
*
|
||||||
* @param encounterId 就诊id
|
* @param encounterId 就诊id
|
||||||
|
* @param statusEnum 收费状态过滤(可选,不传则返回全部状态)
|
||||||
* @return 患者处方列表
|
* @return 患者处方列表
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/patient-prescription")
|
@GetMapping(value = "/patient-prescription")
|
||||||
public R<?> getEncounterPatientPrescription(@RequestParam Long encounterId) {
|
public R<?> getEncounterPatientPrescription(@RequestParam Long encounterId,
|
||||||
return R.ok(outpatientChargeAppService.getEncounterPatientPrescription(encounterId));
|
@RequestParam(required = false) Integer statusEnum) {
|
||||||
|
return R.ok(outpatientChargeAppService.getEncounterPatientPrescription(encounterId, statusEnum));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
package com.healthlink.his.web.chargemanage.controller;
|
package com.healthlink.his.web.chargemanage.controller;
|
||||||
|
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.web.chargemanage.appservice.IOutpatientRefundAppService;
|
import com.healthlink.his.web.chargemanage.appservice.IOutpatientRefundAppService;
|
||||||
import com.healthlink.his.web.chargemanage.dto.EncounterPatientPageParam;
|
import com.healthlink.his.web.chargemanage.dto.EncounterPatientPageParam;
|
||||||
import com.healthlink.his.web.chargemanage.dto.RefundItemParam;
|
import com.healthlink.his.web.chargemanage.dto.RefundItemParam;
|
||||||
@@ -68,7 +69,7 @@ public class OutpatientRefundController {
|
|||||||
@GetMapping(value = "/patient-payment")
|
@GetMapping(value = "/patient-payment")
|
||||||
public R<?> getEncounterPatientPayment(@RequestParam(required = false) Long encounterId) {
|
public R<?> getEncounterPatientPayment(@RequestParam(required = false) Long encounterId) {
|
||||||
if (encounterId == null) {
|
if (encounterId == null) {
|
||||||
return R.fail(null, "请先选择患者后再进行退费操作");
|
return R.fail(null, MessageUtils.message("his.refund.select_patient_first"));
|
||||||
}
|
}
|
||||||
return outpatientRefundAppService.getEncounterPatientPayment(encounterId);
|
return outpatientRefundAppService.getEncounterPatientPayment(encounterId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,8 @@ public interface OutpatientChargeAppMapper {
|
|||||||
@Param("chinesePatentMedicine") Integer chinesePatentMedicine,
|
@Param("chinesePatentMedicine") Integer chinesePatentMedicine,
|
||||||
@Param("planned") Integer planned, @Param("billable") Integer billable,
|
@Param("planned") Integer planned, @Param("billable") Integer billable,
|
||||||
@Param("billed") Integer billed, @Param("refunding") Integer refunding, @Param("refunded") Integer refunded,
|
@Param("billed") Integer billed, @Param("refunding") Integer refunding, @Param("refunded") Integer refunded,
|
||||||
@Param("partRefund") Integer partRefund, @Param("worDeviceRequest") String worDeviceRequest);
|
@Param("partRefund") Integer partRefund, @Param("worDeviceRequest") String worDeviceRequest,
|
||||||
|
@Param("filterStatus") Integer filterStatus);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据就诊id查询患者处方列表并新增字段:应收金额,实收金额,优惠金额,折扣率
|
* 根据就诊id查询患者处方列表并新增字段:应收金额,实收金额,优惠金额,折扣率
|
||||||
|
|||||||
@@ -57,7 +57,8 @@ public interface OutpatientRegistrationAppMapper {
|
|||||||
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus,
|
@Param("register") Integer register, @Param("paymentStatus") Integer paymentStatus,
|
||||||
@Param("registerTimeSTime") String registerTimeSTime,
|
@Param("registerTimeSTime") String registerTimeSTime,
|
||||||
@Param("registerTimeETime") String registerTimeETime,
|
@Param("registerTimeETime") String registerTimeETime,
|
||||||
@Param("statusFilter") Integer statusFilter);
|
@Param("statusFilter") Integer statusFilter,
|
||||||
|
@Param("deptId") Long deptId);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询item绑定的信息(耗材或诊疗)
|
* 查询item绑定的信息(耗材或诊疗)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.check.appservice.impl;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.CheckMethod;
|
import com.healthlink.his.check.domain.CheckMethod;
|
||||||
import com.healthlink.his.check.domain.CheckPackage;
|
import com.healthlink.his.check.domain.CheckPackage;
|
||||||
import com.healthlink.his.check.service.ICheckMethodService;
|
import com.healthlink.his.check.service.ICheckMethodService;
|
||||||
@@ -120,13 +121,13 @@ public class CheckMethodAppServiceImpl implements ICheckMethodAppService {
|
|||||||
public R<?> addCheckMethod(CheckMethod checkMethod) {
|
public R<?> addCheckMethod(CheckMethod checkMethod) {
|
||||||
//1.数据校验
|
//1.数据校验
|
||||||
if (ObjectUtil.isEmpty(checkMethod.getName())) {
|
if (ObjectUtil.isEmpty(checkMethod.getName())) {
|
||||||
return R.fail("检查方法名称不能为空!");
|
return R.fail(MessageUtils.message("his.check_method.name_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(checkMethod.getCode())) {
|
if (ObjectUtil.isEmpty(checkMethod.getCode())) {
|
||||||
return R.fail("检查方法代码不能为空!");
|
return R.fail(MessageUtils.message("his.check_method.code_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(checkMethod.getCheckType())) {
|
if (ObjectUtil.isEmpty(checkMethod.getCheckType())) {
|
||||||
return R.fail("检查方法的检查类型不能为空!");
|
return R.fail(MessageUtils.message("his.check_method.type_required"));
|
||||||
}
|
}
|
||||||
//2.保存
|
//2.保存
|
||||||
checkMethodService.save(checkMethod);
|
checkMethodService.save(checkMethod);
|
||||||
@@ -139,13 +140,13 @@ public class CheckMethodAppServiceImpl implements ICheckMethodAppService {
|
|||||||
public R<?> updateCheckMethod(CheckMethod checkMethod) {
|
public R<?> updateCheckMethod(CheckMethod checkMethod) {
|
||||||
//1.数据校验
|
//1.数据校验
|
||||||
if (ObjectUtil.isEmpty(checkMethod.getName())) {
|
if (ObjectUtil.isEmpty(checkMethod.getName())) {
|
||||||
return R.fail("检查方法名称不能为空!");
|
return R.fail(MessageUtils.message("his.check_method.name_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(checkMethod.getCode())) {
|
if (ObjectUtil.isEmpty(checkMethod.getCode())) {
|
||||||
return R.fail("检查方法代码不能为空!");
|
return R.fail(MessageUtils.message("his.check_method.code_required"));
|
||||||
}
|
}
|
||||||
if (ObjectUtil.isEmpty(checkMethod.getCheckType())) {
|
if (ObjectUtil.isEmpty(checkMethod.getCheckType())) {
|
||||||
return R.fail("检查方法的检查类型不能为空!");
|
return R.fail(MessageUtils.message("his.check_method.type_required"));
|
||||||
}
|
}
|
||||||
//2.更新
|
//2.更新
|
||||||
boolean b = checkMethodService.updateById(checkMethod);
|
boolean b = checkMethodService.updateById(checkMethod);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.check.appservice.impl;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.CheckPackage;
|
import com.healthlink.his.check.domain.CheckPackage;
|
||||||
import com.healthlink.his.check.domain.CheckPackageDetail;
|
import com.healthlink.his.check.domain.CheckPackageDetail;
|
||||||
import com.healthlink.his.check.domain.CheckPart;
|
import com.healthlink.his.check.domain.CheckPart;
|
||||||
@@ -90,7 +91,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取检查套餐列表失败", e);
|
log.error("获取检查套餐列表失败", e);
|
||||||
return R.fail("获取检查套餐列表失败");
|
return R.fail(MessageUtils.message("his.check.get_package_list_failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +100,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
try {
|
try {
|
||||||
CheckPackage checkPackage = checkPackageService.getById(id);
|
CheckPackage checkPackage = checkPackageService.getById(id);
|
||||||
if (checkPackage == null) {
|
if (checkPackage == null) {
|
||||||
return R.fail("套餐不存在");
|
return R.fail(MessageUtils.message("his.check.package_not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取套餐明细
|
// 获取套餐明细
|
||||||
@@ -124,7 +125,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
return R.ok(dto);
|
return R.ok(dto);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取检查套餐详情失败", e);
|
log.error("获取检查套餐详情失败", e);
|
||||||
return R.fail("获取检查套餐详情失败");
|
return R.fail(MessageUtils.message("his.check.get_package_detail_failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 保存套餐主表
|
// 保存套餐主表
|
||||||
boolean saveResult = checkPackageService.save(checkPackage);
|
boolean saveResult = checkPackageService.save(checkPackage);
|
||||||
if (!saveResult) {
|
if (!saveResult) {
|
||||||
return R.fail("保存套餐失败");
|
return R.fail(MessageUtils.message("his.check.save_package_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存套餐明细
|
// 保存套餐明细
|
||||||
@@ -159,7 +160,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 同步更新引用此套餐的检查部位价格
|
// 同步更新引用此套餐的检查部位价格
|
||||||
syncCheckPartPrice(checkPackage.getPackageName(), checkPackage.getPackagePrice());
|
syncCheckPartPrice(checkPackage.getPackageName(), checkPackage.getPackagePrice());
|
||||||
|
|
||||||
return R.ok(checkPackage.getId(), "保存成功");
|
return R.ok(checkPackage.getId(), MessageUtils.message("msg.success"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("新增检查套餐失败", e);
|
log.error("新增检查套餐失败", e);
|
||||||
|
|
||||||
@@ -175,11 +176,11 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
if (errorMessage.contains("check_package")) {
|
if (errorMessage.contains("check_package")) {
|
||||||
constraintInfo = "套餐名称或编码";
|
constraintInfo = "套餐名称或编码";
|
||||||
}
|
}
|
||||||
return R.fail("保存失败:数据重复," + constraintInfo + "已存在。详细错误:" + errorMessage);
|
return R.fail(MessageUtils.message("his.check.save_duplicate_failed", constraintInfo, errorMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.fail("新增检查套餐失败:" + errorMessage);
|
return R.fail(MessageUtils.message("his.check.add_package_failed", errorMessage));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -190,7 +191,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 检查套餐是否存在
|
// 检查套餐是否存在
|
||||||
CheckPackage existPackage = checkPackageService.getById(checkPackageDto.getId());
|
CheckPackage existPackage = checkPackageService.getById(checkPackageDto.getId());
|
||||||
if (existPackage == null) {
|
if (existPackage == null) {
|
||||||
return R.fail("套餐不存在");
|
return R.fail(MessageUtils.message("his.check.package_not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新套餐主表数据
|
// 更新套餐主表数据
|
||||||
@@ -203,7 +204,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
|
|
||||||
boolean updateResult = checkPackageService.updateById(checkPackage);
|
boolean updateResult = checkPackageService.updateById(checkPackage);
|
||||||
if (!updateResult) {
|
if (!updateResult) {
|
||||||
return R.fail("更新套餐失败");
|
return R.fail(MessageUtils.message("his.check.update_package_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除原有明细
|
// 删除原有明细
|
||||||
@@ -221,10 +222,10 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 同步更新引用此套餐的检查部位价格
|
// 同步更新引用此套餐的检查部位价格
|
||||||
syncCheckPartPrice(existPackage.getPackageName(), checkPackage.getPackagePrice());
|
syncCheckPartPrice(existPackage.getPackageName(), checkPackage.getPackagePrice());
|
||||||
|
|
||||||
return R.ok("更新成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("更新检查套餐失败", e);
|
log.error("更新检查套餐失败", e);
|
||||||
return R.fail("更新检查套餐失败:" + e.getMessage());
|
return R.fail(MessageUtils.message("his.check.update_package_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +236,7 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 检查套餐是否存在
|
// 检查套餐是否存在
|
||||||
CheckPackage existPackage = checkPackageService.getById(id);
|
CheckPackage existPackage = checkPackageService.getById(id);
|
||||||
if (existPackage == null) {
|
if (existPackage == null) {
|
||||||
return R.fail("套餐不存在");
|
return R.fail(MessageUtils.message("his.check.package_not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除套餐明细 - 先删除子表数据
|
// 删除套餐明细 - 先删除子表数据
|
||||||
@@ -250,14 +251,14 @@ public class CheckPackageAppServiceImpl implements ICheckPackageAppService {
|
|||||||
// 删除套餐主表
|
// 删除套餐主表
|
||||||
boolean deleteResult = checkPackageService.removeById(id);
|
boolean deleteResult = checkPackageService.removeById(id);
|
||||||
if (!deleteResult) {
|
if (!deleteResult) {
|
||||||
return R.fail("删除套餐失败");
|
return R.fail(MessageUtils.message("his.check.delete_package_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info("删除检查套餐成功,套餐 ID: {}", id);
|
log.info("删除检查套餐成功,套餐 ID: {}", id);
|
||||||
return R.ok("删除成功");
|
return R.ok(MessageUtils.message("msg.success"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("删除检查套餐失败", e);
|
log.error("删除检查套餐失败", e);
|
||||||
return R.fail("删除检查套餐失败:" + e.getMessage());
|
return R.fail(MessageUtils.message("his.check.delete_package_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.check.appservice.impl;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.LisGroupInfo;
|
import com.healthlink.his.check.domain.LisGroupInfo;
|
||||||
import com.healthlink.his.check.service.ILisGroupInfoService;
|
import com.healthlink.his.check.service.ILisGroupInfoService;
|
||||||
import com.healthlink.his.web.check.appservice.ILisGroupInfoAppService;
|
import com.healthlink.his.web.check.appservice.ILisGroupInfoAppService;
|
||||||
@@ -29,7 +30,7 @@ public class LisGroupInfoAppServiceImpl implements ILisGroupInfoAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> add(LisGroupInfo lisGroupInfo) {
|
public R<?> add(LisGroupInfo lisGroupInfo) {
|
||||||
if (ObjectUtil.isEmpty(lisGroupInfo)) {
|
if (ObjectUtil.isEmpty(lisGroupInfo)) {
|
||||||
return R.fail("信息不能为空");
|
return R.fail(MessageUtils.message("his.lis.info_required"));
|
||||||
}
|
}
|
||||||
boolean save = lisGroupInfoService.save(lisGroupInfo);
|
boolean save = lisGroupInfoService.save(lisGroupInfo);
|
||||||
return R.ok(save);
|
return R.ok(save);
|
||||||
@@ -38,7 +39,7 @@ public class LisGroupInfoAppServiceImpl implements ILisGroupInfoAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<?> update(LisGroupInfo lisGroupInfo) {
|
public R<?> update(LisGroupInfo lisGroupInfo) {
|
||||||
if (ObjectUtil.isEmpty(lisGroupInfo)) {
|
if (ObjectUtil.isEmpty(lisGroupInfo)) {
|
||||||
return R.fail("信息不能为空");
|
return R.fail(MessageUtils.message("his.lis.info_required"));
|
||||||
}
|
}
|
||||||
boolean update = lisGroupInfoService.updateById(lisGroupInfo);
|
boolean update = lisGroupInfoService.updateById(lisGroupInfo);
|
||||||
return R.ok( update);
|
return R.ok( update);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.check.appservice.impl;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.DicomPrintRecord;
|
import com.healthlink.his.check.domain.DicomPrintRecord;
|
||||||
import com.healthlink.his.check.domain.RadiologyImage;
|
import com.healthlink.his.check.domain.RadiologyImage;
|
||||||
import com.healthlink.his.check.domain.RadiologyImageReport;
|
import com.healthlink.his.check.domain.RadiologyImageReport;
|
||||||
@@ -82,7 +83,7 @@ public class RadiologyImageAppServiceImpl implements IRadiologyImageAppService {
|
|||||||
public R<?> submitReport(Long id) {
|
public R<?> submitReport(Long id) {
|
||||||
RadiologyImageReport r = reportService.getById(id);
|
RadiologyImageReport r = reportService.getById(id);
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
return R.fail("报告不存在");
|
return R.fail(MessageUtils.message("radiology.report.not.exist"));
|
||||||
}
|
}
|
||||||
r.setStatus("REPORTED");
|
r.setStatus("REPORTED");
|
||||||
r.setReportTime(new Date());
|
r.setReportTime(new Date());
|
||||||
@@ -101,7 +102,7 @@ public class RadiologyImageAppServiceImpl implements IRadiologyImageAppService {
|
|||||||
public R<?> verifyReport(Long id, String doctor) {
|
public R<?> verifyReport(Long id, String doctor) {
|
||||||
RadiologyImageReport r = reportService.getById(id);
|
RadiologyImageReport r = reportService.getById(id);
|
||||||
if (r == null) {
|
if (r == null) {
|
||||||
return R.fail("报告不存在");
|
return R.fail(MessageUtils.message("radiology.report.not.exist"));
|
||||||
}
|
}
|
||||||
r.setStatus("VERIFIED");
|
r.setStatus("VERIFIED");
|
||||||
r.setVerifyDoctor(doctor);
|
r.setVerifyDoctor(doctor);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.core.common.core.domain.AjaxResult;
|
|||||||
import com.core.common.core.page.TableDataInfo;
|
import com.core.common.core.page.TableDataInfo;
|
||||||
import com.core.common.exception.ServiceException;
|
import com.core.common.exception.ServiceException;
|
||||||
import com.core.common.utils.AssignSeqUtil;
|
import com.core.common.utils.AssignSeqUtil;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.healthlink.his.administration.domain.Account;
|
import com.healthlink.his.administration.domain.Account;
|
||||||
import com.healthlink.his.administration.domain.ChargeItem;
|
import com.healthlink.his.administration.domain.ChargeItem;
|
||||||
@@ -307,7 +308,7 @@ public class ExamApplyController extends BaseController {
|
|||||||
// 🔧 BugFix#385: 获取患者真实的自费账户,预结算验证要求accountId必须真实存在
|
// 🔧 BugFix#385: 获取患者真实的自费账户,预结算验证要求accountId必须真实存在
|
||||||
Account selfAccount = accountService.getSelfAccount(dto.getEncounterId());
|
Account selfAccount = accountService.getSelfAccount(dto.getEncounterId());
|
||||||
if (selfAccount == null) {
|
if (selfAccount == null) {
|
||||||
throw new ServiceException("患者自费账户不存在,无法创建检查收费项,encounterId=" + dto.getEncounterId());
|
throw new ServiceException(MessageUtils.message("his.exam.self_account_not_found", dto.getEncounterId()));
|
||||||
}
|
}
|
||||||
chargeItem.setAccountId(selfAccount.getId());
|
chargeItem.setAccountId(selfAccount.getId());
|
||||||
// 🔧 BugFix#385: 使用 ItemType.ACTIVITY.getValue()=3 表示诊疗,而不是硬编码的2
|
// 🔧 BugFix#385: 使用 ItemType.ACTIVITY.getValue()=3 表示诊疗,而不是硬编码的2
|
||||||
@@ -497,7 +498,7 @@ public class ExamApplyController extends BaseController {
|
|||||||
// 🔧 BugFix#385: 获取患者真实的自费账户,预结算验证要求accountId必须真实存在
|
// 🔧 BugFix#385: 获取患者真实的自费账户,预结算验证要求accountId必须真实存在
|
||||||
Account selfAccount = accountService.getSelfAccount(dto.getEncounterId());
|
Account selfAccount = accountService.getSelfAccount(dto.getEncounterId());
|
||||||
if (selfAccount == null) {
|
if (selfAccount == null) {
|
||||||
throw new ServiceException("患者自费账户不存在,无法创建检查收费项,encounterId=" + dto.getEncounterId());
|
throw new ServiceException(MessageUtils.message("his.exam.self_account_not_found", dto.getEncounterId()));
|
||||||
}
|
}
|
||||||
chargeItem.setAccountId(selfAccount.getId());
|
chargeItem.setAccountId(selfAccount.getId());
|
||||||
// 🔧 BugFix#385: 使用 ItemType.ACTIVITY.getValue()=3 表示诊疗
|
// 🔧 BugFix#385: 使用 ItemType.ACTIVITY.getValue()=3 表示诊疗
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.check.controller;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.ExamAppointment;
|
import com.healthlink.his.check.domain.ExamAppointment;
|
||||||
import com.healthlink.his.check.service.IExamAppointmentService;
|
import com.healthlink.his.check.service.IExamAppointmentService;
|
||||||
import lombok.AllArgsConstructor;import lombok.extern.slf4j.Slf4j;
|
import lombok.AllArgsConstructor;import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -40,28 +41,28 @@ public class ExamAppointmentController {
|
|||||||
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
||||||
@Transactional(rollbackFor=Exception.class)
|
@Transactional(rollbackFor=Exception.class)
|
||||||
public R<?> checkin(@PathVariable Long id) {
|
public R<?> checkin(@PathVariable Long id) {
|
||||||
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail("预约不存在");
|
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail(MessageUtils.message("his.exam.appointment_not_found"));
|
||||||
a.setStatus("CHECKED_IN"); appointmentService.updateById(a); return R.ok();
|
a.setStatus("CHECKED_IN"); appointmentService.updateById(a); return R.ok();
|
||||||
}
|
}
|
||||||
@PutMapping("/start/{id}")
|
@PutMapping("/start/{id}")
|
||||||
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
||||||
@Transactional(rollbackFor=Exception.class)
|
@Transactional(rollbackFor=Exception.class)
|
||||||
public R<?> startExam(@PathVariable Long id) {
|
public R<?> startExam(@PathVariable Long id) {
|
||||||
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail("预约不存在");
|
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail(MessageUtils.message("his.exam.appointment_not_found"));
|
||||||
a.setStatus("EXAMINING"); appointmentService.updateById(a); return R.ok();
|
a.setStatus("EXAMINING"); appointmentService.updateById(a); return R.ok();
|
||||||
}
|
}
|
||||||
@PutMapping("/complete/{id}")
|
@PutMapping("/complete/{id}")
|
||||||
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
||||||
@Transactional(rollbackFor=Exception.class)
|
@Transactional(rollbackFor=Exception.class)
|
||||||
public R<?> complete(@PathVariable Long id) {
|
public R<?> complete(@PathVariable Long id) {
|
||||||
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail("预约不存在");
|
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail(MessageUtils.message("his.exam.appointment_not_found"));
|
||||||
a.setStatus("COMPLETED"); appointmentService.updateById(a); return R.ok();
|
a.setStatus("COMPLETED"); appointmentService.updateById(a); return R.ok();
|
||||||
}
|
}
|
||||||
@PutMapping("/cancel/{id}")
|
@PutMapping("/cancel/{id}")
|
||||||
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
@PreAuthorize("@ss.hasPermi('infection:check:edit')")
|
||||||
@Transactional(rollbackFor=Exception.class)
|
@Transactional(rollbackFor=Exception.class)
|
||||||
public R<?> cancel(@PathVariable Long id) {
|
public R<?> cancel(@PathVariable Long id) {
|
||||||
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail("预约不存在");
|
ExamAppointment a = appointmentService.getById(id); if (a == null) return R.fail(MessageUtils.message("his.exam.appointment_not_found"));
|
||||||
a.setStatus("CANCELLED"); appointmentService.updateById(a); return R.ok();
|
a.setStatus("CANCELLED"); appointmentService.updateById(a); return R.ok();
|
||||||
}
|
}
|
||||||
@GetMapping("/queue")
|
@GetMapping("/queue")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.check.controller;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.*;
|
import com.healthlink.his.check.domain.*;
|
||||||
import com.healthlink.his.check.service.*;
|
import com.healthlink.his.check.service.*;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -54,7 +55,7 @@ public class RadiologyEnhancedController {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> notifyReport(@RequestParam Long id) {
|
public R<?> notifyReport(@RequestParam Long id) {
|
||||||
RadiologyUrgentReport r = urgentReportService.getById(id);
|
RadiologyUrgentReport r = urgentReportService.getById(id);
|
||||||
if (r == null) return R.fail("报告不存在");
|
if (r == null) return R.fail(MessageUtils.message("his.radiology.report_not_found"));
|
||||||
r.setNotifyStatus(1);
|
r.setNotifyStatus(1);
|
||||||
r.setNotifyTime(new Date());
|
r.setNotifyTime(new Date());
|
||||||
r.setUpdateTime(new Date());
|
r.setUpdateTime(new Date());
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.healthlink.his.web.check.controller;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.check.domain.SpecimenBarcode;
|
import com.healthlink.his.check.domain.SpecimenBarcode;
|
||||||
import com.healthlink.his.check.service.ISpecimenBarcodeService;
|
import com.healthlink.his.check.service.ISpecimenBarcodeService;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
@@ -76,7 +77,7 @@ public class SpecimenBarcodeController {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> scanConfirm(@PathVariable Long id, @RequestParam("confirmBy") String confirmBy) {
|
public R<?> scanConfirm(@PathVariable Long id, @RequestParam("confirmBy") String confirmBy) {
|
||||||
SpecimenBarcode barcode = barcodeService.getById(id);
|
SpecimenBarcode barcode = barcodeService.getById(id);
|
||||||
if (barcode == null) return R.fail("条码不存在");
|
if (barcode == null) return R.fail(MessageUtils.message("his.specimen.barcode_not_found"));
|
||||||
barcode.setStatus("SCANNED");
|
barcode.setStatus("SCANNED");
|
||||||
barcode.setScanConfirmTime(LocalDateTime.now());
|
barcode.setScanConfirmTime(LocalDateTime.now());
|
||||||
barcode.setScanConfirmBy(confirmBy);
|
barcode.setScanConfirmBy(confirmBy);
|
||||||
@@ -88,7 +89,7 @@ public class SpecimenBarcodeController {
|
|||||||
@Transactional(rollbackFor = Exception.class)
|
@Transactional(rollbackFor = Exception.class)
|
||||||
public R<?> reject(@PathVariable Long id) {
|
public R<?> reject(@PathVariable Long id) {
|
||||||
SpecimenBarcode barcode = barcodeService.getById(id);
|
SpecimenBarcode barcode = barcodeService.getById(id);
|
||||||
if (barcode == null) return R.fail("条码不存在");
|
if (barcode == null) return R.fail(MessageUtils.message("his.specimen.barcode_not_found"));
|
||||||
barcode.setStatus("REJECTED");
|
barcode.setStatus("REJECTED");
|
||||||
barcodeService.updateById(barcode);
|
barcodeService.updateById(barcode);
|
||||||
return R.ok(barcode);
|
return R.ok(barcode);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.AjaxResult;
|
import com.core.common.core.domain.AjaxResult;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.clinical.domain.*;
|
import com.healthlink.his.clinical.domain.*;
|
||||||
import com.healthlink.his.clinical.service.*;
|
import com.healthlink.his.clinical.service.*;
|
||||||
import com.healthlink.his.web.clinical.appservice.IClinicalPathwayAppService;
|
import com.healthlink.his.web.clinical.appservice.IClinicalPathwayAppService;
|
||||||
@@ -42,7 +43,7 @@ public class ClinicalPathwayController {
|
|||||||
public R<?> completePathway(@PathVariable Long id) {
|
public R<?> completePathway(@PathVariable Long id) {
|
||||||
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
|
||||||
qw.eq(ClinicalPathwayExecution::getPathwayId, id).eq(ClinicalPathwayExecution::getStatus, "IN_PATH").orderByDesc(ClinicalPathwayExecution::getCreateTime).last("LIMIT 1");
|
qw.eq(ClinicalPathwayExecution::getPathwayId, id).eq(ClinicalPathwayExecution::getStatus, "IN_PATH").orderByDesc(ClinicalPathwayExecution::getCreateTime).last("LIMIT 1");
|
||||||
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail("执行记录不存在");
|
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail(MessageUtils.message("his.clinical.execution_not_found"));
|
||||||
e.setStatus("COMPLETED"); e.setCompleteDate(java.time.LocalDate.now());
|
e.setStatus("COMPLETED"); e.setCompleteDate(java.time.LocalDate.now());
|
||||||
executionService.updateById(e); return R.ok();
|
executionService.updateById(e); return R.ok();
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ public class ClinicalPathwayController {
|
|||||||
public R<?> varyPathway(@PathVariable Long id, @RequestParam("reason") String reason) {
|
public R<?> varyPathway(@PathVariable Long id, @RequestParam("reason") String reason) {
|
||||||
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<ClinicalPathwayExecution> qw = new LambdaQueryWrapper<>();
|
||||||
qw.eq(ClinicalPathwayExecution::getPathwayId, id).eq(ClinicalPathwayExecution::getStatus, "IN_PATH").orderByDesc(ClinicalPathwayExecution::getCreateTime).last("LIMIT 1");
|
qw.eq(ClinicalPathwayExecution::getPathwayId, id).eq(ClinicalPathwayExecution::getStatus, "IN_PATH").orderByDesc(ClinicalPathwayExecution::getCreateTime).last("LIMIT 1");
|
||||||
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail("执行记录不存在");
|
ClinicalPathwayExecution e = executionService.getOne(qw); if (e == null) return R.fail(MessageUtils.message("his.clinical.execution_not_found"));
|
||||||
e.setStatus("VARIATION"); e.setVariationReason(reason); executionService.updateById(e); return R.ok();
|
e.setStatus("VARIATION"); e.setVariationReason(reason); executionService.updateById(e); return R.ok();
|
||||||
}
|
}
|
||||||
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
|
@PreAuthorize("@ss.hasPermi('inpatient:clinical:list')")
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 缓存中没有,从数据库查询
|
// 缓存中没有,从数据库查询
|
||||||
SurgeryDto surgeryDto = surgeryAppMapper.getSurgeryDetail(id);
|
SurgeryDto surgeryDto = surgeryAppMapper.getSurgeryDetail(id);
|
||||||
if (surgeryDto == null) {
|
if (surgeryDto == null) {
|
||||||
return R.fail("手术信息不存在");
|
return R.fail(MessageUtils.message("surgery.info.not.exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从申请单中获取次要手术信息
|
// 从申请单中获取次要手术信息
|
||||||
@@ -257,24 +257,24 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 校验患者是否存在
|
// 校验患者是否存在
|
||||||
Patient patient = patientService.getById(surgeryDto.getPatientId());
|
Patient patient = patientService.getById(surgeryDto.getPatientId());
|
||||||
if (patient == null) {
|
if (patient == null) {
|
||||||
return R.fail("患者信息不存在");
|
return R.fail(MessageUtils.message("surgery.patient.not.exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验就诊ID是否存在
|
// 校验就诊ID是否存在
|
||||||
if (surgeryDto.getEncounterId() == null) {
|
if (surgeryDto.getEncounterId() == null) {
|
||||||
return R.fail("请选择就诊流水号");
|
return R.fail(MessageUtils.message("surgery.select.encounter"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验就诊记录是否存在
|
// 校验就诊记录是否存在
|
||||||
Encounter encounter = encounterService.getById(surgeryDto.getEncounterId());
|
Encounter encounter = encounterService.getById(surgeryDto.getEncounterId());
|
||||||
if (encounter == null) {
|
if (encounter == null) {
|
||||||
return R.fail("就诊记录不存在");
|
return R.fail(MessageUtils.message("surgery.encounter.not.exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取患者的自费账户ID
|
// 获取患者的自费账户ID
|
||||||
Long accountId = accountService.getSelfPayAccount(surgeryDto.getEncounterId());
|
Long accountId = accountService.getSelfPayAccount(surgeryDto.getEncounterId());
|
||||||
if (accountId == null) {
|
if (accountId == null) {
|
||||||
return R.fail("未找到患者的账户信息,请先完成挂号或住院登记");
|
return R.fail(MessageUtils.message("surgery.account.not.found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 当前登录账号的科室id
|
// 当前登录账号的科室id
|
||||||
@@ -443,7 +443,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
surgeryDto.getEncounterId(), surgeryDto.getPatientId(),
|
surgeryDto.getEncounterId(), surgeryDto.getPatientId(),
|
||||||
surgeryId, surgeryDto.getSurgeryName()));
|
surgeryId, surgeryDto.getSurgeryName()));
|
||||||
|
|
||||||
return R.ok(surgeryId, "手术申请提交成功!");
|
return R.ok(surgeryId, MessageUtils.message("his.surgery.submit_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -484,7 +484,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 校验手术是否存在
|
// 校验手术是否存在
|
||||||
Surgery existSurgery = surgeryService.getById(surgeryDto.getId());
|
Surgery existSurgery = surgeryService.getById(surgeryDto.getId());
|
||||||
if (existSurgery == null) {
|
if (existSurgery == null) {
|
||||||
return R.fail("手术信息不存在");
|
return R.fail(MessageUtils.message("surgery.info.not.exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为实体对象
|
// 转换为实体对象
|
||||||
@@ -522,7 +522,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 清除相关缓存
|
// 清除相关缓存
|
||||||
clearSurgeryAppCache(surgery);
|
clearSurgeryAppCache(surgery);
|
||||||
|
|
||||||
return R.ok(null, "手术申请修改成功!");
|
return R.ok(null, MessageUtils.message("his.surgery.update_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -537,12 +537,12 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 校验手术是否存在
|
// 校验手术是否存在
|
||||||
Surgery existSurgery = surgeryService.getById(id);
|
Surgery existSurgery = surgeryService.getById(id);
|
||||||
if (existSurgery == null) {
|
if (existSurgery == null) {
|
||||||
return R.fail("手术信息不存在");
|
return R.fail(MessageUtils.message("surgery.info.not.exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 已完成的手术不能删除
|
// 已完成的手术不能删除
|
||||||
if (existSurgery.getStatusEnum() == 3) {
|
if (existSurgery.getStatusEnum() == 3) {
|
||||||
return R.fail("已完成的手术不能删除");
|
return R.fail(MessageUtils.message("surgery.completed.cannot.delete"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 级联删除关联数据
|
// 级联删除关联数据
|
||||||
@@ -587,7 +587,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
// 校验手术是否存在
|
// 校验手术是否存在
|
||||||
Surgery existSurgery = surgeryService.getById(id);
|
Surgery existSurgery = surgeryService.getById(id);
|
||||||
if (existSurgery == null) {
|
if (existSurgery == null) {
|
||||||
return R.fail("手术信息不存在");
|
return R.fail(MessageUtils.message("surgery.info.not.exist"));
|
||||||
}
|
}
|
||||||
|
|
||||||
surgeryService.updateSurgeryStatus(id, statusEnum);
|
surgeryService.updateSurgeryStatus(id, statusEnum);
|
||||||
@@ -607,7 +607,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
@Override
|
@Override
|
||||||
public R<List<Encounter>> getEncounterListByPatientId(Long patientId) {
|
public R<List<Encounter>> getEncounterListByPatientId(Long patientId) {
|
||||||
if (patientId == null) {
|
if (patientId == null) {
|
||||||
return R.fail("患者ID不能为空");
|
return R.fail(MessageUtils.message("surgery.patient.id.empty"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询该患者的所有就诊记录(进行中的优先)
|
// 查询该患者的所有就诊记录(进行中的优先)
|
||||||
@@ -620,7 +620,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
List<Encounter> encounterList = encounterService.list(wrapper);
|
List<Encounter> encounterList = encounterService.list(wrapper);
|
||||||
|
|
||||||
if (encounterList == null || encounterList.isEmpty()) {
|
if (encounterList == null || encounterList.isEmpty()) {
|
||||||
return R.fail("该患者暂无就诊记录,请先挂号或办理住院");
|
return R.fail(MessageUtils.message("surgery.no.encounter.record"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.ok(encounterList);
|
return R.ok(encounterList);
|
||||||
@@ -861,7 +861,7 @@ public class SurgeryAppServiceImpl implements ISurgeryAppService {
|
|||||||
.ne(excludeId != null, Surgery::getId, excludeId);
|
.ne(excludeId != null, Surgery::getId, excludeId);
|
||||||
long conflictCount = surgeryService.count(wrapper);
|
long conflictCount = surgeryService.count(wrapper);
|
||||||
if (conflictCount > 0) {
|
if (conflictCount > 0) {
|
||||||
return R.fail("该手术室在该时间段已有手术安排,请选择其他时间或手术室");
|
return R.fail(MessageUtils.message("surgery.room.time.conflict"));
|
||||||
}
|
}
|
||||||
return R.ok(false);
|
return R.ok(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
|
|||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
import com.core.common.core.domain.model.LoginUser;
|
import com.core.common.core.domain.model.LoginUser;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
import com.healthlink.his.administration.domain.Patient;
|
import com.healthlink.his.administration.domain.Patient;
|
||||||
import com.healthlink.his.administration.service.IPatientService;
|
import com.healthlink.his.administration.service.IPatientService;
|
||||||
@@ -92,7 +93,7 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
public R<OpScheduleDto> getSurgeryScheduleDetail(Long scheduleId) {
|
public R<OpScheduleDto> getSurgeryScheduleDetail(Long scheduleId) {
|
||||||
OpScheduleDto opScheduleDto = surgicalScheduleAppMapper.getSurgeryScheduleDetail(scheduleId);
|
OpScheduleDto opScheduleDto = surgicalScheduleAppMapper.getSurgeryScheduleDetail(scheduleId);
|
||||||
if (opScheduleDto == null) {
|
if (opScheduleDto == null) {
|
||||||
return R.fail("手术安排信息不存在");
|
return R.fail(MessageUtils.message("his.surgical_schedule.not_found"));
|
||||||
}
|
}
|
||||||
return R.ok(opScheduleDto);
|
return R.ok(opScheduleDto);
|
||||||
}
|
}
|
||||||
@@ -110,7 +111,7 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
if (opCreateScheduleDto.getPatientId() != null) {
|
if (opCreateScheduleDto.getPatientId() != null) {
|
||||||
Patient patient = patientService.getById(opCreateScheduleDto.getPatientId());
|
Patient patient = patientService.getById(opCreateScheduleDto.getPatientId());
|
||||||
if (patient == null) {
|
if (patient == null) {
|
||||||
return R.fail("患者信息不存在");
|
return R.fail(MessageUtils.message("his.surgical_schedule.patient_not_found"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +126,7 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
opCreateScheduleDto.getOperName()
|
opCreateScheduleDto.getOperName()
|
||||||
);
|
);
|
||||||
if (existsDuplicate != null && existsDuplicate) {
|
if (existsDuplicate != null && existsDuplicate) {
|
||||||
return R.fail("该患者此手术单号已存在手术安排,请勿重复提交");
|
return R.fail(MessageUtils.message("his.surgical_schedule.duplicate_schedule"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,14 +137,14 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
if (startTime != null && endTime != null && roomCode != null && !roomCode.isEmpty()) {
|
if (startTime != null && endTime != null && roomCode != null && !roomCode.isEmpty()) {
|
||||||
Boolean scheduleConflict = surgicalScheduleAppMapper.isScheduleConflict(startTime, endTime, roomCode);
|
Boolean scheduleConflict = surgicalScheduleAppMapper.isScheduleConflict(startTime, endTime, roomCode);
|
||||||
if (scheduleConflict != null && scheduleConflict) {
|
if (scheduleConflict != null && scheduleConflict) {
|
||||||
return R.fail("该时段内手术间被占用");
|
return R.fail(MessageUtils.message("his.surgical_schedule.room_occupied"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bug #432 修复:获取当前登录用户信息,增加null校验防止NPE
|
// Bug #432 修复:获取当前登录用户信息,增加null校验防止NPE
|
||||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||||
if (loginUser == null) {
|
if (loginUser == null) {
|
||||||
return R.fail("用户未登录或登录已过期");
|
return R.fail(MessageUtils.message("his.surgical_schedule.user_not_logged_in"));
|
||||||
}
|
}
|
||||||
// 当前登录用户ID
|
// 当前登录用户ID
|
||||||
Long userId = loginUser.getUserId();
|
Long userId = loginUser.getUserId();
|
||||||
@@ -205,7 +206,7 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
// 保存手术安排
|
// 保存手术安排
|
||||||
boolean saved = opScheduleService.save(opSchedule);
|
boolean saved = opScheduleService.save(opSchedule);
|
||||||
if (!saved) {
|
if (!saved) {
|
||||||
return R.fail("新增手术安排失败");
|
return R.fail(MessageUtils.message("his.surgical_schedule.add_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
syncSurgeryIncisionLevel(opSchedule.getOperCode(), opCreateScheduleDto.getIncisionLevel());
|
syncSurgeryIncisionLevel(opSchedule.getOperCode(), opCreateScheduleDto.getIncisionLevel());
|
||||||
@@ -255,7 +256,7 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.ok("新增手术安排成功");
|
return R.ok(MessageUtils.message("his.surgical.add_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -269,11 +270,11 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
public R<?> updateSurgerySchedule(OpScheduleDto opScheduleDto) {
|
public R<?> updateSurgerySchedule(OpScheduleDto opScheduleDto) {
|
||||||
// 校验手术安排是否存在
|
// 校验手术安排是否存在
|
||||||
if (opScheduleDto.getScheduleId() == null) {
|
if (opScheduleDto.getScheduleId() == null) {
|
||||||
return R.fail("排程号不能为空");
|
return R.fail(MessageUtils.message("his.surgical_schedule.schedule_id_required"));
|
||||||
}
|
}
|
||||||
OpSchedule existSchedule = opScheduleService.getById(opScheduleDto.getScheduleId());
|
OpSchedule existSchedule = opScheduleService.getById(opScheduleDto.getScheduleId());
|
||||||
if (existSchedule == null) {
|
if (existSchedule == null) {
|
||||||
return R.fail("手术安排信息不存在");
|
return R.fail(MessageUtils.message("his.surgical_schedule.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为实体对象
|
// 转换为实体对象
|
||||||
@@ -324,12 +325,12 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
// 更新手术安排
|
// 更新手术安排
|
||||||
boolean updated = opScheduleService.updateById(opSchedule);
|
boolean updated = opScheduleService.updateById(opSchedule);
|
||||||
if (!updated) {
|
if (!updated) {
|
||||||
return R.fail("修改手术安排失败");
|
return R.fail(MessageUtils.message("his.surgical_schedule.update_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
syncSurgeryIncisionLevel(opScheduleDto.getOperCode(), opScheduleDto.getIncisionLevel());
|
syncSurgeryIncisionLevel(opScheduleDto.getOperCode(), opScheduleDto.getIncisionLevel());
|
||||||
|
|
||||||
return R.ok("修改手术安排成功");
|
return R.ok(MessageUtils.message("his.surgical.update_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -344,16 +345,16 @@ public class SurgicalScheduleAppServiceImpl implements ISurgicalScheduleAppServi
|
|||||||
// 校验手术安排是否存在
|
// 校验手术安排是否存在
|
||||||
OpSchedule existSchedule = opScheduleService.getById(scheduleId);
|
OpSchedule existSchedule = opScheduleService.getById(scheduleId);
|
||||||
if (existSchedule == null) {
|
if (existSchedule == null) {
|
||||||
return R.fail("手术安排信息不存在");
|
return R.fail(MessageUtils.message("his.surgical_schedule.not_found"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 逻辑删除手术安排
|
// 逻辑删除手术安排
|
||||||
boolean deleted = opScheduleService.removeById(scheduleId);
|
boolean deleted = opScheduleService.removeById(scheduleId);
|
||||||
if (!deleted) {
|
if (!deleted) {
|
||||||
return R.fail("删除手术安排失败");
|
return R.fail(MessageUtils.message("his.surgical_schedule.delete_failed"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return R.ok(null, "删除手术安排成功");
|
return R.ok(null, MessageUtils.message("his.surgical.delete_success"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.clinicalmanage.controller;
|
|||||||
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.common.enums.ActivityDefCategory;
|
import com.healthlink.his.common.enums.ActivityDefCategory;
|
||||||
import com.healthlink.his.web.clinicalmanage.appservice.ISurgicalScheduleAppService;
|
import com.healthlink.his.web.clinicalmanage.appservice.ISurgicalScheduleAppService;
|
||||||
import com.healthlink.his.web.clinicalmanage.dto.OpCreateScheduleDto;
|
import com.healthlink.his.web.clinicalmanage.dto.OpCreateScheduleDto;
|
||||||
@@ -137,9 +138,9 @@ public class SurgicalScheduleController {
|
|||||||
com.core.common.core.domain.model.LoginUser loginUser = com.core.common.utils.SecurityUtils.getLoginUser();
|
com.core.common.core.domain.model.LoginUser loginUser = com.core.common.utils.SecurityUtils.getLoginUser();
|
||||||
String encodedPassword = loginUser.getPassword();
|
String encodedPassword = loginUser.getPassword();
|
||||||
if (com.core.common.utils.SecurityUtils.matchesPassword(password, encodedPassword)) {
|
if (com.core.common.utils.SecurityUtils.matchesPassword(password, encodedPassword)) {
|
||||||
return R.ok(true, "密码验证成功");
|
return R.ok(true, MessageUtils.message("his.surgical.password_verify_success"));
|
||||||
} else {
|
} else {
|
||||||
return R.fail(false, "账户密码错误,请重新输入");
|
return R.fail(false, MessageUtils.message("his.surgical.password_verify_failed"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.core.common.enums.DelFlag;
|
|||||||
import com.core.common.utils.AgeCalculatorUtil;
|
import com.core.common.utils.AgeCalculatorUtil;
|
||||||
import com.core.common.utils.AssignSeqUtil;
|
import com.core.common.utils.AssignSeqUtil;
|
||||||
import com.core.common.utils.SecurityUtils;
|
import com.core.common.utils.SecurityUtils;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.core.common.utils.StringUtils;
|
import com.core.common.utils.StringUtils;
|
||||||
import com.core.common.utils.bean.BeanUtils;
|
import com.core.common.utils.bean.BeanUtils;
|
||||||
import com.healthlink.his.administration.domain.Location;
|
import com.healthlink.his.administration.domain.Location;
|
||||||
@@ -311,7 +312,7 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
// 医保编码和生产厂家校验
|
// 医保编码和生产厂家校验
|
||||||
for (LocationInventoryDto dto : locationInventoryDtoList) {
|
for (LocationInventoryDto dto : locationInventoryDtoList) {
|
||||||
if (StringUtils.isNotEmpty(dto.getYbNo()) && StringUtils.isEmpty(dto.getManufacturerText())) {
|
if (StringUtils.isNotEmpty(dto.getYbNo()) && StringUtils.isEmpty(dto.getManufacturerText())) {
|
||||||
return R.fail("生产厂家不能为空,请到药品目录维护");
|
return R.fail(MessageUtils.message("common.manufacturer.required"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return R.ok(locationInventoryDtoList);
|
return R.ok(locationInventoryDtoList);
|
||||||
@@ -521,9 +522,9 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 方式 3:如果仍然没有病区,且 currentOrgId 为空,尝试获取所有病区
|
// 方式 3:如果仍然没有病区,尝试获取所有活动病区
|
||||||
if (wardList.isEmpty() && currentOrgId == null) {
|
if (wardList.isEmpty()) {
|
||||||
log.info("getPractitionerWard - 尝试获取所有病区");
|
log.info("getPractitionerWard - 未查到指定病区,获取所有病区");
|
||||||
List<Location> allWards = locationService.getWardList(null);
|
List<Location> allWards = locationService.getWardList(null);
|
||||||
log.info("getPractitionerWard - 所有病区数:{}", allWards != null ? allWards.size() : 0);
|
log.info("getPractitionerWard - 所有病区数:{}", allWards != null ? allWards.size() : 0);
|
||||||
if (allWards != null && !allWards.isEmpty()) {
|
if (allWards != null && !allWards.isEmpty()) {
|
||||||
@@ -700,7 +701,7 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
List<InventoryDetailDto> inventoryDetailList = commonAppMapper
|
List<InventoryDetailDto> inventoryDetailList = commonAppMapper
|
||||||
.selectMedicineInventoryDetail(medicationIdList, locationIdList, PublicationStatus.ACTIVE.getValue());
|
.selectMedicineInventoryDetail(medicationIdList, locationIdList, PublicationStatus.ACTIVE.getValue());
|
||||||
if (inventoryDetailList == null || inventoryDetailList.isEmpty()) {
|
if (inventoryDetailList == null || inventoryDetailList.isEmpty()) {
|
||||||
return R.fail("发药单生成失败,请检查药品库存");
|
return R.fail(MessageUtils.message("common.drug.dispense.fail.check.stock"));
|
||||||
}
|
}
|
||||||
// 将库存信息根据药品id和库房id进行分组
|
// 将库存信息根据药品id和库房id进行分组
|
||||||
Map<String, List<InventoryDetailDto>> inventoryDetailMap = inventoryDetailList.stream()
|
Map<String, List<InventoryDetailDto>> inventoryDetailMap = inventoryDetailList.stream()
|
||||||
@@ -720,7 +721,7 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
+ medicationDispense.getLocationId();
|
+ medicationDispense.getLocationId();
|
||||||
// 查询对应的库存信息
|
// 查询对应的库存信息
|
||||||
if (!inventoryDetailMap.containsKey(inventoryKey)) {
|
if (!inventoryDetailMap.containsKey(inventoryKey)) {
|
||||||
return R.fail("药品库存不存在,药品ID: " + medicationDispense.getMedicationId());
|
return R.fail(MessageUtils.message("common.drug.stock.not.exist", medicationDispense.getMedicationId()));
|
||||||
}
|
}
|
||||||
List<InventoryDetailDto> inventoryList = inventoryDetailMap.get(inventoryKey);
|
List<InventoryDetailDto> inventoryList = inventoryDetailMap.get(inventoryKey);
|
||||||
|
|
||||||
@@ -788,13 +789,13 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
// 校验药品库存
|
// 校验药品库存
|
||||||
for (MedicationDispense medicationDispense : medicationDispenseList) {
|
for (MedicationDispense medicationDispense : medicationDispenseList) {
|
||||||
if (medicationDispense.getLotNumber() == null) {
|
if (medicationDispense.getLotNumber() == null) {
|
||||||
return R.fail("药品:" + medicationDispense.getMedicationId() + "库存不足,请检查药品库存");
|
return R.fail(MessageUtils.message("common.drug.stock.insufficient", medicationDispense.getMedicationId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 新增或更新发药单
|
// 新增或更新发药单
|
||||||
boolean result = medicationDispenseService.saveOrUpdateBatch(medicationDispenseList);
|
boolean result = medicationDispenseService.saveOrUpdateBatch(medicationDispenseList);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return R.fail("发药单生成失败,请联系管理员");
|
return R.fail(MessageUtils.message("common.drug.dispense.fail.contact.admin"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 查询患者待发放的耗材信息
|
// 查询患者待发放的耗材信息
|
||||||
@@ -815,7 +816,7 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
List<InventoryDetailDto> inventoryDetailList = commonAppMapper.selectDeviceInventoryDetail(deviceIdList,
|
List<InventoryDetailDto> inventoryDetailList = commonAppMapper.selectDeviceInventoryDetail(deviceIdList,
|
||||||
locationIdList, PublicationStatus.ACTIVE.getValue());
|
locationIdList, PublicationStatus.ACTIVE.getValue());
|
||||||
if (inventoryDetailList == null || inventoryDetailList.isEmpty()) {
|
if (inventoryDetailList == null || inventoryDetailList.isEmpty()) {
|
||||||
return R.fail("发耗材单生成失败,请检查耗材库存");
|
return R.fail(MessageUtils.message("common.device.dispense.fail.check.stock"));
|
||||||
}
|
}
|
||||||
// 将库存信息根据耗材id和库房id进行分组
|
// 将库存信息根据耗材id和库房id进行分组
|
||||||
Map<String, List<InventoryDetailDto>> inventoryDetailMap = inventoryDetailList.stream()
|
Map<String, List<InventoryDetailDto>> inventoryDetailMap = inventoryDetailList.stream()
|
||||||
@@ -835,7 +836,7 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
= deviceDispense.getDeviceDefId() + CommonConstants.Common.DASH + deviceDispense.getLocationId();
|
= deviceDispense.getDeviceDefId() + CommonConstants.Common.DASH + deviceDispense.getLocationId();
|
||||||
// 查询对应的库存信息
|
// 查询对应的库存信息
|
||||||
if (!inventoryDetailMap.containsKey(inventoryKey)) {
|
if (!inventoryDetailMap.containsKey(inventoryKey)) {
|
||||||
return R.fail("耗材库存不存在,耗材ID: " + deviceDispense.getDeviceDefId());
|
return R.fail(MessageUtils.message("common.device.stock.not.exist", deviceDispense.getDeviceDefId()));
|
||||||
}
|
}
|
||||||
List<InventoryDetailDto> inventoryList = inventoryDetailMap.get(inventoryKey);
|
List<InventoryDetailDto> inventoryList = inventoryDetailMap.get(inventoryKey);
|
||||||
|
|
||||||
@@ -903,13 +904,13 @@ public class CommonServiceImpl implements ICommonService {
|
|||||||
// 校验耗材库存
|
// 校验耗材库存
|
||||||
for (DeviceDispense deviceDispense : deviceDispenseList) {
|
for (DeviceDispense deviceDispense : deviceDispenseList) {
|
||||||
if (deviceDispense.getLotNumber() == null) {
|
if (deviceDispense.getLotNumber() == null) {
|
||||||
return R.fail("耗材:" + deviceDispense.getDeviceDefId() + "库存不足,请检查耗材库存");
|
return R.fail(MessageUtils.message("common.device.stock.insufficient", deviceDispense.getDeviceDefId()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 新增或更新发耗材单
|
// 新增或更新发耗材单
|
||||||
boolean result = deviceDispenseService.saveOrUpdateBatch(deviceDispenseList);
|
boolean result = deviceDispenseService.saveOrUpdateBatch(deviceDispenseList);
|
||||||
if (!result) {
|
if (!result) {
|
||||||
return R.fail("发耗材单生成失败,请联系管理员");
|
return R.fail(MessageUtils.message("common.device.dispense.fail.contact.admin"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return R.ok();
|
return R.ok();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.healthlink.his.web.consultation.controller;
|
package com.healthlink.his.web.consultation.controller;
|
||||||
|
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.web.consultation.appservice.IConsultationAppService;
|
import com.healthlink.his.web.consultation.appservice.IConsultationAppService;
|
||||||
import com.healthlink.his.web.consultation.dto.ConsultationActivityDto;
|
import com.healthlink.his.web.consultation.dto.ConsultationActivityDto;
|
||||||
import com.healthlink.his.web.consultation.dto.ConsultationConfirmationDto;
|
import com.healthlink.his.web.consultation.dto.ConsultationConfirmationDto;
|
||||||
@@ -46,7 +47,7 @@ public class ConsultationController {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取会诊列表失败", e);
|
log.error("获取会诊列表失败", e);
|
||||||
return R.fail("获取会诊列表失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_list_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ public class ConsultationController {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("查询会诊申请列表失败", e);
|
log.error("查询会诊申请列表失败", e);
|
||||||
return R.fail("查询会诊申请列表失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.query_list_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +82,7 @@ public class ConsultationController {
|
|||||||
return R.ok(page);
|
return R.ok(page);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("分页查询会诊申请列表失败", e);
|
log.error("分页查询会诊申请列表失败", e);
|
||||||
return R.fail("分页查询会诊申请列表失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.query_page_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,10 +94,10 @@ public class ConsultationController {
|
|||||||
public R<String> saveConsultation(@RequestBody ConsultationRequestDto dto) {
|
public R<String> saveConsultation(@RequestBody ConsultationRequestDto dto) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.saveConsultation(dto);
|
Boolean result = consultationAppService.saveConsultation(dto);
|
||||||
return result ? R.ok("保存成功") : R.fail("保存失败");
|
return result ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("保存会诊申请失败", e);
|
log.error("保存会诊申请失败", e);
|
||||||
return R.fail("保存会诊申请失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.save_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,10 +110,10 @@ public class ConsultationController {
|
|||||||
@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.submitConsultation(consultationId);
|
Boolean result = consultationAppService.submitConsultation(consultationId);
|
||||||
return result ? R.ok("提交成功") : R.fail("提交失败");
|
return result ? R.ok(MessageUtils.message("msg.success")) : R.fail(MessageUtils.message("msg.failure"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("提交会诊申请失败", e);
|
log.error("提交会诊申请失败", e);
|
||||||
return R.fail("提交会诊申请失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.submit_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,10 +127,10 @@ public class ConsultationController {
|
|||||||
@Parameter(description = "作废原因") @RequestParam(required = false) String cancelReason) {
|
@Parameter(description = "作废原因") @RequestParam(required = false) String cancelReason) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.cancelConsultation(consultationId, cancelReason);
|
Boolean result = consultationAppService.cancelConsultation(consultationId, cancelReason);
|
||||||
return result ? R.ok("作废成功") : R.fail("作废失败");
|
return result ? R.ok(MessageUtils.message("his.consultation.void_success")) : R.fail(MessageUtils.message("his.consultation.void_failed"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("作废会诊申请失败", e);
|
log.error("作废会诊申请失败", e);
|
||||||
return R.fail("作废会诊申请失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.void_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,10 +143,10 @@ public class ConsultationController {
|
|||||||
@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.completeConsultation(consultationId);
|
Boolean result = consultationAppService.completeConsultation(consultationId);
|
||||||
return result ? R.ok("结束成功") : R.fail("结束失败");
|
return result ? R.ok(MessageUtils.message("his.consultation.complete_success")) : R.fail(MessageUtils.message("his.consultation.complete_failed"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("结束会诊申请失败", e);
|
log.error("结束会诊申请失败", e);
|
||||||
return R.fail("结束会诊申请失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.complete_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +161,7 @@ public class ConsultationController {
|
|||||||
return R.ok(tree);
|
return R.ok(tree);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取科室医生树失败", e);
|
log.error("获取科室医生树失败", e);
|
||||||
return R.fail("获取科室医生树失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_dept_tree_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,7 +177,7 @@ public class ConsultationController {
|
|||||||
return R.ok(diagnosis);
|
return R.ok(diagnosis);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取主诊断失败", e);
|
log.error("获取主诊断失败", e);
|
||||||
return R.fail("获取主诊断失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_diagnosis_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +192,7 @@ public class ConsultationController {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取我的会诊邀请失败", e);
|
log.error("获取我的会诊邀请失败", e);
|
||||||
return R.fail("获取我的会诊邀请失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_invitations_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,7 +207,7 @@ public class ConsultationController {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取会诊项目列表失败", e);
|
log.error("获取会诊项目列表失败", e);
|
||||||
return R.fail("获取会诊项目列表失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_activities_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +224,7 @@ public class ConsultationController {
|
|||||||
return R.ok(list);
|
return R.ok(list);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取待确认会诊列表失败", e);
|
log.error("获取待确认会诊列表失败", e);
|
||||||
return R.fail("获取待确认会诊列表失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_pending_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,10 +236,10 @@ public class ConsultationController {
|
|||||||
public R<String> confirmConsultation(@RequestBody ConsultationConfirmationDto dto) {
|
public R<String> confirmConsultation(@RequestBody ConsultationConfirmationDto dto) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.confirmConsultation(dto);
|
Boolean result = consultationAppService.confirmConsultation(dto);
|
||||||
return result ? R.ok("确认成功") : R.fail("确认失败");
|
return result ? R.ok(MessageUtils.message("his.consultation.confirm_success")) : R.fail(MessageUtils.message("his.consultation.confirm_failed"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("确认会诊失败", e);
|
log.error("确认会诊失败", e);
|
||||||
return R.fail("确认会诊失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.confirm_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,10 +251,10 @@ public class ConsultationController {
|
|||||||
public R<String> cancelConfirmation(@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
public R<String> cancelConfirmation(@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.cancelConfirmation(consultationId);
|
Boolean result = consultationAppService.cancelConfirmation(consultationId);
|
||||||
return result ? R.ok("取消确认成功") : R.fail("取消确认失败");
|
return result ? R.ok(MessageUtils.message("his.consultation.cancel_confirm_success")) : R.fail(MessageUtils.message("his.consultation.cancel_confirm_failed"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("取消确认会诊失败", e);
|
log.error("取消确认会诊失败", e);
|
||||||
return R.fail("取消确认会诊失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.cancel_confirm_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,10 +266,10 @@ public class ConsultationController {
|
|||||||
public R<String> signConsultation(@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
public R<String> signConsultation(@Parameter(description = "会诊申请单号") @RequestParam String consultationId) {
|
||||||
try {
|
try {
|
||||||
Boolean result = consultationAppService.signConsultation(consultationId);
|
Boolean result = consultationAppService.signConsultation(consultationId);
|
||||||
return result ? R.ok("签名成功") : R.fail("签名失败");
|
return result ? R.ok(MessageUtils.message("his.consultation.sign_success")) : R.fail(MessageUtils.message("his.consultation.sign_failed"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("签名会诊失败", e);
|
log.error("签名会诊失败", e);
|
||||||
return R.fail("签名会诊失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.sign_error", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -284,7 +285,7 @@ public class ConsultationController {
|
|||||||
return R.ok(detail);
|
return R.ok(detail);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取会诊确认详情失败", e);
|
log.error("获取会诊确认详情失败", e);
|
||||||
return R.fail("获取会诊确认详情失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_detail_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,7 +301,7 @@ public class ConsultationController {
|
|||||||
return R.ok(opinions);
|
return R.ok(opinions);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取会诊意见列表失败", e);
|
log.error("获取会诊意见列表失败", e);
|
||||||
return R.fail("获取会诊意见列表失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_opinions_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -316,7 +317,7 @@ public class ConsultationController {
|
|||||||
return R.ok(detail);
|
return R.ok(detail);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("查询会诊申请详情失败", e);
|
log.error("查询会诊申请详情失败", e);
|
||||||
return R.fail("查询会诊申请详情失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.query_detail_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -335,7 +336,7 @@ public class ConsultationController {
|
|||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("检查会诊时限失败", e);
|
log.error("检查会诊时限失败", e);
|
||||||
return R.fail("检查会诊时限失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.check_time_limit_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +351,7 @@ public class ConsultationController {
|
|||||||
return R.ok(stats);
|
return R.ok(stats);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("获取会诊时限统计失败", e);
|
log.error("获取会诊时限统计失败", e);
|
||||||
return R.fail("获取会诊时限统计失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.consultation.get_time_stats_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.healthlink.his.web.cssd.controller;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.cssd.domain.*;
|
import com.healthlink.his.cssd.domain.*;
|
||||||
import com.healthlink.his.cssd.service.*;
|
import com.healthlink.his.cssd.service.*;
|
||||||
import lombok.AllArgsConstructor;import lombok.extern.slf4j.Slf4j;
|
import lombok.AllArgsConstructor;import lombok.extern.slf4j.Slf4j;
|
||||||
@@ -46,7 +47,7 @@ public class CssdController {
|
|||||||
LambdaQueryWrapper<CssdTray> tw = new LambdaQueryWrapper<>();
|
LambdaQueryWrapper<CssdTray> tw = new LambdaQueryWrapper<>();
|
||||||
tw.eq(CssdTray::getTrayCode, trayCode);
|
tw.eq(CssdTray::getTrayCode, trayCode);
|
||||||
CssdTray tray = trayService.getOne(tw);
|
CssdTray tray = trayService.getOne(tw);
|
||||||
if (tray == null) return R.fail("器械包不存在: " + trayCode);
|
if (tray == null) return R.fail(MessageUtils.message("his.cssd.tray_not_found", trayCode));
|
||||||
|
|
||||||
// 更新器械包状态
|
// 更新器械包状态
|
||||||
Map<String, String> statusFlow = Map.of(
|
Map<String, String> statusFlow = Map.of(
|
||||||
@@ -113,7 +114,7 @@ public class CssdController {
|
|||||||
}
|
}
|
||||||
@PutMapping("/sterilize/complete/{id}") @Transactional(rollbackFor=Exception.class)
|
@PutMapping("/sterilize/complete/{id}") @Transactional(rollbackFor=Exception.class)
|
||||||
public R<?> completeBatch(@PathVariable Long id, @RequestBody Map<String, Object> params) {
|
public R<?> completeBatch(@PathVariable Long id, @RequestBody Map<String, Object> params) {
|
||||||
CssdSterilizeBatch b = batchService.getById(id); if (b == null) return R.fail("批次不存在");
|
CssdSterilizeBatch b = batchService.getById(id); if (b == null) return R.fail(MessageUtils.message("his.cssd.batch_not_found"));
|
||||||
b.setEndTime(new Date());
|
b.setEndTime(new Date());
|
||||||
b.setBiologicalResult((String) params.getOrDefault("biologicalResult", "PASS"));
|
b.setBiologicalResult((String) params.getOrDefault("biologicalResult", "PASS"));
|
||||||
b.setChemicalResult((String) params.getOrDefault("chemicalResult", "PASS"));
|
b.setChemicalResult((String) params.getOrDefault("chemicalResult", "PASS"));
|
||||||
@@ -123,9 +124,9 @@ public class CssdController {
|
|||||||
}
|
}
|
||||||
@PutMapping("/sterilize/release/{id}") @Transactional(rollbackFor=Exception.class)
|
@PutMapping("/sterilize/release/{id}") @Transactional(rollbackFor=Exception.class)
|
||||||
public R<?> releaseBatch(@PathVariable Long id, @RequestParam("releaseBy") String releaseBy) {
|
public R<?> releaseBatch(@PathVariable Long id, @RequestParam("releaseBy") String releaseBy) {
|
||||||
CssdSterilizeBatch b = batchService.getById(id); if (b == null) return R.fail("批次不存在");
|
CssdSterilizeBatch b = batchService.getById(id); if (b == null) return R.fail(MessageUtils.message("his.cssd.batch_not_found"));
|
||||||
if (!"PASS".equals(b.getBiologicalResult()) || !"PASS".equals(b.getChemicalResult()) || !"PASS".equals(b.getPhysicalResult())) {
|
if (!"PASS".equals(b.getBiologicalResult()) || !"PASS".equals(b.getChemicalResult()) || !"PASS".equals(b.getPhysicalResult())) {
|
||||||
return R.fail("三项监测未全部合格,禁止释放");
|
return R.fail(MessageUtils.message("his.cssd.monitor_not_passed"));
|
||||||
}
|
}
|
||||||
b.setBatchStatus("RELEASED"); b.setReleaseBy(releaseBy); b.setReleaseTime(new Date());
|
b.setBatchStatus("RELEASED"); b.setReleaseBy(releaseBy); b.setReleaseTime(new Date());
|
||||||
batchService.updateById(b); return R.ok(b);
|
batchService.updateById(b); return R.ok(b);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.healthlink.his.web.datacollection.appservice.impl;
|
package com.healthlink.his.web.datacollection.appservice.impl;
|
||||||
|
|
||||||
import com.core.common.core.domain.R;
|
import com.core.common.core.domain.R;
|
||||||
|
import com.core.common.utils.MessageUtils;
|
||||||
import com.healthlink.his.quality.service.IBusinessAnalyticsService;
|
import com.healthlink.his.quality.service.IBusinessAnalyticsService;
|
||||||
import com.healthlink.his.quality.domain.BusinessAnalytics;
|
import com.healthlink.his.quality.domain.BusinessAnalytics;
|
||||||
import com.healthlink.his.web.datacollection.appservice.IDataCollectionAppService;
|
import com.healthlink.his.web.datacollection.appservice.IDataCollectionAppService;
|
||||||
@@ -43,7 +44,7 @@ public class DataCollectionAppServiceImpl implements IDataCollectionAppService {
|
|||||||
log.error("临床数据采集失败", e);
|
log.error("临床数据采集失败", e);
|
||||||
result.put("status", "error");
|
result.put("status", "error");
|
||||||
result.put("message", "采集失败: " + e.getMessage());
|
result.put("message", "采集失败: " + e.getMessage());
|
||||||
return R.fail("采集失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.datacollect.collect_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
@@ -71,7 +72,7 @@ public class DataCollectionAppServiceImpl implements IDataCollectionAppService {
|
|||||||
log.error("运营数据采集失败", e);
|
log.error("运营数据采集失败", e);
|
||||||
result.put("status", "error");
|
result.put("status", "error");
|
||||||
result.put("message", "采集失败: " + e.getMessage());
|
result.put("message", "采集失败: " + e.getMessage());
|
||||||
return R.fail("采集失败: " + e.getMessage());
|
return R.fail(MessageUtils.message("his.datacollect.collect_failed", e.getMessage()));
|
||||||
}
|
}
|
||||||
return R.ok(result);
|
return R.ok(result);
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user