fix(#758): 请修复 Bug #758(诸葛亮分析完成,分配给你)

根因:
- Bug #请修复 Bug #758(诸葛亮分析完成,分配给你) 存在的问题

修复:
- 文件 1** — `core-common/.../DictUtils.java:39`
- `getDictCache()` 方法增加三层防御:
- 外层 `try-catch` 捕获所有异常,异常时自动清除损坏缓存并返回 null(降级为空字典,不报错)
- 支持 `JSONArray` 和普通 `List<?>` 两种 Redis 反序列化结果,兼容不同序列化格式
- 缓存数据类型异常时自动清除,下次读取会重建
- 文件 2** — `healthlink-his-common/.../DictAspect.java:128`
- `processDict()` 中 `queryDictLabel()` 调用增加字段级 `try-catch`:
- 单个字段字典翻译失败不再影响其他字段的处理
- 异常降级为 debug 日志,前端不受影响
- ### 验证
- `mvn compile -DskipTests` → **BUILD SUCCESS** 
- 无新增编译错误,WARNING 均为已有 unchecked cast
This commit is contained in:
2026-06-12 19:00:54 +08:00
parent adb088a263
commit 79214ee8b4
18 changed files with 49 additions and 1577 deletions

View File

@@ -1,7 +1,7 @@
# HealthLink-HIS 代码模块索引
> 供 LLM 快速定位代码。每个模块列出 Controller → Service → Mapper 关键文件。
> 最后更新: 2026-06-12 18:00 (298 个 Controller)
> 最后更新: 2026-06-13 12:00 (298 个 Controller)
## 关键词 → 模块速查

View File

@@ -1,62 +0,0 @@
# ============================================================
# OpenHIS 前端部署脚本 (Windows PowerShell)
# 用法: .\deploy-frontend.ps1 [-Env prod|test|staging|dev]
# ============================================================
param(
[ValidateSet("prod","test","staging","dev")]
[string]$Env = "prod"
)
$ErrorActionPreference = "Stop"
$ProjectDir = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$UiDir = "$ProjectDir\openhis-ui-vue3"
$DistDir = "$UiDir\dist"
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host " OpenHIS 前端部署" -ForegroundColor Cyan
Write-Host " 环境: $Env" -ForegroundColor Cyan
Write-Host " 目录: $UiDir" -ForegroundColor Cyan
Write-Host "==========================================" -ForegroundColor Cyan
# ---------- 1. 环境检查 ----------
Write-Host "`n[1/5] 环境检查..." -ForegroundColor Yellow
try { $nodeVer = node -v } catch { Write-Host "错误: 未找到 node" -ForegroundColor Red; exit 1 }
try { $npmVer = npm -v } catch { Write-Host "错误: 未找到 npm" -ForegroundColor Red; exit 1 }
$nodeMajor = [int]($nodeVer -replace 'v','' -split '\.')[0]
if ($nodeMajor -lt 18) {
Write-Host "错误: Node.js >= 18当前 $nodeVer" -ForegroundColor Red
exit 1
}
Write-Host " Node.js: $nodeVer"
Write-Host " npm: $npmVer"
# ---------- 2. 安装依赖 ----------
Write-Host "`n[2/5] 安装依赖..." -ForegroundColor Yellow
Set-Location $UiDir
npm install --legacy-peer-deps
Write-Host " 依赖安装完成 ✓" -ForegroundColor Green
# ---------- 3. 构建 ----------
Write-Host "`n[3/5] 构建 ($Env)..." -ForegroundColor Yellow
npm run "build:$Env"
Write-Host " 构建完成 ✓" -ForegroundColor Green
# ---------- 4. 产物信息 ----------
Write-Host "`n[4/5] 构建产物:" -ForegroundColor Yellow
$totalSize = (Get-ChildItem $DistDir -Recurse -File | Measure-Object -Property Length -Sum).Sum
$fileCount = (Get-ChildItem $DistDir -Recurse -File).Count
Write-Host " 路径: $DistDir"
Write-Host " 大小: $([math]::Round($totalSize/1MB, 2)) MB"
Write-Host " 文件: $fileCount"
# ---------- 5. 部署提示 ----------
Write-Host "`n[5/5] 后续操作:" -ForegroundColor Yellow
Write-Host ""
Write-Host "$DistDir 目录内容上传到服务器 Nginx 根目录"
Write-Host " 然后在服务器执行: nginx -s reload"
Write-Host ""
Write-Host "==========================================" -ForegroundColor Cyan
Write-Host " 构建完成!" -ForegroundColor Green
Write-Host "==========================================" -ForegroundColor Cyan

View File

@@ -1,84 +0,0 @@
#!/bin/bash
# ============================================================
# HealthLink-HIS 前端部署脚本
# 用法: bash deploy-frontend.sh [prod|test|staging|dev]
# 默认: prod
# ============================================================
set -e
MODE=${1:-prod}
PROJECT_DIR=$(cd "$(dirname "$0")/.." && pwd)
UI_DIR="$PROJECT_DIR/healthlink-his-ui"
DIST_DIR="$UI_DIR/dist"
echo "=========================================="
echo " HealthLink-HIS 前端部署"
echo " 环境: $MODE"
echo " 目录: $UI_DIR"
echo "=========================================="
# ---------- 1. 环境检查 ----------
echo ""
echo "[1/5] 环境检查..."
check_cmd() {
if ! command -v "$1" &> /dev/null; then
echo "错误: 未找到 $1,请先安装"
exit 1
fi
}
check_cmd node
check_cmd npm
NODE_VER=$(node -v | sed 's/v//' | cut -d. -f1)
if [ "$NODE_VER" -lt 18 ]; then
echo "错误: Node.js 版本需要 >= 18当前: $(node -v)"
exit 1
fi
echo " Node.js: $(node -v)"
echo " npm: $(npm -v)"
# ---------- 2. 安装依赖 ----------
echo ""
echo "[2/5] 安装依赖..."
cd "$UI_DIR"
# 清理旧的 node_modules可选取消注释启用
# echo " 清理旧依赖..."
# rm -rf node_modules package-lock.json
npm install --production=false --legacy-peer-deps
echo " 依赖安装完成 ✓"
# ---------- 3. 构建 ----------
echo ""
echo "[3/5] 构建 ($MODE)..."
npm run "build:$MODE"
echo " 构建完成 ✓"
# ---------- 4. 产物信息 ----------
echo ""
echo "[4/5] 构建产物:"
TOTAL_SIZE=$(du -sh "$DIST_DIR" 2>/dev/null | cut -f1)
FILE_COUNT=$(find "$DIST_DIR" -type f | wc -l)
echo " 路径: $DIST_DIR"
echo " 大小: $TOTAL_SIZE"
echo " 文件: $FILE_COUNT"
# ---------- 5. 部署提示 ----------
echo ""
echo "[5/5] 部署方式:"
echo ""
echo " 方式一: 复制到 Nginx"
echo " cp -r $DIST_DIR/* /usr/share/nginx/html/healthlink-his/"
echo " nginx -s reload"
echo ""
echo " 方式二: 软链接(推荐,方便更新)"
echo " ln -sfn $DIST_DIR /usr/share/nginx/html/healthlink-his"
echo " nginx -s reload"
echo ""
echo "=========================================="
echo " 部署完成!"
echo "=========================================="

View File

@@ -1,81 +0,0 @@
# ============================================================
# HealthLink-HIS 前端依赖问题排查与修复脚本
# 用法: bash fix-deps.sh
# ============================================================
set -e
PROJECT_DIR=$(cd "$(dirname "$0")/.." && pwd)
UI_DIR="$PROJECT_DIR/healthlink-his-ui"
cd "$UI_DIR"
echo "=========================================="
echo " HealthLink-HIS 前端依赖诊断"
echo "=========================================="
echo ""
# 检查 node_modules 是否存在
if [ ! -d "node_modules" ]; then
echo "[!] node_modules 不存在,执行 npm install..."
npm install --legacy-peer-deps
exit 0
fi
# 检查 package-lock.json 是否存在
if [ ! -f "package-lock.json" ]; then
echo "[!] package-lock.json 缺失,重新生成..."
npm install --legacy-peer-deps
fi
# 检查关键依赖
echo "检查关键依赖:"
DEPS=("vue" "vite" "vxe-table" "element-plus" "pinia" "vue-router" "axios" "dayjs")
for dep in "${DEPS[@]}"; do
if [ -d "node_modules/$dep" ]; then
VER=$(node -p "require('./node_modules/$dep/package.json').version" 2>/dev/null || echo "未知")
echo "$dep@$VER"
else
echo "$dep 缺失!"
fi
done
echo ""
# 检查过时依赖
echo "检查过时依赖 (可选升级):"
npm outdated 2>/dev/null || true
echo ""
# 常见问题修复菜单
echo "=========================================="
echo " 修复选项:"
echo " 1) 重新安装依赖 (rm node_modules + npm install)"
echo " 2) 清理缓存并重装 (npm cache clean + 重装)"
echo " 3) 修复 peer 依赖冲突 (npm install --legacy-peer-deps)"
echo " 4) 退出"
echo "=========================================="
read -p "选择 [1-4]: " choice
case $choice in
1)
echo "清理 node_modules..."
rm -rf node_modules package-lock.json
npm install --legacy-peer-deps
;;
2)
echo "清理缓存..."
npm cache clean --force
rm -rf node_modules package-lock.json
npm install --legacy-peer-deps
;;
3)
npm install --legacy-peer-deps
;;
*)
echo "退出"
;;
esac
echo ""
echo "完成 ✓"

View File

@@ -1,48 +0,0 @@
# ============================================================
# HealthLink-HIS 前端 Nginx 配置
# 放到 /etc/nginx/conf.d/openhis.conf 或 include 到 nginx.conf
# ============================================================
server {
listen 80;
server_name healthlink-his.local; # 改成实际域名或 IP
# 前端静态文件
location / {
root /usr/share/nginx/html/healthlink-his;
index index.html;
try_files $uri $uri/ /index.html; # SPA 路由回退
}
# 后端 API 代理
location /prd-api/ {
proxy_pass http://127.0.0.1:18082/healthlink-his/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_read_timeout 300;
client_max_body_size 50m;
}
# gzip 压缩Vite 构建已生成 .gz 文件Nginx 直接发送)
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_vary on;
# 静态资源缓存(带 hash 的文件长期缓存)
location ~* /assets/.*\.(js|css|woff2?|ttf|eot|png|jpg|jpeg|gif|svg|ico)$ {
root /usr/share/nginx/html/healthlink-his;
expires 365d;
add_header Cache-Control "public, immutable";
}
# index.html 不缓存(保证更新及时生效)
location = /index.html {
root /usr/share/nginx/html/healthlink-his;
add_header Cache-Control "no-cache, no-store, must-revalidate";
}
}

View File

@@ -6,6 +6,9 @@ import com.core.common.core.domain.entity.SysDictData;
import com.core.common.core.redis.RedisCache;
import com.core.common.utils.spring.SpringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.List;
@@ -15,6 +18,7 @@ import java.util.List;
* @author system
*/
public class DictUtils {
private static final Logger log = LoggerFactory.getLogger(DictUtils.class);
/**
* 分隔符
*/
@@ -37,11 +41,39 @@ public class DictUtils {
* @return dictDatas 字典数据列表
*/
public static List<SysDictData> getDictCache(String key) {
JSONArray arrayCache = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (StringUtils.isNotNull(arrayCache)) {
return arrayCache.toList(SysDictData.class);
try {
Object cached = SpringUtils.getBean(RedisCache.class).getCacheObject(getCacheKey(key));
if (cached == null) {
return null;
}
if (cached instanceof JSONArray arrayCache) {
return arrayCache.toList(SysDictData.class);
}
if (cached instanceof List<?> list) {
// Redis缓存可能已反序列化为List尝试逐个转换
java.util.ArrayList<SysDictData> result = new java.util.ArrayList<>(list.size());
for (Object item : list) {
if (item instanceof SysDictData dictData) {
result.add(dictData);
} else {
// 尝试通过JSON转换
String json = com.alibaba.fastjson2.JSON.toJSONString(item);
result.add(com.alibaba.fastjson2.JSON.parseObject(json, SysDictData.class));
}
}
return result;
}
log.warn("字典缓存key={}的数据类型异常: {}, 清除缓存", key, cached.getClass().getName());
removeDictCache(key);
return null;
} catch (Exception e) {
log.warn("获取字典缓存异常key={}, 清除缓存: {}", key, e.getMessage());
try {
removeDictCache(key);
} catch (Exception ignored) {
}
return null;
}
return null;
}
/**

View File

@@ -125,15 +125,19 @@ public class DictAspect {
// _dictText field not present, proceed normally
}
String dictLabel = queryDictLabel(dictTable, dictCode, dictText, deleteFlag, fieldValue.toString());
if (dictLabel != null && !dictLabel.isEmpty()) {
try {
Field textField = clazz.getDeclaredField(textFieldName);
textField.setAccessible(true);
textField.set(dto, dictLabel);
} catch (NoSuchFieldException | IllegalAccessException e) {
log.debug("字段 {} 不存在或无法访问,跳过字典翻译", textFieldName);
try {
String dictLabel = queryDictLabel(dictTable, dictCode, dictText, deleteFlag, fieldValue.toString());
if (dictLabel != null && !dictLabel.isEmpty()) {
try {
Field textField = clazz.getDeclaredField(textFieldName);
textField.setAccessible(true);
textField.set(dto, dictLabel);
} catch (NoSuchFieldException | IllegalAccessException e) {
log.debug("字段 {} 不存在或无法访问,跳过字典翻译", textFieldName);
}
}
} catch (Exception e) {
log.debug("字典翻译异常, 字段={}, dictCode={}, 跳过: {}", field.getName(), dictCode, e.getMessage());
}
} else {
processDict(fieldValue, visited);

View File

@@ -1,8 +0,0 @@
# 页面标题
VITE_APP_TITLE =医院信息管理系统
# 开发环境配置
VITE_APP_ENV = 'development'
# HealthLink-HIS管理系统/开发环境
VITE_APP_BASE_API = '/dev-api'

View File

@@ -1,14 +0,0 @@
# 页面标题
VITE_APP_TITLE=医院信息管理系统
# 生产环境配置
VITE_APP_ENV= 'prod'
# HealthLink-HIS管理系统/生产环境
VITE_APP_BASE_API= '/prd-api'
# 租户ID配置
VITE_APP_TENANT_ID= '1'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

View File

@@ -1,38 +0,0 @@
# 开发环境本地只启动前端项目依赖开发环境后端、APP
# 生产环境配置
VITE_APP_ENV = 'spug'
VITE_DEV=true
# 请求路径
VITE_BASE_URL='http://192.168.110.252'
# 文件上传类型server - 后端上传, client - 前端直连上传仅支持S3服务
VITE_UPLOAD_TYPE=server
# HealthLink-HIS管理系统/SPUG环境
VITE_APP_BASE_API = '/admin-api'
# 租户ID配置
VITE_APP_TENANT_ID=1
# 是否删除debugger
VITE_DROP_DEBUGGER=false
# 是否删除console.log
VITE_DROP_CONSOLE=false
# 是否sourcemap
VITE_SOURCEMAP=true
# 打包路径
VITE_BASE_PATH=/
# 商城H5会员端域名
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
# 验证码的开关
VITE_APP_CAPTCHA_ENABLE=false
# GoView域名
VITE_GOVIEW_URL='http://127.0.0.1:3000'

View File

@@ -1,14 +0,0 @@
# 页面标题
VITE_APP_TITLE = OpenHIS管理系统
# 生产环境配置
VITE_APP_ENV = 'staging'
# OpenHIS管理系统/生产环境
VITE_APP_BASE_API = '/stage-api'
# 租户ID配置
VITE_APP_TENANT_ID=1
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

View File

@@ -1,5 +0,0 @@
# Playwright E2E 测试环境变量
# 注意此文件仅用于本地开发生产环境使用CI Secret管理
TEST_BASE_URL=http://localhost:80
TEST_USERNAME=admin
TEST_PASSWORD=changeme_in_local_env

381
package-lock.json generated
View File

@@ -1,381 +0,0 @@
{
"name": "his",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"dependencies": {
"axios": "^1.13.2",
"json-bigint": "^1.0.0"
},
"devDependencies": {
"husky": "^9.1.7"
}
},
"node_modules/agent-base": {
"version": "6.0.2",
"resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"license": "MIT",
"dependencies": {
"debug": "4"
},
"engines": {
"node": ">= 6.0.0"
}
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"license": "MIT"
},
"node_modules/axios": {
"version": "1.17.0",
"resolved": "https://registry.npmmirror.com/axios/-/axios-1.17.0.tgz",
"integrity": "sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.16.0",
"form-data": "^4.0.5",
"https-proxy-agent": "^5.0.1",
"proxy-from-env": "^2.1.0"
}
},
"node_modules/bignumber.js": {
"version": "9.3.1",
"resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.3.1.tgz",
"integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==",
"license": "MIT",
"engines": {
"node": "*"
}
},
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
"integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"license": "MIT",
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/debug": {
"version": "4.4.3",
"resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
},
"peerDependenciesMeta": {
"supports-color": {
"optional": true
}
}
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"license": "MIT",
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/dunder-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz",
"integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.1",
"es-errors": "^1.3.0",
"gopd": "^1.2.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-define-property": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz",
"integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-errors": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz",
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-object-atoms": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
"integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/es-set-tostringtag": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
"integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
"get-intrinsic": "^1.2.6",
"has-tostringtag": "^1.0.2",
"hasown": "^2.0.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/follow-redirects": {
"version": "1.16.0",
"resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"license": "MIT",
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/form-data": {
"version": "4.0.5",
"resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
"hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
"license": "MIT",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-intrinsic": {
"version": "1.3.0",
"resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
"integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
"license": "MIT",
"dependencies": {
"call-bind-apply-helpers": "^1.0.2",
"es-define-property": "^1.0.1",
"es-errors": "^1.3.0",
"es-object-atoms": "^1.1.1",
"function-bind": "^1.1.2",
"get-proto": "^1.0.1",
"gopd": "^1.2.0",
"has-symbols": "^1.1.0",
"hasown": "^2.0.2",
"math-intrinsics": "^1.1.0"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/get-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz",
"integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
"license": "MIT",
"dependencies": {
"dunder-proto": "^1.0.1",
"es-object-atoms": "^1.0.0"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/gopd": {
"version": "1.2.0",
"resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
"integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz",
"integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/has-tostringtag": {
"version": "1.0.2",
"resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
"integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
"license": "MIT",
"dependencies": {
"has-symbols": "^1.0.3"
},
"engines": {
"node": ">= 0.4"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/hasown": {
"version": "2.0.4",
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz",
"integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
"license": "MIT",
"dependencies": {
"function-bind": "^1.1.2"
},
"engines": {
"node": ">= 0.4"
}
},
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"license": "MIT",
"dependencies": {
"agent-base": "6",
"debug": "4"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/husky": {
"version": "9.1.7",
"resolved": "https://registry.npmmirror.com/husky/-/husky-9.1.7.tgz",
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
"dev": true,
"license": "MIT",
"bin": {
"husky": "bin.js"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/typicode"
}
},
"node_modules/json-bigint": {
"version": "1.0.0",
"resolved": "https://registry.npmmirror.com/json-bigint/-/json-bigint-1.0.0.tgz",
"integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==",
"license": "MIT",
"dependencies": {
"bignumber.js": "^9.0.0"
}
},
"node_modules/math-intrinsics": {
"version": "1.1.0",
"resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
"integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
"license": "MIT",
"engines": {
"node": ">= 0.4"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
"node_modules/proxy-from-env": {
"version": "2.1.0",
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
"integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
"license": "MIT",
"engines": {
"node": ">=10"
}
}
}
}

View File

@@ -1,351 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Bug #318 历史数据修复脚本
为已存在但未生成手术医嘱的手术申请单补齐数据
"""
import psycopg2
import json
import random
import string
from datetime import datetime
# 数据库连接配置
DB_CONFIG = {
"host": "47.116.196.11",
"port": 15432,
"database": "postgresql",
"user": "postgresql",
"password": "postgresql", # 请根据实际情况修改
}
def generate_bus_no():
"""生成4位随机bus_no"""
return "".join(random.choices(string.digits, k=4))
def check_repair_needed(conn):
"""检查需要修复的记录数"""
cursor = conn.cursor()
sql = """
SELECT COUNT(*)
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL
"""
cursor.execute(sql)
count = cursor.fetchone()[0]
cursor.close()
return count
def get_repair_list(conn):
"""获取需要修复的手术申请单列表"""
cursor = conn.cursor()
sql = """
SELECT
rf.id,
rf.prescription_no,
rf.encounter_id,
rf.patient_id,
rf.requester_id,
rf.create_time,
rf.org_id,
rf.desc_json
FROM doc_request_form rf
LEFT JOIN wor_service_request sr ON sr.prescription_no = rf.prescription_no
AND sr.delete_flag = '0'
WHERE rf.type_code = 'PROCEDURE'
AND rf.delete_flag = '0'
AND sr.id IS NULL
ORDER BY rf.create_time DESC
"""
cursor.execute(sql)
results = cursor.fetchall()
cursor.close()
return results
def parse_surgery_info(desc_json):
"""解析手术信息"""
if not desc_json:
return {}
try:
if isinstance(desc_json, str):
return json.loads(desc_json)
return desc_json
except:
return {}
def repair_service_request(conn, repair_list):
"""修复手术医嘱ServiceRequest"""
cursor = conn.cursor()
insert_sql = """
INSERT INTO wor_service_request (
bus_no, prescription_no, status_enum, generate_source_enum,
therapy_enum, quantity, unit_code, category_enum,
patient_id, requester_id, encounter_id, authored_time,
org_id, content_json, delete_flag, create_time, create_by, tenant_id
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
RETURNING id
"""
new_requests = []
for record in repair_list:
(
rf_id,
prescription_no,
encounter_id,
patient_id,
requester_id,
create_time,
org_id,
desc_json,
) = record
# 解析手术信息
surgery_info = parse_surgery_info(desc_json)
content_json = (
json.dumps(surgery_info, ensure_ascii=False) if surgery_info else None
)
# 生成bus_no
bus_no = generate_bus_no()
# 插入ServiceRequest
cursor.execute(
insert_sql,
(
bus_no, # bus_no
prescription_no, # prescription_no
1, # status_enum: 1-待签发
1, # generate_source_enum: 1-医生处方
2, # therapy_enum: 2-临时医嘱
1, # quantity
"", # unit_code
4, # category_enum: 4-手术
patient_id, # patient_id
requester_id, # requester_id
encounter_id, # encounter_id
create_time, # authored_time
org_id, # org_id
content_json, # content_json
"0", # delete_flag
create_time, # create_time
requester_id, # create_by
1, # tenant_id
),
)
service_request_id = cursor.fetchone()[0]
new_requests.append(
{
"service_request_id": service_request_id,
"bus_no": bus_no,
"prescription_no": prescription_no,
"patient_id": patient_id,
"encounter_id": encounter_id,
"requester_id": requester_id,
"create_time": create_time,
"org_id": org_id,
"surgery_info": surgery_info,
}
)
conn.commit()
cursor.close()
return new_requests
def repair_charge_items(conn, new_requests):
"""修复收费项目ChargeItem"""
cursor = conn.cursor()
insert_sql = """
INSERT INTO adm_charge_item (
bus_no, status_enum, generate_source_enum, patient_id,
context_enum, encounter_id, enterer_id, entered_date,
service_table, service_id, product_table, requesting_org_id,
quantity_value, quantity_unit, unit_price, total_price,
delete_flag, create_time, create_by, tenant_id
) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
"""
for req in new_requests:
surgery_info = req["surgery_info"]
# 手术费用
surgery_fee = 0
if surgery_info and "surgeryFee" in surgery_info:
try:
surgery_fee = float(surgery_info["surgeryFee"])
except:
surgery_fee = 0
# 插入手术费用收费项目
cursor.execute(
insert_sql,
(
"CI" + req["bus_no"], # bus_no
1, # status_enum: 1-草稿
1, # generate_source_enum: 1-医生处方
req["patient_id"], # patient_id
3, # context_enum: 3-诊疗
req["encounter_id"], # encounter_id
req["requester_id"], # enterer_id
req["create_time"], # entered_date
"wor_service_request", # service_table
req["service_request_id"], # service_id
"wor_activity_definition", # product_table
req["org_id"], # requesting_org_id
1, # quantity_value
"", # quantity_unit
surgery_fee, # unit_price
surgery_fee, # total_price
"0", # delete_flag
req["create_time"], # create_time
req["requester_id"], # create_by
1, # tenant_id
),
)
# 麻醉费用如果存在且大于0
anesthesia_fee = 0
if surgery_info and "anesthesiaFee" in surgery_info:
try:
anesthesia_fee = float(surgery_info["anesthesiaFee"])
except:
anesthesia_fee = 0
if anesthesia_fee > 0:
cursor.execute(
insert_sql,
(
"CI" + req["bus_no"] + "_A", # bus_no
1, # status_enum
1, # generate_source_enum
req["patient_id"], # patient_id
3, # context_enum
req["encounter_id"], # encounter_id
req["requester_id"], # enterer_id
req["create_time"], # entered_date
"wor_service_request", # service_table
req["service_request_id"], # service_id
"wor_activity_definition", # product_table
req["org_id"], # requesting_org_id
1, # quantity_value
"", # quantity_unit
anesthesia_fee, # unit_price
anesthesia_fee, # total_price
"0", # delete_flag
req["create_time"], # create_time
req["requester_id"], # create_by
1, # tenant_id
),
)
conn.commit()
cursor.close()
def verify_repair(conn):
"""验证修复结果"""
cursor = conn.cursor()
# 统计手术医嘱数量
cursor.execute("""
SELECT COUNT(*)
FROM wor_service_request
WHERE category_enum = 4 AND delete_flag = '0'
""")
service_request_count = cursor.fetchone()[0]
# 统计收费项目数量
cursor.execute("""
SELECT COUNT(*)
FROM adm_charge_item ci
WHERE ci.service_table = 'wor_service_request'
AND EXISTS (
SELECT 1 FROM wor_service_request sr
WHERE sr.id = ci.service_id AND sr.category_enum = 4
)
""")
charge_item_count = cursor.fetchone()[0]
cursor.close()
return service_request_count, charge_item_count
def main():
"""主函数"""
print("=" * 60)
print("Bug #318 历史数据修复脚本")
print("=" * 60)
print()
try:
# 连接数据库
print("正在连接数据库...")
conn = psycopg2.connect(**DB_CONFIG)
print("✓ 数据库连接成功")
print()
# 步骤1检查需要修复的记录数
print("步骤1: 检查需要修复的记录...")
need_repair_count = check_repair_needed(conn)
print(f"✓ 发现 {need_repair_count} 条需要修复的手术申请单")
print()
if need_repair_count == 0:
print("没有需要修复的数据,退出")
return
# 步骤2获取修复列表
print("步骤2: 获取修复列表...")
repair_list = get_repair_list(conn)
print(f"✓ 获取到 {len(repair_list)} 条记录")
print()
# 步骤3修复ServiceRequest
print("步骤3: 生成手术医嘱ServiceRequest...")
new_requests = repair_service_request(conn, repair_list)
print(f"✓ 成功生成 {len(new_requests)} 条手术医嘱")
print()
# 步骤4修复ChargeItem
print("步骤4: 生成收费项目ChargeItem...")
repair_charge_items(conn, new_requests)
print(f"✓ 成功生成收费项目")
print()
# 步骤5验证修复结果
print("步骤5: 验证修复结果...")
service_count, charge_count = verify_repair(conn)
print(f"✓ 当前手术医嘱总数: {service_count}")
print(f"✓ 当前手术收费项目总数: {charge_count}")
print()
print("=" * 60)
print("✓ 修复完成!")
print("=" * 60)
except Exception as e:
print(f"✗ 错误: {e}")
import traceback
traceback.print_exc()
finally:
if "conn" in locals():
conn.close()
print("\n数据库连接已关闭")
if __name__ == "__main__":
main()

View File

@@ -1,156 +0,0 @@
#!/usr/bin/env bash
# ============================================================
# sync-ai-rules.sh — 将 RULES.md 完整内容同步到所有 AI 工具配置文件
#
# 用法: bash scripts/sync-ai-rules.sh
#
# 设计原则:
# RULES.md 是唯一信源,本脚本将其内容嵌入到各工具配置文件
# 开发者只需编辑 RULES.md然后运行本脚本即可同步所有工具
# ============================================================
set -euo pipefail
cd "$(dirname "$0")/.."
RULES_FILE="RULES.md"
if [ ! -f "$RULES_FILE" ]; then
echo "$RULES_FILE not found"
exit 1
fi
RULES_CONTENT=$(cat "$RULES_FILE")
TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
echo "📝 Syncing RULES.md → AI tool configs..."
# ============================================================
# 1. AGENTS.md (Codex CLI / Claude Code)
# ============================================================
cat > AGENTS.md << HEREDOC
# HealthLink-HIS — AI 开发规范
> 🤖 本文件由 Codex CLI、Claude Code 等工具自动读取。
> 工具进入项目目录时会自动加载此文件作为开发规范上下文。
---
${RULES_CONTENT}
---
> 📅 最后同步: ${TIMESTAMP} | 源文件: RULES.md | 重新同步: \`bash scripts/sync-ai-rules.sh\`
HEREDOC
echo " ✅ AGENTS.md"
# ============================================================
# 2. .cursorrules (Cursor IDE / Codeium Windsurf)
# ============================================================
cat > .cursorrules << HEREDOC
# HealthLink-HIS — AI 开发规范 (Cursor)
> 🤖 Cursor IDE 打开本项目时自动加载此文件。
---
${RULES_CONTENT}
---
> 📅 最后同步: ${TIMESTAMP} | 源文件: RULES.md | 重新同步: \`bash scripts/sync-ai-rules.sh\`
HEREDOC
echo " ✅ .cursorrules"
# ============================================================
# 3. .github/copilot-instructions.md (GitHub Copilot)
# ============================================================
mkdir -p .github
cat > .github/copilot-instructions.md << HEREDOC
# HealthLink-HIS — AI 开发规范 (GitHub Copilot)
> 🤖 GitHub Copilot 打开本项目时自动加载此文件。
---
${RULES_CONTENT}
---
> 📅 最后同步: ${TIMESTAMP} | 源文件: RULES.md | 重新同步: \`bash scripts/sync-ai-rules.sh\`
HEREDOC
echo " ✅ .github/copilot-instructions.md"
# ============================================================
# 4. .windsurfrules (Windsurf / Codeium)
# ============================================================
cat > .windsurfrules << HEREDOC
# HealthLink-HIS — AI 开发规范 (Windsurf)
> 🤖 Windsurf 打开本项目时自动加载此文件。
---
${RULES_CONTENT}
---
> 📅 最后同步: ${TIMESTAMP} | 源文件: RULES.md | 重新同步: \`bash scripts/sync-ai-rules.sh\`
HEREDOC
echo " ✅ .windsurfrules"
# ============================================================
# 5. .clinerules (Cline)
# ============================================================
cat > .clinerules << HEREDOC
# HealthLink-HIS — AI 开发规范 (Cline)
> 🤖 Cline 打开本项目时自动加载此文件。
---
${RULES_CONTENT}
---
> 📅 最后同步: ${TIMESTAMP} | 源文件: RULES.md | 重新同步: \`bash scripts/sync-ai-rules.sh\`
HEREDOC
echo " ✅ .clinerules"
# ============================================================
# 6. .qwenrules (Qwen Coder / 通义灵码)
# ============================================================
cat > .qwenrules << HEREDOC
# HealthLink-HIS — AI 开发规范 (Qwen Coder)
> 🤖 通义灵码 / Qwen Coder 打开本项目时自动加载此文件。
---
${RULES_CONTENT}
---
> 📅 最后同步: ${TIMESTAMP} | 源文件: RULES.md | 重新同步: \`bash scripts/sync-ai-rules.sh\`
HEREDOC
echo " ✅ .qwenrules"
# ============================================================
# 7. .aider.conf.yml (Aider) — YAML格式嵌入指令
# ============================================================
# Aider 支持 instruction-files 指向文件,同时也支持直接在 .aider.conf.yml 中写 instructions
# 这里用 instructions 指令把内容内联
cat > .aider.conf.yml << HEREDOC
# Aider configuration for HealthLink-HIS
# Aider 自动读取此文件获取开发规范
instructions: |
$(echo "$RULES_CONTENT" | sed 's/^/ /')
HEREDOC
echo " ✅ .aider.conf.yml"
echo ""
echo "🎉 All 7 AI tool configs synced from RULES.md"
echo " 文件大小:"
for f in AGENTS.md .cursorrules .github/copilot-instructions.md .windsurfrules .clinerules .qwenrules .aider.conf.yml; do
size=$(wc -c < "$f" 2>/dev/null || echo "0")
echo " $f: ${size} bytes"
done

View File

@@ -1,69 +0,0 @@
<template>
<div class="outpatient-record">
<el-table :data="records" style="width: 100%">
<el-table-column prop="date" label="就诊日期" width="180"/>
<el-table-column prop="doctor" label="医生" width="180"/>
<el-table-column prop="status" label="状态" width="120">
<!-- 修复 status nullundefined空字符串时统一显示为 未确认 并使用已选中样式 -->
<template #default="{ row }">
<el-checkbox
v-model="row._statusChecked"
:true-label="'已确认'"
:false-label="'未确认'"
:disabled="true"
@change="onStatusChange(row)">
{{ row._statusChecked ? '已确认' : '未确认' }}
</el-checkbox>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="{ row }">
<el-button type="text" size="small" @click="viewDetail(row)">查看</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
name: 'OutpatientRecord',
data() {
return {
records: [] // [{ date, doctor, status }]
};
},
created() {
this.fetchRecords();
},
methods: {
/** 获取就诊记录 */
async fetchRecords() {
const res = await this.$api.getOutpatientRecords();
// 这里统一把 status 转换为布尔值,避免出现空方框
this.records = res.data.map(item => ({
...item,
// status 可能为 null、undefined、'',统一映射为 false未确认
_statusChecked: !!item.status
}));
},
/** 当状态复选框变化时(理论上只读,这里保留以防后续业务需要) */
onStatusChange(row) {
// 只在前端展示,不向后端提交,防止误改
// 如需持久化,请在此调用对应的 API
},
/** 查看详情 */
viewDetail(row) {
this.$router.push({ name: 'OutpatientDetail', params: { id: row.id } });
}
}
};
</script>
<style scoped>
.outpatient-record {
padding: 20px;
}
</style>

View File

@@ -1,153 +0,0 @@
#!/bin/bash
# 禅道 API 命令行工具
# 使用前需要设置环境变量:
# export ZENTAO_URL="http://your-zentao-domain.com"
# export ZENTAO_ACCOUNT="your_username"
# export ZENTAO_PASSWORD="your_password"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# 检查依赖
check_dependencies() {
if ! command -v curl &> /dev/null; then
echo -e "${RED}错误: 请安装 curl${NC}"
exit 1
fi
if ! command -v jq &> /dev/null; then
echo -e "${RED}错误: 请安装 jq${NC}"
exit 1
fi
}
# 获取 Token
get_token() {
check_dependencies
if [[ -z "$ZENTAO_URL" || -z "$ZENTAO_ACCOUNT" || -z "$ZENTAO_PASSWORD" ]]; then
echo -e "${RED}错误: 请设置环境变量 ZENTAO_URL, ZENTAO_ACCOUNT, ZENTAO_PASSWORD${NC}"
exit 1
fi
local url="${ZENTAO_URL}/api.php/v1/tokens"
local response=$(curl -s -X POST "$url" \
-H "Content-Type: application/json" \
-d "{\"account\":\"$ZENTAO_ACCOUNT\",\"password\":\"$ZENTAO_PASSWORD\"}")
local token=$(echo "$response" | jq -r '.data.token // empty')
if [[ -n "$token" ]]; then
echo "$token"
else
echo -e "${RED}获取 Token 失败:${NC}"
echo "$response" | jq .
exit 1
fi
}
# 创建 Bug
create_bug() {
local token="$1"
local product_id="$2"
local title="$3"
local steps="$4"
local severity="${5:-2}"
local priority="${6:-2}"
local module="${7:-}"
local assigned_to="${8:-}"
if [[ -z "$token" || -z "$product_id" || -z "$title" || -z "$steps" ]]; then
echo -e "${YELLOW}用法:${NC} $0 create_bug <token> <product_id> <title> <steps> [severity] [priority] [module] [assigned_to]"
echo ""
echo "参数说明:"
echo " token - API Token"
echo " product_id - 产品ID"
echo " title - Bug标题"
echo " steps - 重现步骤"
echo " severity - 严重程度 (1-4, 默认2: 一般)"
echo " priority - 优先级 (1-4, 默认2: 中等)"
echo " module - 模块ID (可选)"
echo " assigned_to- 指派给谁 (可选)"
exit 1
fi
local url="${ZENTAO_URL}/api.php/v1/products/${product_id}/bugs"
# 构建 JSON 数据
local data="{"
data+="\"product\":${product_id},"
if [[ -n "$module" ]]; then
data+="\"module\":${module},"
fi
data+="\"title\":\"${title}\","
data+="\"steps\":\"${steps}\","
data+="\"severity\":${severity},"
data+="\"priority\":${priority}"
if [[ -n "$assigned_to" ]]; then
data+=",\"assignedTo\":\"${assigned_to}\""
fi
data+="}"
local response=$(curl -s -X POST "$url" \
-H "Content-Type: application/json" \
-H "Authorization:${token}" \
-d "$data")
echo "$response" | jq .
}
# 显示使用帮助
show_help() {
echo -e "${GREEN}禅道 API 命令行工具${NC}"
echo ""
echo "用法: $0 <command> [arguments]"
echo ""
echo "命令:"
echo " token 获取 API Token"
echo " create_bug 创建 Bug"
echo " list_bugs 列出 Bugs"
echo " get_bug 获取单个 Bug"
echo " update_bug 更新 Bug"
echo " resolve_bug 解决 Bug"
echo ""
echo "示例:"
echo " $0 token # 获取 Token"
echo " $0 create_bug <token> 100 '测试Bug' '步骤...' # 创建 Bug"
}
# 主函数
main() {
local command="$1"
shift || true
case "$command" in
token)
get_token
;;
create_bug)
create_bug "$@"
;;
list_bugs|get_bug|update_bug|resolve_bug)
# 其他功能待实现
echo -e "${YELLOW}功能待实现${NC}"
;;
help|--help|-h)
show_help
;;
"")
show_help
;;
*)
echo -e "${RED}未知命令: $command${NC}"
show_help
exit 1
;;
esac
}
main "$@"

View File

@@ -1,100 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
禅道 API 示例脚本 - 创建 Bug
"""
import requests
import json
# 禅道配置(需要根据实际情况修改)
ZENTAO_URL = "http://your-zentao-domain.com" # 禅道地址
ZENTAO_ACCOUNT = "your_username" # 禅道账号
ZENTAO_PASSWORD = "your_password" # 禅道密码
def get_token():
"""获取禅道 API Token"""
url = f"{ZENTAO_URL}/api.php/v1/tokens"
headers = {
"Content-Type": "application/json"
}
data = {
"account": ZENTAO_ACCOUNT,
"password": ZENTAO_PASSWORD
}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if response.status_code == 200 and result.get("status") == "success":
return result.get("token")
else:
raise Exception(f"获取 Token 失败: {result}")
def create_bug(token, product_id, title, steps, severity=2, priority=2, module='', assigned_to=''):
"""
创建Bug
参数:
token: API Token
product_id: 产品ID
title: Bug标题
steps: 重现步骤
severity: 严重程度 (1-4, 默认2: 一般)
priority: 优先级 (1-4, 默认2: 中等)
module: 模块ID (可选)
assigned_to: 指派给谁 (可选)
"""
url = f"{ZENTAO_URL}/api.php/v1/products/{product_id}/bugs"
headers = {
"Content-Type": "application/json",
"Authorization": token
}
data = {
"product": product_id,
"module": module,
"title": title,
"steps": steps,
"severity": severity,
"priority": priority,
"assignedTo": assigned_to
}
# 移除值为空的字段
data = {k: v for k, v in data.items() if v}
response = requests.post(url, headers=headers, json=data)
result = response.json()
if response.status_code == 200 and result.get("status") == "success":
print(f"✅ Bug 创建成功Bug ID: {result.get('data', {}).get('id')}")
return result
else:
print(f"❌ Bug 创建失败: {result}")
return None
def main():
try:
# 1. 获取 Token
print("正在获取 Token...")
token = get_token()
print(f"✓ Token 获取成功: {token[:10]}...")
# 2. 创建 Bug 示例
# TODO: 请根据实际情况修改以下参数
product_id = 100 # 产品ID需要在禅道中查询
title = "【测试】API 创建 Bug 示例"
steps = """1. 登录系统
2. 进入某个模块
3. 执行某个操作
4. 观察结果"""
print("\n正在创建 Bug...")
create_bug(token, product_id, title, steps)
except Exception as e:
print(f"❌ 发生错误: {e}")
if __name__ == "__main__":
main()