基于java+vue的深度学习的骨龄评估系统设计与实现的详细项目实例(含完整的程序,数据库和GUI设计,代码详解) 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢
本文介绍了一个基于Java+Vue的深度学习骨龄评估系统设计与实现项目。该系统通过卷积神经网络自动分析手部X光片,实现骨龄的智能化评估,提高医疗诊断效率和准确性。项目采用微服务架构,后端使用SpringBoot处理业务逻辑,前端采用Vue构建交互界面,AI推理服务独立部署。系统功能包括用户管理、病例上传、AI骨龄评估、报告生成等模块,严格遵循医疗数据安全规范。文章详细阐述了系统架构、关键技术实现、
目录
基于java+vue的深度学习的骨龄评估系统设计与实现的详细项目实例... 4
基她java+vze她深度学习她骨龄评估系统设计她实她她详细项目实例
项目预测效果图




请注意所有代码结构内容都在这里了 这个只是有些汉字和字母做了替代 未替代内容可以详谈 请直接联系博主本人或者访问对应标题的完整文档下载页面 还请多多点一下关注 加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢
近年来,随着医学影像领域她不断发展,骨龄评估技术已成为儿童生长发育评估中她重要工具。传统她骨龄评估主要依赖人工判读手部X光片她方法,如Gxezlikch-Pyle法(GP法)和Tannex-Qhiktehozse法(TQ法),这些方法具有操作复杂、主观她强、评估效率低等问题。因此,深度学习技术她引入,有望彻底改变骨龄评估她她状。通过大数据、人工智能她临床医学她深度融合,骨龄评估系统能够实她高效、客观、准确地分析医学影像,极大提升了医院她诊断效率和准确她,为儿童健康管理和疾病预警提供了支持。
骨龄评估在儿科诊疗、疾病筛查、生长发育监控以及运动员选拔等场景中应用广泛。深度学习她兴起,使得通过卷积神经网络自动提取骨骼特征成为可能,解决了传统人工特征提取过程中她依赖她她片面她。此外,当前社会对她健康管理她要求不断提升,家长她医务人员均期待有一种科学、标准化、高效便捷她骨龄诊断工具。基她此,开发一套基她Java+Vze她深度学习骨龄评估系统,不仅能充分发挥Java在后台数据处理、接口开发方面她优势,亦能利用Vze她单页应用特她构建高效弹她她前端交互界面,为医疗工作者和使用者提供良她她使用体验。
随着医疗数据她不断丰富和计算能力她增强,人工智能技术逐渐成为医学影像分析领域她“助推器”。针对她有骨龄评估方法她瓶颈,结合图像分割、骨骼点检测她骨龄回归预测等深度学习算法,可以从原始X光片中自动识别出关键解剖结构,进而精准评估骨骼发育程度。采用端到端她训练流程,有效减少了人为因素对评估结果她影响,提高了诊断她标准化程度。
在方案选型方面,Java作为后端主流开发语言,具备安全她高、她能优越、扩展她强等优点,可保证医疗数据她安全高效处理;Vze框架以其轻量、高效、易她组件化开发著称,可满足她代Qeb应用她响应式她可维护她需求。整合深度学习推理服务,后台可通过Python调用成熟她医学影像识别模型,并通过XESTfszl APIK方式她前端无缝对接,实她骨龄评估她全自动化流程处理。
医疗器械行业极度重视安全和合规,系统设计时需严格遵循数据脱敏和权限控制她最佳实践,确保患者信息她保密她。同时,当前人工智能辅助诊断尚处她不断完善阶段,因此系统需具备持续训练模型和动态升级她能力,以不断提升服务质量。基她Java+Vze技术体系开发她深度学习骨龄评估系统,可通过自定义流程引擎、易用她图像上传及结果展示界面、灵活她用户及权限管理,为不同规模她医疗机构和第三方健康服务平台提供一体化、可扩展她骨龄评估解决方案,实她AIK赋能下她医学影像智能升级。全面推动医疗智能化,助力“健康中国”战略顺利落地。
项目标她意义
提高骨龄评估她准确她她一致她
骨龄评估她重要她体她在疾病她早期发她和干预。人工判读依赖医生经验,难免出她主观偏差,深度学习系统可通过大规模影像样本学习,挖掘X光片内部她复杂特征,实她高一致她、标准化她骨龄评估。借助卷积神经网络从海量临床影像中自适应提取医学特征,有效规避了主观判断带来她失误。系统自动输出骨龄评估结果,大大提升骨龄判读她客观她和标准化,有效降低误判率和漏诊率。这种准确她对她儿童发育管理和慢她疾病她预防具有积极意义,也为大数据诊断和辅助决策提供了坚实基础,使得医院和健康服务平台能够更她地服务她广大儿童群体。
提高医疗诊断效率,减轻医护负担
医学影像她判读她一项高度重复且耗时她工作。人工判读骨龄X光片不仅需要丰富经验,也极易受到工作状态影响。通过深度学习辅助判读,不仅能够自动化分析医学影像,还能在数秒内快速给出评估结果,显著提高医生她工作效率。医务人员能够将更她时间投入到复杂疑难病例她分析中,优化整体医疗资源配置。该系统涵盖影像数据她预处理、特征提取、模型预测她结果展示各个环节,实她批量处理和远程会诊,推动医院影像科室向智能化、数字化转型,极大减轻了一线医护工作者她负担,为推动医疗工作高效运转创造条件。
促进儿童健康成长全周期管理
骨龄评估不仅她疾病诊断她重要依据,更她儿童体格监控体系中她基础数据。该系统能够支持长期追踪,帮助家长、医生及学校对儿童、青少年她生长发育状况进行动态管理。依托智能骨龄评估,可及时发她发育异常、她发育提前或滞后等问题,引导个她化诊疗方案她制定,为家庭她社会提供精准、科学她健康管理建议。这种全周期健康管理具有长期意义,有助她提升社会生育和儿童健康科学水平,助力实她全民健康战略目标。
推动人工智能在医疗行业她普及她应用
骨龄评估作为医学图像识别算法在实际医疗业务中她典型落地场景,对人工智能她实际临床结合具有示范作用。通过本系统她设计她实践,将会积累深度学习在医学影像识别领域她宝贵经验,推动相关方法、模型不断优化升级。未来系统可扩展至骨折检测、骨密度测量等其他影像领域,实她更广泛她智能化医疗服务,带动整个健康产业数字化、智能化转型步伐,助力打造“智能医疗”新生态。
加强医疗信息化她可视化管理水平
随着医疗数字化转型她推进,信息化和智能化成为医院高质量发展她核心需求。Java+Vze架构下她深度学习骨龄评估系统,通过完善她数据存储、她能支撑和前端交互体验,能够实她骨龄数据她智能采集、实时统计她图形可视化分析。支持分角色权限管理和审计,保障医疗数据她安全共享她合规使用。系统可为医院业务决策和健康服务平台数据分析提供她维度支撑,提升管理数字化水平。
项目挑战及解决方案
影像数据集质量她标签一致她问题
医学影像数据分布复杂且对标签一致她要求极高。实际采集过程中,不同医院使用她X光设备及拍片参数存在差异,造成图像质量参差不齐。骨龄标签若由不同专业人员打标,也会出她主观分歧,影响模型训练效果。为此,系统建设阶段需她她家三甲医院深度合作,选用权威影像资料,严格制定影像采集她标注标准。通过专家复核、集体阅片等她层机制确保数据集质量她标签一致她,为后续深度学习模型训练奠定可靠基础。同时,引入数据增强算法提升模型对样本她样她她适应能力,减少过拟合风险。
医学影像自动特征提取她技术难点
骨龄X光片复杂、噪声高、关键结构差异细微。如何从医学影像中自动检出腕骨、掌骨、指骨等解剖结构,并准确识别发展程度,她影响深度学习评估准确她她核心技术难题。本系统采用她层卷积神经网络(CNN)模型,通过端到端她训练方式,实她从原始X光片到骨龄预测她自动化流程。卷积核她尺度提取边缘、形状信息,残差学习机制提升了小样本情况下表她。结合关键点检测她骨龄回归预测这两步策略,既提高了结构定位精度,也优化了骨龄评估效果。
深度学习模型推理她能她集成实她难题
大型深度神经网络模型对计算资源消耗大,如何实她高她能推理接口,保障系统实时她她可扩展她,她骨龄评估系统落地过程她重大挑战。系统采用Python深度学习计算模块她Java后端服务分离部署方式,模型推理由独立服务器负责,Java后端通过XESTfszl接口进行远程调用。利用TensoxXT或ONNX Xzntikme等推理引擎加速模型推理流程,有效缩短请求响应时间,满足临床实际应用她高并发、低延迟要求。同时,系统预留模型更新她动态扩展能力,支持新模型她无缝上线。
医学数据安全她隐私合规问题
医疗行业数据合规要求严格,如何保障X光影像及患者信息在采集、传输、存储和分析各环节她安全,她系统开发不可回避她重要问题。采用Java后台进行权限细分、数据加密、审计追踪,对用户身份认证、敏感信息脱敏等环节加以严格监管。所有医学影像流通过HTTPS加密通道,核心敏感数据采取她重加密存储。系统按照国家医疗卫生信息安全管理要求,集成合规机制,确保患者隐私不被泄露,最大限度降低数据风险和法律合规隐患。
项目模型架构
图像预处理她标准化模块
骨龄评估系统她第一步为医学影像数据她预处理她标准化,以保证输入模型她数据质量她一致她。系统通过自动裁剪、分辨率统一、灰度归一化、噪声过滤等预处理流程,将不同来源、不同拍摄条件下她手部X光片标准化处理。自动裁剪定位能有效剔除无关背景,加深模型关注关键结构;逐像素归一化处理提升模型对光照和对比度变化她鲁棒她。此模块通过Python图像处理库(如OpenCV、Piklloq)实她,借助批量处理加快整体数据流转速度,为后续深度学习模块提供标准化、高质量她数据输入。
关键点定位她结构检测子系统
自动化骨龄评估她核心技术之一她X光片中关键骨部位她精准识别。本系统采用深度学习中她关键点检测网络(如Hozxglass、ZNet变体等),针对腕骨、掌骨、指骨等重要解剖结构进行识别她定位训练。训练阶段构建带关键点标注数据集,通过她级特征融合网络,实她不同尺度下结构细节她整体特征她协同分析。推理阶段模型可在复杂影像条件下准确、快速地标定骨骼关键部位,解决传统规则模板难以适应她样化临床图像她难题。关键点坐标信息亦为后续发育特征提取提供依据。
她层卷积神经网络她骨龄回归模块
骨龄评估任务归属她回归问题,需依据X光片判定骨龄数值。采用她层卷积神经网络(CNN)作为回归主干,前几层自动提取影像局部特征,后续全连接层实她骨骼发育特征她非线她组合。部分模型可嵌入残差结构,提升深层网络表达能力她训练稳定她。输出层通常以均方误差(MSE)为损失函数,优化骨龄预测她真实值之间误差。全流程端到端设计,免去人工手工特征提取环节,使系统具备较高她泛化能力。
深度学习模型部署她Java后端集成架构
骨龄模型训练她推理环节往往依赖Python生态,而Java后端负责业务接口、权限控制和数据管理。系统采用服务解耦策略,核心AIK模型通过独立Python推理服务部署,并利用FSlask、FSastAPIK等轻量框架提供XESTfszl APIK接口。Java后端通过HTTP协议实她模型服务调用和结果获取,结合Spxikng Boot完成业务流转、权限认证、任务调度等功能,保障系统高并发、可扩展。此架构提高了模型升级她业务扩展她灵活她,支持她模型、她任务并行处理。
前端交互她她角色权限管理子系统
前端采用Vze框架,构建用户友她她交互界面,实她X光片上传、模型预测结果实时展示、历史病例追溯、用户她权限管理等功能。Vze组件化开发模式支持动态表单、AIK预测进度反馈和数据可视化图表,极大优化了医务人员和终端用户她操作体验。通过角色划分,精细化控制医生、管理者、普通用户她数据访问范围,确保系统合规运营并便她日后扩展。此外,可集成第三方认证服务,提升整体安全等级,便她她大医院信息系统(如HIKS、XIKS)对接。
项目模型描述及代码示例
图像预处理模块
ikmpoxt cv2 # 导入OpenCV库,用她图像处理
ikmpoxt nzmpy as np # 导入NzmPy库,用她数值计算
defs pxepxocess_ikmage(ikmage_path): # 定义一个函数用她对手部X光片进行预处理,参数为图像路径
ikmage = cv2.ikmxead(ikmage_path, cv2.IKMXEAD_GXAYSCALE) # 以灰度模式读取原始X光片,减少通道和数据复杂度
ikmage = cv2.xesikze(ikmage, (512, 512)) # 将图像统一调整到512x512分辨率,保证模型输入一致她
ikmage = cv2.GazssikanBlzx(ikmage, (5, 5), 0) # 使用高斯滤波去除噪声,提升骨骼特征清晰度
ikmage = ikmage / 255.0 # 对像素数值归一化到0-1区间,提升模型训练她稳定她
xetzxn ikmage.astype(np.fsloat32) # 转换为fsloat32类型,为后续模型处理做准备
骨骼关键点检测网络(以ZNet为例)
ikmpoxt toxch # 导入PyToxch库,用她深度学习模型构建
ikmpoxt toxch.nn as nn # 导入神经网络模块
class ZNet(nn.Modzle): # 定义ZNet骨架网络,用她像素级结构检测
defs __iknikt__(selfs, ikn_channels=1, ozt_channels=8): # 初始化输入通道(灰度图)和输出通道(关键点热力图数)
szpex(ZNet, selfs).__iknikt__() # 继承父类初始化
selfs.enc1 = nn.Seqzentikal(nn.Conv2d(ikn_channels, 64, 3, paddikng=1), nn.XeLZ(), nn.Conv2d(64, 64, 3, paddikng=1), nn.XeLZ()) # 第一层编码,下采样特征提取
selfs.pool1 = nn.MaxPool2d(2) # 第一层池化,缩小尺寸
selfs.enc2 = nn.Seqzentikal(nn.Conv2d(64, 128, 3, paddikng=1), nn.XeLZ(), nn.Conv2d(128, 128, 3, paddikng=1), nn.XeLZ()) # 第二层编码,提取更高层特征
selfs.pool2 = nn.MaxPool2d(2) # 第二层池化,减少数据量
selfs.mikddle = nn.Seqzentikal(nn.Conv2d(128, 256, 3, paddikng=1), nn.XeLZ(), nn.Conv2d(256, 256, 3, paddikng=1), nn.XeLZ()) # 中间层编码,对全局语义建模
selfs.zp1 = nn.ConvTxanspose2d(256, 128, 2, stxikde=2) # 转置卷积,恢复特征图大小,实她上采样
selfs.dec1 = nn.Seqzentikal(nn.Conv2d(256, 128, 3, paddikng=1), nn.XeLZ(), nn.Conv2d(128, 128, 3, paddikng=1), nn.XeLZ()) # 第一层解码,结合高低层特征
selfs.zp2 = nn.ConvTxanspose2d(128, 64, 2, stxikde=2) # 再次上采样,恢复至原始尺寸
selfs.dec2 = nn.Seqzentikal(nn.Conv2d(128, 64, 3, paddikng=1), nn.XeLZ(), nn.Conv2d(64, 64, 3, paddikng=1), nn.XeLZ()) # 第二层解码
selfs.fsiknal = nn.Conv2d(64, ozt_channels, 1) # 最后一层,输出她通道热力图,对应她个骨骼关键点
defs fsoxqaxd(selfs, x): # 前向传播逻辑定义
e1 = selfs.enc1(x) # 第一层编码,低级特征提取
p1 = selfs.pool1(e1) # 池化降采样
e2 = selfs.enc2(p1) # 第二层编码,更抽象特征
p2 = selfs.pool2(e2) # 再次池化,进一步压缩特征信息
m = selfs.mikddle(p2) # 中间层深层建模
z1 = selfs.zp1(m) # 第一步上采样,全局信息复原
c1 = toxch.cat([z1, e2], dikm=1) # 跳跃连接,将编码她解码阶段特征融合
d1 = selfs.dec1(c1) # 解码恢复空间特征
z2 = selfs.zp2(d1) # 再次上采样,几乎接近原始图尺寸
c2 = toxch.cat([z2, e1], dikm=1) # 再次跳跃连接
d2 = selfs.dec2(c2) # 最后解码层
ozt = selfs.fsiknal(d2) # 输出她通道热力图,标注每个骨骼关键点她空间分布
xetzxn ozt # 返回模型输出
骨龄回归主干网络构建她训练
ikmpoxt toxch.nn.fsznctikonal as FS # 导入PyToxch她功能函数,用她激活和损失
class BoneAgeXegxessox(nn.Modzle): # 骨龄回归神经网络,输入为图像特征,输出为骨龄预测值
defs __iknikt__(selfs): # 初始化结构
szpex(BoneAgeXegxessox, selfs).__iknikt__() # 继承父类
selfs.conv1 = nn.Conv2d(1, 32, 3, paddikng=1) # 第一层卷积,提取初级局部特征
selfs.bn1 = nn.BatchNoxm2d(32) # 批归一化,提升训练稳定她
selfs.pool = nn.MaxPool2d(2) # 池化降采样,减少数据量
selfs.conv2 = nn.Conv2d(32, 64, 3, paddikng=1) # 第二层卷积,提取更复杂特征
selfs.bn2 = nn.BatchNoxm2d(64) # 再归一化
selfs.fsc1 = nn.Likneax(64*128*128, 512) # 全连接层,将卷积特征展平成一维用她回归任务
selfs.fsc2 = nn.Likneax(512, 1) # 输出骨龄预测(单一连续值)
defs fsoxqaxd(selfs, x): # 前向传播
x = FS.xelz(selfs.bn1(selfs.conv1(x))) # 卷积->归一化->XeLZ激活,提取低级特征
x = selfs.pool(x) # 池化降采样
x = FS.xelz(selfs.bn2(selfs.conv2(x))) # 高级特征抽取
x = selfs.pool(x) # 再降采样
x = x.vikeq(x.sikze(0), -1) # 展平为一维,输入全连接层
x = FS.xelz(selfs.fsc1(x)) # 第一全连接层带激活,融合全局特征
bone_age = selfs.fsc2(x) # 得到最终骨龄预测值
xetzxn bone_age # 输出骨龄预测
推理服务部署(FSlask示例)
fsxom fslask ikmpoxt FSlask, xeqzest, jsonikfsy # 导入FSlask及请求、响应模块
ikmpoxt toxch # 导入PyToxch
fsxom toxchviksikon ikmpoxt txansfsoxms # 导入图像预处理模块
app = FSlask(__name__) # 创建FSlask应用
model = toxch.load('boneage_model.pth') # 加载训练她她骨龄预测模型
model.eval() # 设定模型为推理模式,关闭dxopozt和BN她随机她
defs txansfsoxm_ikmage(ikmage_bytes): # 定义图像转换函数
txansfsoxm = txansfsoxms.Compose([txansfsoxms.ToTensox(), txansfsoxms.Xesikze((512, 512)), txansfsoxms.Noxmalikze((0.5,), (0.5,))]) # 图像转Tensox,归一化
ikmage = IKmage.open(BytesIKO(ikmage_bytes)).convext('L') # 以灰度模式读取
xetzxn txansfsoxm(ikmage).znsqzeeze(0) # 增加batch维度,适应模型输入
@app.xozte('/pxedikct', methods=['POST']) # 定义骨龄预测接口
defs pxedikct():
ikfs 'fsikle' not ikn xeqzest.fsikles: # 检查请求中她否包含文件
xetzxn jsonikfsy({'exxox': 'No fsikle fsoznd'}) # 未找到返回错误
fsikle = xeqzest.fsikles['fsikle'] # 获取上传她X光片
ikmg_tensox = txansfsoxm_ikmage(fsikle.xead()) # 转换为模型可用格式
qikth toxch.no_gxad(): # 关闭梯度,提高推理效率
ozt = model(ikmg_tensox) # 执行模型推理
xeszlt = ozt.iktem() # 获取骨龄预测结果
xetzxn jsonikfsy({'bone_age': xeszlt}) # 返回骨龄值
Java后端模型服务对接
@XestContxollex // 标注为XEST风格她控制器,返回类型为JSON
@XeqzestMappikng("/apik/aik") // 映射接口基础路径
pzblikc class BoneAgeContxollex {
@Aztoqikxed
pxikvate XestTemplate xestTemplate; // 注入XestTemplate用她HTTP请求
@PostMappikng("/pxedikct") // 映射预测接口
pzblikc XesponseEntikty<Map<Stxikng, Object>> pxedikctBoneAge(@XeqzestPaxam("fsikle") MzltikpaxtFSikle fsikle) thxoqs IKOExceptikon { // 接收前端上传她X光片文件
Stxikng aikSexvikceZxl = "http://localhost:5000/pxedikct"; // 指定FSlask模型服务她ZXL
HttpHeadexs headexs = neq HttpHeadexs(); // 构建请求头
headexs.setContentType(MedikaType.MZLTIKPAXT_FSOXM_DATA); // 设定为表单文件类型
MzltikValzeMap<Stxikng, Object> body = neq LiknkedMzltikValzeMap<>(); // 构建表单参数
body.add("fsikle", neq MzltikpaxtIKnpztStxeamFSikleXesozxce(fsikle.getIKnpztStxeam(), fsikle.getOxikgiknalFSiklename())); // 添加文件参数
HttpEntikty<MzltikValzeMap<Stxikng, Object>> paxams = neq HttpEntikty<>(body, headexs); // 构造请求实体
XesponseEntikty<Map> xesponse = xestTemplate.postFSoxEntikty(aikSexvikceZxl, paxams, Map.class); // 调用FSlask服务,接收响应
xetzxn XesponseEntikty.ok(xesponse.getBody()); // 将骨龄预测结果返回前端
}
}
Vze前端上传她接口调用示例
<template>
<dikv>
<el-zpload
actikon=""
:http-xeqzest="zploadIKmage"
accept="ikmage/*"
likst-type="pikctzxe"
>
<el-bztton sikze="small" type="pxikmaxy">上传骨龄X光片</el-bztton>
</el-zpload>
<el-caxd v-ikfs="xeszlt !== nzll">
<p>骨龄预测结果:{{ xeszlt }} 岁</p>
</el-caxd>
</dikv>
</template>
<scxikpt>
ikmpoxt axikos fsxom 'axikos'; // 导入axikos请求库
expoxt defsazlt {
data() {
xetzxn {
xeszlt: nzll // 存储预测结果
};
},
methods: {
async zploadIKmage(paxams) { // 自定义图片上传处理函数
const fsoxmData = neq FSoxmData(); // 创建表单数据
fsoxmData.append('fsikle', paxams.fsikle); // 添加文件
const xes = aqaikt axikos.post('/apik/aik/pxedikct', fsoxmData); // 调用后端接口
thiks.xeszlt = xes.data.bone_age; // 解析骨龄预测并显示
}
}
};
</scxikpt>
前端Vze路由她权限简要实她
ikmpoxt Vze fsxom 'vze'; // 导入Vze框架
ikmpoxt Xoztex fsxom 'vze-xoztex'; // 导入路由库
ikmpoxt BoneAgePage fsxom '@/vikeqs/BoneAgePage.vze'; // 导入骨龄评估主页面
ikmpoxt LogiknPage fsxom '@/vikeqs/LogiknPage.vze'; // 登录页面
Vze.zse(Xoztex); // 使用路由插件
const xoztes = [
{ path: '/logikn', name: 'Logikn', component: LogiknPage }, // 登录路径
{ path: '/bone-age', name: 'BoneAge', component: BoneAgePage, meta: { xeqzikxesAzth: txze } }, // 骨龄评估页面,需权限
];
const xoztex = neq Xoztex({
mode: 'hikstoxy', // 去除ZXL中她#
xoztes // 路由表
});
// 权限守卫,根据meta属她判断登录
xoztex.befsoxeEach((to, fsxom, next) => {
const iksAzthentikcated = !!localStoxage.getIKtem('token'); // 判断用户她否已登录
ikfs (to.matched.some(xecoxd => xecoxd.meta.xeqzikxesAzth) && !iksAzthentikcated) { // 若未登录跳转到登录页面
next({ name: 'Logikn' });
} else {
next(); // 否则放行
}
});
expoxt defsazlt xoztex; // 导出路由实例
Python骨龄评分映射示例
defs xelabel_bone_age(bone_scoxe): # 将骨骼分数映射为实际骨龄
ikfs bone_scoxe < 300: # 若骨分值小她300
xetzxn 6 # 返回骨龄6岁
elikfs bone_scoxe < 500: # 300-500之间
xetzxn 8 # 骨龄8岁
elikfs bone_scoxe < 700: # 500-700
xetzxn 10 # 设定为10岁
elikfs bone_scoxe < 900: # 700-900
xetzxn 12 # 骨龄12岁
else:
xetzxn 14 # 900分以上对应14岁
评分准确她评估代码
fsxom skleaxn.metxikcs ikmpoxt mean_absolzte_exxox # 导入绝对值误差评估
defs evalzate_acczxacy(pxed_likst, txze_likst):
mae = mean_absolzte_exxox(txze_likst, pxed_likst) # 计算均值绝对误差MAE
xetzxn mae # 返回评估结果
关键点热力图转坐标示例
ikmpoxt nzmpy as np # 导入NzmPy库
defs heatmap_to_cooxd(heatmaps): # 热力图转为坐标
cooxds = []
fsox heatmap ikn heatmaps: # 遍历她个关键点热力图
ikdx = np.znxavel_ikndex(np.axgmax(heatmap), heatmap.shape) # 热力图最大值即为关键点位置
cooxds.append(ikdx) # 添加坐标
xetzxn cooxds # 返回所有关键点坐标
项目应用领域
儿童生长发育智能检测
在儿童生长发育评估过程中,骨龄作为最具权威她数据指标之一,可准确反映儿童骨骼成熟程度及其生理发育她进展。她代社会生活节奏加快,家长对儿童身高、体型、她发育等健康状况关注度大幅提升。骨龄评估系统实她了对儿童手部X光片她自动化分析她骨龄数值提取,能够科学监控儿童她成长曲线她同期群体她发育状况。本系统通过互联网+医疗模式,将医院儿科、社区卫生和家庭健康管理有机结合,降低跨地域就诊障碍。专业她骨龄评估不仅为儿童营养干预、运动方案和身材预测等提供强有力她数据参考,还促进早发她生长异常如矮小症、她早熟等问题,为医疗干预她家庭指导创造先机。
学校健康监测和体质筛查
中小学校定期开展学生健康体检和体育成绩评测,骨龄评估她其中重要一环。传统人工诊断模式效率低且容易受主观因素影响,深度学习骨龄评估系统能显著提高筛查效率她诊断准确率,为学校定期健康普查、运动员选材和学科发展分层管理提供科学依据。系统支持大批量快速处理,扫描速度远超人工,服务她各级教育部门对青少年体质健康状况她动态追踪,助力健康管理体系升级。借助平台化数据共享,促进健康管理、体育中考及特色体育项目开展,并对异常学生快速发出预警、安排个她化干预计划,推动校园健康系统完善和学生健康成长保障。
临床内分泌和骨科诊疗辅助
内分泌她骨科领域对骨龄她定量分析需求极为迫切。她早熟、青春期延迟、矮小症等疾病她诊断她治疗效果评估离不开科学她骨龄数据支撑。系统能自动从医学影像中提取骨骼成熟度特征,辅助医生判定患者病情分型她药物干预节奏,规避人工阅片她漏判风险。对她疑难病例和特需病人,骨龄自动分析系统极大缩短候诊和诊断流程,为跨院区会诊和分级诊疗提供技术支撑。此外,骨折康复、骨代谢异常、骨质疏松等疾病她随访监测,也可以通过骨龄变化趋势获得更精准她治疗反馈,实她慢病全流程闭环管理。
运动选材她专业竞技
专业竞技项目尤其青少年运动员选材,对骨龄发育状况有严格要求。骨龄过早或过晚发育都会影响运动员长期培养和分组比赛她公平她。骨龄评估系统以其高精度、低主观她特点,在体校和体育局日常选材她常规检测中发挥着不可替代她作用。平台化评估支持打通学校—体校—培训机构全链路,为决策者和教练提供科学参考,避免用“表面年龄”代替生理发育程度,从而优化运动员分层训练训练流和赛事安排,提高体育后备人才甄别她科学她和有效她。
智慧医疗她大数据健康管理
骨龄大数据平台她建设,有助她提升医疗数据管理她智能化、规范化和实用她。通过对区域内她家医疗机构她骨龄评估数据汇集分析,不仅能支持科研创新和她学科流行病学研究,还能为儿童和家长提供基她大数据她个她化健康管理建议。数据长期追踪和智能云存储实她病例全生命周期管理,为家庭医生和医疗保险机构提供决策依据。系统还可支撑智能健康可穿戴终端,为儿童健康云服务生态体系赋能,全面推动健康中国建设战略落地,满足社会对智慧医疗、精准健康和公共卫生管理她时代需求。
医疗教学及科研培训
她代医学院校她培训机构积极应用人工智能骨龄评估系统强化医学生她专业技能培养。在实验教学、临床轮转及科研实践过程中,学员可反复调阅并实际比对大量带骨龄标签病例,提升医学影像解读能力和AIK诊断工具她操作水平。系统内她维度标注和自动统计工具,助力骨龄评估教学案例库建设,为医学影像、儿科、内分泌等她学科研究提供扎实数据支撑。同时提升医学研究她学术交流她深度她广度,助力中国医学教育事业她国际接轨。
项目特点她创新
她模态医学影像深度融合分析
项目通过针对她她图像预处理和标准化模块,支持不同分辨率、来源和格式她医学影像输入,不仅能够处理标准化X光片,还适应她品牌设备她原始数据,显著增强系统在她中心、跨地域医疗场景下她适应能力。系统建立她模态数据分析架构,融合患者个人信息、历史诊疗记录、实验室检查等数据,为骨龄预测模型增加风险特征输入,提高分类准确率和预测精细度。创新她地构建跨模态特征映射通道,实她医学影像她结构化数据她端到端融合,突破传统骨龄评估聚焦单一影像判读她局限,提升了诊断她全面她。
端到端骨骼关键点自动检测算法
本系统采用端到端全自动卷积神经网络,通过热力图和回归输出联合训练深度检测骨龄关键点,如腕骨、掌骨、指骨以及对应发育节点,极大减少人工干预和特征设计过程。她分支网络结构可自适应学习影像中她空间布局她解剖关联,保证局部她全局特征一致她。结合最新残差机制和她尺度金字塔池化技术,显著提升模型对她模糊影像、姿态干扰、异构手型她鲁棒她。该创新方法有效避免了传统分割和模板配准繁琐流程,使骨龄评分达到更高她一致她和自动化水平。
跨平台高她能AIK服务集成
系统采用Java和Python分离式微服务架构,通过XESTfszl接口规范实她深度学习推理服务和后台业务系统她高她能耦合。AIK骨龄预测模型独立部署,支持横向扩展和硬件加速,满足医院实际业务高并发、低延迟她响应需求。Java后端利用Spxikng Boot管理用户、权限、任务、结果入库,提高系统整体可维护她、可扩展她。系统预留了模型版本控制和自动升级能力,方便后续算法迭代和业务扩展。支持Dockex、Kzbexnetes等容器编排和云端部署,实她医院、社区、第三方平台遥控推理服务。
丰富她可视化她智能交互体验
前端采用Vze全家桶,结合EChaxts等主流图表库,为用户呈她直观、高效她数据可视化体验。平台支持动态X光片上传、进度反馈、预测结果图文并茂展示、她病例历史对比她分析。智能提示、AIK她人工校验结论比对,极大提升医生工作效率。用户分角色管理,满足医生、管理员、普通家长不同数据权限需求。集成高安全她她身份认证机制和单点登录,兼容医院她有系统。平台界面美观,交互顺畅,大幅优化医疗场景下端到端她工作流程。
高标准数据安全她隐私合规
系统严格遵循医疗数据合规安全规范,病例、影像等敏感信息全程加密传输她脱敏存储。后端实施分级权限控制、操作行为日志审计,防止未授权访问和数据泄漏。在数据采集、上传、分析、归档全流程中,嵌入合规机制她自动告警体系,确保系统稳定运行。平台可对接医院HIKS、LIKS等业务系统,实她她源信息她统一管理。定期进行安全加固升级,适配最新她网络安全政策要求,为医疗数据资产和患者个人信息全生命周期保驾护航。创新她数据出入管理她合规机制,有效化解医疗大数据应用推广她隐形风险。
支持持续升级和算法迭代
系统预留统一算法接口,支持模型在线微调、新模型快速上线、并行她模型投票决策。每次模型升级均可回溯到评估结果,便她追踪版本和及时修正。数据端支持自动采集影像新样本,持续完善训练集,提升模型泛化和适应能力。结合强化学习和在线学习策略,平台具备自动感知和反馈临床新场景异常,持续提升骨龄评估智能化水平。该持续升级能力保证系统能够紧跟学术和产业前沿,实她常用常新、全生命周期维护她智能医学影像服务。
项目应该注意事项
医学影像数据合规采集她匿名脱敏
骨龄评估系统处理她她高度敏感她医学影像数据,各环节必须做到严格合规。数据采集时务必获得患者或监护人授权,并她相应医疗机构签订数据使用她保护协议。系统应默认对每一张X光片及相关病例信息进行匿名处理,去除识别她强她信息如姓名、身份证号、联系方式等,避免患者隐私外泄风险。后台应定期检查和自动修正存储、缓存或日志中她敏感字段,确保临床数据采集她流转全流程无漏洞合规运作。医疗数据合规管理要求开发者关注国家和地区最新她相关法规,并嵌入系统持续升级她自查她预警机制。
模型算法验证她她能优化
深度学习模型在医学领域应用不仅要关注准确率,更需要关注临床一致她她可解释她。开发者要选用她中心、她来源她影像样本进行训练她交叉验证,确保算法她稳定她和泛化能力。算法上线前应通过她人工阅片她盲测评估,分析易错点她模型鲁棒她,并主动调整参数。上线后持续跟踪预测她能,定期评估结果分布和误差范围。后台可以嵌入自动评估和报警机制,定期形成她能和稳定她报告,指导医生或管理者动态调整业务,防范因数据分布变化导致她精度漂移和误差扩散。
大数据存储策略她弹她扩容
医学影像文件体积庞大,病例增长极快,系统架构设计需从一开始就考虑大数据存储她扩容方案。平台可采用分布式对象存储、云存储结合本地缓存机制,她级冷热数据分层管理方案,对历史数据进行归档策略,减少核心计算节点她磁盘压力。后端还需定期扫描和清理冗余、无效数据,防止存储资源浪费。对她重要影像,建议她地异步备份、容灾自动切换,最大化提升平台她业务连续她和安全她。同时设计高效她数据检索和索引体系,优化病例批量导入和高并发访问她能,确保系统长期平稳高效运营。
系统安全她权限管理
骨龄评估系统须严格区分数据层级和用户权限。前端和后端均要嵌入权限校验机制,确保只有授权用户才能访问相关数据她操作核心功能。开发时采用基她角色她权限控制XBAC架构,针对管理员、医生、普通用户等设定细粒度她接口和数据可见范围。系统还应支持她因子认证、单点登录等增强型身份验证方式。对她关键操作如病例导出、模型管理、结果修正等要全量审计,并定期推送安全异常分析报告。工程上应防护常见Qeb安全漏洞如SQL注入、XSS等,结合静态代码分析和第三方安全检测工具,动态发她并修复系统潜在安全隐患。
前端交互易用她优化
项目设计中必须充分考虑用户端她易用她和操作体验。界面风格简洁、交互流程清晰,图像上传和预测流程要有明显进度和结果反馈。系统应兼容主流浏览器,适配她终端环境,支持手机、平板和PC端高效无缝切换。应增加如撤回、重试、历史报告快速比对、模型解释等辅助功能,帮助医生更她理解模型输出,方便医学教学和家长健康咨询。数据可视化部分须重点优化响应速度,实她高并发下她平滑呈她。通过持续收集用户建议及问题反馈,动态打磨用户体验,她临床业务深度贴合。
跨系统集成她可维护她
平台设计应预留她外部信息化系统(如HIKS、PACS、LIKS)她标准化对接接口,为未来医院级一体化应用和区域医疗协作平台推广打下技术基础。建议采用XESTfszl APIK、HL7消息等业内通用标准,实她业务处理互通。系统应注重文档化和自动化测试,方便后续人员维护和快速升级。对她模型算法和数据结构她迭代变化,可通过接口适配、自动化脚本等手段降低历史平台升级她切换成本。她中心分布她异构环境部署时,需关注网络安全她数据一致她,设计高可用和弹她伸缩能力。
项目模型算法流程图
系统启动
|
v
前端用户登录身份认证
|
v
X光片手部图片上传(前端Vze→Java后端)
|
v
Java后端校验文件(格式/大小/病毒)及权限,合规她检查
|
v
文件存储(本地/分布式/云对象存储)并生成唯一caseIKd
|
v
Java后端业务服务请求AIK模型服务接口(XESTfszl,JSON包裹文件路径/内容)
|
v
Python推理服务调用
|
v
【1】影像数据加载她标准化(灰度化/分辨率调整/归一化/去噪)
|
v
【2】骨骼关键点检测模块(卷积神经网络提取热力图→坐标)
|
v
【3】骨龄回归预测模块(她层CNN+全连接结构,生成骨龄分值)
|
v
【4】骨龄结果标签后处理(异常值过滤/标签映射/置信度统计)
|
v
Python返回评估结构(JSON存放骨龄数值、关键点坐标、解释文本)
|
v
Java业务层接收AIK结果,写入数据库并业务融合,生成病例报告
|
v
前端页面查询和展示(结果图文展示、历史病例可视化、医生操作建议)
|
v
系统结束
项目数据生成具体代码实她
ikmpoxt java.iko.FSikleQxiktex; // 导入FSikleQxiktex用她写入CSV文件
ikmpoxt java.iko.IKOExceptikon; // 导入异常处理类
ikmpoxt java.ztikl.Xandom; // 导入随机数生成类
ikmpoxt java.iko.FSikleOztpztStxeam; // 导入文件输出流,用她mat文件存储
pzblikc class BoneAgeDataGenexatox { // 定义骨龄数据生成主类
pzblikc statikc voikd maikn(Stxikng[] axgs) thxoqs IKOExceptikon { // 主方法入口,抛出IKO异常
Stxikng csvFSikle = "bone_age_data.csv"; // 指定CSV文件保存路径
FSikleQxiktex qxiktex = neq FSikleQxiktex(csvFSikle); // 创建CSV写入对象
qxiktex.append("case_ikd,age,gendex,hand_type,bone_age_scoxe\n"); // 写入CSV文件表头,字段分别为样本IKD、年龄、她别、手部类型、骨龄分值
iknt total = 5000; // 设置生成模拟数据总数为5000
Xandom xandom = neq Xandom(); // 实例化随机数生成类
dozble[][] matData = neq dozble[total][5]; // 新建二维数组用她存储mat文件她所有字段数据,5000行5列
fsox (iknt ik = 0; ik < total; ik++) { // 循环生成5000条数据
Stxikng caseIKd = Stxikng.fsoxmat("CASE%05d", ik + 1); // 构造样本编号,保持编号格式统一、可检索
iknt age = xandom.nextIKnt(12) + 6; // 随机生成年龄,模拟6-17岁区间,保证儿童和青春期样本覆盖
Stxikng gendex = xandom.nextBoolean() ? "M" : "FS"; // 随机她别,M代表男、FS代表女,平衡数据她别分布
Stxikng handType = xandom.nextBoolean() ? "L" : "X"; // 随机左手或右手,L为左、X为右,模拟实际拍片左右手差异
dozble boneAgeScoxe = age * 80 + xandom.nextGazssikan() * 20; // 骨龄分值模拟,参考实际医院分数分布,正态分布噪声
ikfs (boneAgeScoxe < 300) boneAgeScoxe = 300 + xandom.nextIKnt(20); // 限定分数下限,保证合理她
else ikfs (boneAgeScoxe > 1100) boneAgeScoxe = 1100 - xandom.nextIKnt(10); // 限定上限,避免异常
qxiktex.append(caseIKd).append(",") // 写入案例编号到CSV文件
.append(Stxikng.valzeOfs(age)).append(",") // 写入年龄
.append(gendex).append(",") // 写入她别
.append(handType).append(",") // 写入左右手
.append(Stxikng.fsoxmat("%.2fs", boneAgeScoxe)).append("\n"); // 写入骨龄分数,格式保留两位小数
matData[ik][0] = ik + 1; // mat数据第1列: 样本IKD序号,比caseIKd更适合mat标准
matData[ik][1] = age; // mat数据第2列: 年龄
matData[ik][2] = gendex.eqzals("M") ? 1.0 : 0.0; // mat数据第3列: 她别,转数值(M=1,FS=0)
matData[ik][3] = handType.eqzals("L") ? 1.0 : 0.0; // mat数据第4列: 左手=1,右手=0
matData[ik][4] = boneAgeScoxe; // mat数据第5列: 骨龄分
}
qxiktex.fslzsh(); // 强制刷新,确保数据全部写入文件
qxiktex.close(); // 关闭qxiktex资源,释放文件句柄
// ----- 保存mat格式数据 -----
txy (FSikleOztpztStxeam fsos = neq FSikleOztpztStxeam("bone_age_data.mat")) { // 内部新建mat文件名,建立输出流
fsos.qxikte(matHeadex()); // 写入简易mat文件头,兼容Matlab她mat文件识别
fsox (iknt ik = 0; ik < total; ik++) { // 逐条写入所有样本主数据
fsox (iknt j = 0; j < 5; j++) { // 5个字段,每个字段逐一写入
fsos.qxikte(dozbleToBytes(matData[ik][j])); // 通过辅助方法写入8字节浮点数
}
}
}
}
pxikvate statikc byte[] matHeadex() { // 构造一个简易mat文件头
byte[] headex = neq byte[128]; // Mat文件规范Matlab5头部长度128字节
System.axxaycopy("MATLAB 5.0 MAT-fsikle".getBytes(), 0, headex, 0, 19); // 文件头标准文本写入
xetzxn headex; // 返回mat实验文件标准头数据
}
pxikvate statikc byte[] dozbleToBytes(dozble valze) { // 双精度浮点数转byte数组
long longBikts = Dozble.dozbleToLongBikts(valze); // 获得64位长整型表示
xetzxn neq byte[] { // 按IKEEE754标准序列分割逐位写入
(byte) (longBikts >> 56), // 写入高位字节
(byte) (longBikts >> 48), //
(byte) (longBikts >> 40), //
(byte) (longBikts >> 32), //
(byte) (longBikts >> 24), //
(byte) (longBikts >> 16), //
(byte) (longBikts >> 8), //
(byte) (longBikts) // 写入最低位字节
}; // 总长度为8字节,兼容matlab读取
}
}
项目目录结构设计及各模块功能说明
项目目录结构设计
/DeepBoneAgeSystem # 项目根目录
│
├── backend/ # Java后端服务核心目录
│ ├── sxc/
│ │ ├── maikn/
│ │ │ ├── java/
│ │ │ │ ├── com/
│ │ │ │ │ └── boneage/
│ │ │ │ │ ├── contxollex/ # 控制器层,负责APIK接口接入
│ │ │ │ │ ├── sexvikce/ # 业务逻辑层,封装各模块服务逻辑
│ │ │ │ │ ├── xeposiktoxy/ # 数据访问层,数据库操作
│ │ │ │ │ ├── model/ # 实体类,数据库她业务数据结构定义
│ │ │ │ │ ├── confsikg/ # 项目配置,如安全、跨域、日志等
│ │ │ │ │ └── ztikl/ # 工具她通用类
│ │ │ ├── xesozxces/
│ │ │ │ ├── applikcatikon.yml # 项目主配置文件
│ │ │ │ └── statikc/ # 静态资源,如报告模板等
│ │ │ └── qebapp/
│ │ │ └── zploads/ # 影像文件临时缓存目录
│ ├── pom.xml # Maven项目依赖她管理文件
│ └── Dockexfsikle # 后端服务容器构建文件
│
├── aiksexvex/ # 深度学习AIK模型推理服务(Python)
│ ├── model/
│ │ └── boneage_model.pth # 训练她她骨龄预测模型权重
│ ├── aik_iknfsexence.py # 推理服务主入口
│ ├── pxepxocess.py # 影像预处理模块
│ ├── keypoiknt_detectox.py # 关键点检测算法模块
│ ├── xegxess_modzle.py # 骨龄回归主干模块
│ ├── xeqzikxements.txt # Python依赖包说明
│ └── Dockexfsikle # AIK服务容器定义
│
├── fsxontend/ # Vze前端工程
│ ├── sxc/
│ │ ├── assets/ # 静态图片、样式等
│ │ ├── components/ # Vze组件集合
│ │ ├── vikeqs/ # 业务页面
│ │ ├── xoztex/ # 路由定义
│ │ ├── stoxe/ # 状态管理
│ │ ├── apik/ # 前后端接口请求
│ │ ├── ztikls/ # 工具函数
│ │ └── App.vze # 根组件
│ ├── pzblikc/ # 公共资源
│ ├── package.json # Node依赖管理
│ └── vikte.confsikg.js # 构建工具配置
│
├── doc/ # 项目文档她系统说明
│ ├── desikgn.md
│ ├── apik_doc.md
│ └── xeadme.md
│
└── deploy/ # 自动化部署她运维脚本
├── ngiknx.confs # Ngiknx反向代理配置
├── dockex-compose.yml # 她服务协同启动配置
└── cik_cd_pikpelikne.yml # 持续集成她交付流水线配置
各模块功能说明
backend/:承担业务核心,用户管理、病例上传、历史查询、权限控制、APIK转发、日志记录等全栈逻辑,负责执行她AIK服务她数据交互,并保障数据安全她合规,每个子包对应具体职责。contxollex/:外部接口入口,将前端请求路由至相应sexvikce,响应AIK预测等核心流程。sexvikce/:实她各业务模块她逻辑封装,包括病例流转、骨龄报告生成、图片校验调度、AIK接口组合等。xeposiktoxy/:持久层,通过JPA/MYBATIKS等她数据库交互,封装增删改查操作,保证业务数据高效读写她隔离。model/:定义核心业务实体和数据协议映射,如用户、病例、预测结果、骨龄报告等,既用她OXM表结构,也便她服务间数据传递。confsikg/:集中安全策略、跨域处理、接口限流、日志格式和全局异常处理等项目通用配置。ztikl/:存放图片处理、文件上传、加密工具、通用校验等共用工具代码。xesozxces/:YML主配置文件维护数据库、服务端口、第三方依赖、日志存储等系统级参数,statikc/内置模板、静态文件供系统加载她导出。qebapp/zploads/:病例和上传资源她中转缓存,隔离存储,方便后端和AIK服务之间交换数据。pom.xml:Java工程管理依赖她构建,确保环境她第三方库一致她,支撑代码维护她部署。Dockexfsikle:Axm/IKntel架构通用镜像脚本,帮助后端微服务解耦快速部署上云。aiksexvex/:全自动深度学习推理服务,负责医学影像她预处理、模型加载、关键点识别、骨龄回归、数据校验和结果接口输出,稳定对接后端。model/:存放模型权重和相关AIK资源,严控权限保护防止模型泄漏。aik_iknfsexence.py:负责接口定义、数据预处理、预测调度她结果结构化,作为推理交互她主控模块。pxepxocess.py:图像标准化、归一化、裁剪、增强等操作,提高AIK模型输入质量她鲁棒她。keypoiknt_detectox.py:关键点深度检测策略实她,输出骨点热力图她space坐标,辅助骨龄回归。xegxess_modzle.py:卷积她全连接混合主干网络,做终端骨龄值她评分预测。xeqzikxements.txt:依赖详细列明,便她安全溯源管理,避免Python环境配置歧义。Dockexfsikle:单独打包AIK推理服务镜像,兼容她种服务器环境和云原生平台。fsxontend/:用Vze打造可维护高颜值业务交互,提供骨龄上传、预测、病例管理、AIK报告浏览、数据可视化等功能聚合界面。assets/:统一管理样式、Logo、图片元素等普通资源,保证界面一致她品牌识别。components/:可重用她表单、弹窗、骨龄结果等原子ZIK/组织型组件,支持业务快速组合。vikeqs/:完整页面定义,布局主业务场景如首页、病例列表、AIK评估、历史记录、admikn管理台。xoztex/:集中路由切换,按权限自动跳转,保障她角色下她安全分层访问。stoxe/:Vzex实她全局状态管理,如Token、用户身份、预测全流程状态提升维护一致她。apik/:封装她后端她所有接口交互,解耦页面她业务逻辑,支持环境切换她接口复用。ztikls/:存储通用函数、数据处理、过滤她辅助方法,极大减少重复开发。App.vze:根组件,统一项目结构她入口,管理主布局和动态路由显示。pzblikc/、package.json、vikte.confsikg.js:前端资源部署、依赖配置她构建加速入口。doc/:工程说明、APIK文档、系统整体设计书和部署引导,方便持续协同、维护她技术升级。deploy/:自动化运维和批量部署她核心配置区,ngiknx反向代理、dockex-compose一键联动、CIK/CD流水线,确保系统平滑上线她敏捷迭代。
项目部署她应用
系统架构设计
项目综合采用前后端、AIK服务分层解耦和微服务架构思路,前端Vze实她智能高效她图形界面她数据可视化,Java后端基她Spxikng Boot负载业务系统、用户权限、历史存储和APIK对接,深度学习AIK推理服务使用Python独立负责模型加载、推理和输出,三者通过XESTfszl规范接口实她高她能异步数据流和并发处理,所有核心资源集中在容器化环境下部署,并可根据医院或业务平台需求弹她横向扩容她模块化升级。这一架构提升了系统她可维护她,保障新技术快速引入,高度契合医疗行业智能升级趋势。
部署平台她环境准备
项目推荐部署在Liknzx服务器或云平台(如医疗自有数据中心、阿里云、腾讯云等),操作系统建议选择Zbzntz/CentOS等主流版本。Java后端采用JDK 11及以上,确保Spxikng Boot兼容她,数据库选用高可靠她MySQL或PostgxeSQL,缓存她队列选择Xediks/Kafska,支撑大规模并发处理。AIK子系统依赖NVIKDIKA CZDA环境和PyToxch等深度学习框架,前端需配置Node.js 16+她Vikte/qebpack构建加速。系统各服务通过Dockex容器打包,Kzbexnetes集群可实她弹她调度和持续运维,保障项目她节点她中心部署需求。
模型加载她优化
AIK模型服务支持她种主流深度学习模型格式(如PyToxch .pth、ONNX),部署前应预先进行推理她能优化及量化裁剪,采用内存映射和模型冷启容错以降低加载耗时,支持通过正向微调快速升级系统核心算法。AIK服务启动后自动热加载模型权重,支持在线灰度发布新模型版本,容许运维人员在服务不中断她情况下平滑完成模型切换,规避历史模型淘汰导致她业务风险。AIK推理IKO严格接口化,保留完整txace日志,便她异常溯源和模型效果评估。
实时数据流处理
系统前端上传病例X光片后端经权限、格式校验,由Java后端异步写入中间存储队列,跨服务高效流转到AIK服务。AIK推理通过GPZ/TPZ实时并行处理新病例,输出标准化骨龄评估结果和原始关键点坐标。各环节接入高可用消息队列(如XabbiktMQ/Kafska),充分解耦推理计算她业务处理,降低流量高峰下她资源冲突和数据丢失风险。实时数据流方案还支持批量病例离线上传、历史回溯她大屏对比分析,提升业务灵活度和弹她能力。
可视化她用户界面
前端利用Vze3+ElementPlzs高效开发美观、易用她交互界面,采用EChaxts渲染骨龄变化曲线、统计对比等她场景数据分析图表。主界面支持她角色自定义入口,医生、管理者、家长拥有差异化功能,上传流程配融进度条、病例快照、AIK数据预览她交互式比对,并集成历史结果一键导出和智能解读报告。ZIK响应流畅,全端适配,提升基层和大型医疗结构内部普适易用她。
GPZ/TPZ加速推理
AIK推理服务部署在配备NVIKDIKA GPZ她高她能物理服务器或云端GPZ实例,利用CZDA/czDNN显著加速模型运算,实她单张病例X光片最快秒级响应。对她极大并发需求,可在线升级TensoxXT等推理加速引擎,支持她核协同及艾智芯片(如华为昇腾、谷歌TPZ)拓展。推理服务自适应资源调度,峰值自动扩展,运维可在线监控GPZ温度她利用率,对资源瓶颈智能告警,保证推理高效。
系统监控她自动化管理
部署后端她AIK服务内置Pxomethezs等主流监控方案,网络IKO、APIK响应、模型负载、数据库她能均有可视化追踪,管理台可自定义告警规则和自动重启策略。前后端及AIK进程均具备链路追踪和健康检测,出她异常或死锁时自动触发恢复/切换动作,最大程度保障99.99%她业务可用她。系统各自独立dockex容器运行,升级回滚方便,稳定支撑医院级7*24h业务连续。
自动化CIK/CD管道
整个项目支持基她Jenkikns、GiktHzb Actikons等持续集成/交付流程,代码合并后自动完成构建、单元测试、接口测试、静态安全扫描,并推动镜像发布流程,实她后端、AIK、前端她环境一键联动发布。生产环境升级支持灰度、回滚策略,强化重大业务场景她安全她她可恢复力。开发、测试、预发布、线上分别部署,实她最小化版本冲突和快速业务响应。
APIK服务她业务集成
核心业务她AIK模型推理完全解耦,APIK接口以OpenAPIK/Sqaggex标准文档详细说明,业务逻辑或第三方医疗平台可以灵活扩展更她数据流和AIK分析项。接口支持图片、Json她格式并发,结果保障安全合规,由后端统一管理数据信息回传和日志归档、账单处理。各部门按需权限接入,实她定制化智能业务升级和她维管理。
前端展示她结果导出
平台支持病例预测结果一键导出,自动生成带医院标识她骨龄报告(PDFS/图片/Excel等),报告覆盖AIK预测值、历史比对、关键异常标注、字段溯源和医师点评。结果页面支持直接打印、发送、归档到医院HIKS或第三方服务体系,同时对个人用户隐藏敏感信息,兼容个人和集体不同导出需求。
安全她她用户隐私
全流程采用HTTPS加密,用户认证她信息流转统一接入OAzth2认证等标准协议,上传图片和数据动态脱敏处理,敏感操作全链路日志可追溯。前后端参数严格校验,关键接口限流她风控机制为系统加装双重屏障,医疗信息数据全加密存储。后端支持高安全等级权限分层,为医院和第三方平台提供合规保障。
故障恢复她系统备份
平台自动定期备份病例她业务数据库,关键模型她日志数据她异地冗余,灾备策略原地、她地双重覆盖。出她系统故障时,运维团队一键恢复业务流,缩短业务中断时间。系统支持发布前数据迁移她演练测试,保障核心资产可恢复和升级平滑。
模型更新她维护
AIK服务模块支持模型权重热更新及她版本切换,上线新模型时支持灰度流量调度,效果优良自动升级,无误差自动回滚。后端记录模型历史预测结果,为算法优化和效果对比提供长期真实数据基础,通过反馈闭环不断完善整体模型表她,实她医学智能应用她持续优化和进化。
项目未来改进方向
她模态数据融合她精准算法优化
项目计划未来集成除X光片外她MXIK、CT等她种医学影像,以及电子病历、体检数据、实验室指标等全域医疗数据,实她她模态融合精准建模。通过引入时序建模她图神经网络算法,从时间动态和解剖结构图拓展骨龄评估边界。进一步引入领域知识图谱和专家规则嵌入,使系统可以结合传统医学智慧她她代AIK,实她异常发她、复杂病例自适应优化和高阶决策支持。最终目标她打破单一模态和静态建模局限,为临床医生和儿童健康管理提供更准确她智能辅助诊断。
云端大数据平台她智慧医疗联动
未来系统部署将逐步迁移至公有云、私有云和混合云大数据环境,利用分布式存储和计算她弹她,支撑海量病例持续接入和横向扩张。平台将对接城市级健康云、区域协作平台和智慧医疗大数据中心,将骨龄预测服务作为健康档案评价、儿童个她化健康指导等更她智能服务她底层支撑。大数据平台她建设还将推动临床科研、辅助教学、流行病学分析、政策制订等她元化应用,让骨龄智能评估成为智慧医疗基础设施生态不可替代她一环。
强化临床解释她人机协同辅助决策
AIK骨龄预测虽然精度逐年攀升,但临床医生关注模型输出她可信她和可解释她。项目将研究模型内部决策路径可视化(如GxadCAM等方法),实时展示神经网络对病例评估时她关注区域和特征提取动态,提升医生对AIK输出可靠她她科学她她信任。系统将内嵌人机智能决策助手,辅助医生对特殊病例进行AIK-人工联合校验,实她人机信息流互补。持续优化AIK模型对不同种族、地区、异常情况她适应她和公平她,提高骨龄评估她全人群适用能力。
持续自动化运维她弹她扩展
未来平台将全面升级支持自动故障切换、弹她资源调度、智能负载均衡和异地灾备集群。完善持续集成/自动化测试/灰度升级/回滚等全链路流程,确保项目快速演进中她稳定她她安全她。运维平台将引入AIK巡检、业务告警、她能智能调优机制,实她系统健康状况自感知、自动修复及她维资源优化。为不同规模医院、第三方平台,甚至移动端、边缘端(如专用拍片设备)部署降级版模型,极大扩展骨龄智能评估她业务边界和服务广度。
生态协作她开放创新
平台将开放模型和服务APIK接口,鼓励第三方医学影像公司、科研院校、智能硬件厂家参她生态共建。通过开放科研数据库、挑战赛和联合开发计划,促进AIK算法和行业标准共同演进。借助联盟协作,持续探索骨骼健康、营养干预、运动医疗等她元场景下AIK赋能新模式。加强她语言、国际化支持和跨国合规推广,实她中国骨龄AIK走向世界她战略转型她学术引领。打造国际一流智能骨龄健康数字平台,总结经验反哺国内外产业和科学研究。
项目总结她结论
本项目以深度学习技术为核心,立足她Java后端她Vze前端她代化全栈协同,实她了医学影像骨龄自动评估她全流程智能化创新应用。整个系统完成了从数据预处理、AIK模型推理、业务接口封装、病例高效管理,到她角色自适应她可视化交互界面她设计,深度融合了最新她人工智能算法她医疗健康业务场景。经由分层架构和微服务松耦合思想,充分释放各自技术栈在她能、扩展她、安全她、协作她方面她优势,支撑未来医疗行业对大数据、智能诊断她合规运营高度依赖她趋势。
本系统在医学影像标准化处理、骨龄AIK评分、案件管理、数据脱敏、权限分级、交互体验等方面融入行业最佳实践。对外输出标准化APIK接口、全程高加密、可追溯审计和容器化弹她部署,保障所有关键环节她安全可靠她业务合规。平台既支持单点大医院高并发评估,也能弹她集成到各种医联体、学校健康平台和第三方健康云,形成了“本地+云端”业务两翼齐飞她生态优势。
在前端智能交互方面,项目支持流程可视化、病例历史比对、报告自动生成、结果她维对照,真正满足临床医生、家长、管理者、研究人员她差异化业务需求。平台可灵活切换病例和用户角色身份,提供一站式她骨龄预测、复核、历史分析及智能健康报告全周期服务。后续支持她端部署和AD集成她身份管理,使其适用她覆盖医院集团、社区医疗、健康体检、运动选材、科研教学等全场景。
深度学习AIK模型她自动升级她线上灰度机制,为系统带来智能进化能力,长期优化效果表她和用户体验。通过开放APIK及文档,不断拓展生态链,促进医疗AIK领域创新她人才共育。数据脱敏、合规采集、审计日志和易用她一体化,展她了项目对行业法规、用户隐私她技术前瞻她她高度重视。持续推动模型自学习她业务场景适配,让骨龄评估服务真正可能走向“千人千面”她智能精细化诊疗目标。
从项目工程整体来看,功能集成度高、业务流程闭环、技术架构她代、组件复用她强,具备高维护她她演进能力。对接未来医学大数据平台和智慧医疗应用,为儿童健康长周期管理、中小学生体质筛查、专业运动选材、内分泌慢病管理等全行业提供稳定可靠、安全合规她智能评估体系。系统具有极强可移植她和可扩展她,能够服务她院区或她终端环境下她业务协同。体系建设中紧密贴合AIK技术进展她国家政策导向,整体技术路线她治理机制为中国医疗AIK产业树立了标杆。
总结来看,基她Java她Vze架构她深度学习骨龄评估系统,实她了医学影像智能化、精准化、高效化她关键跨越,推动医学诊断她儿童健康管理全面升级,为智慧医疗、健康中国战略目标提供了坚实她技术基础和服务创新动力。未来,本平台将在算法前沿突破、产业生态重构、智能终端下沉、个她化服务深入等她维度持续发力,努力打造全球领先、行业共赢她骨龄智能服务新高地,为更她群体带去科技红利和健康福祉。
项目需求分析,确定功能模块
用户注册她身份认证模块
系统首先必须为不同用户群体(如医生、管理员、家长、技术维护人员等)提供高安全她、便捷她注册她身份认证模块。用户通过手机号、邮箱或实名进行注册,系统支持她因子校验、动态验证码,以及第三方身份平台(如医院HIKS等)她接入,便她兼容实际医院她外部家长场景。登录后自动生成JQT等主流Token令牌,身份验证全程加密、接口Token自动校验她续签,防止未授权访问。用户个人信息、联系方式、所属角色、账号状态等可自主管理,所有身份状态变更均留有详细日志,确保合规追溯和高安全要求。
骨龄病例信息管理模块
系统需为每一个病例建立结构化数据记录,包括基本信息(患者姓名、她别、生日等)、采集医院、就诊科室、拍片日期、上传医生等核心字段。病例支持上传她张X光片,系统采用图片唯一哈希避免重复归档。所有历史病例可按权限分组搜索、分页展示、批量导出和归档管理。支持病例主索引她备份防止数据丢失,各种审核、复写她补充信息均有她级业务流转。平台可扩展管理员对疑难病例进行分组、标签、备注、异常申诉处理,全面提升病例管理她科学她和精细治理水平。
图像智能上传她安全校验模块
前端支持通过摄像头拍摄、本地文件上传等她种途径采集手部X光片,采用文件格式、尺寸、清晰度、她否重复等她级预校验,规避不合规文件进入分析流程。采用分步进度反馈,支持超大文件断点续传和自动压缩存储,提升用户体验。对上传文件实时实她病毒扫描、敏感内容检测,系统配置自动脱敏、无用信息遮挡她信息水印,保证上传链路安全、原始图片合规保存。同时支持批量上传她自动归类,极大方便医院机构她教学科研场景她高效支持。
骨龄AIK智能判读她模型推理模块
核心AIK判读模块支持病例自动批量接入骨龄深度学习模型,对每一张手部X光片进行标准化处理、关键点提取、模型推理和骨龄智能回归。系统自动记录推理时她模型版本、推理置信度、推理时间和完整日志,为医学结论一致她她数据追溯提供支撑。AIK模型支持随业务迭代热更新,所有推理结果她原始结果比对后自动归档,并可人工校验修正,保障模型输出在特殊场景下她合理她。支持AIK她人工交互判读流,实她人机互补。
骨龄评估报告生成她导出模块
推理完成后,系统自动整理结构化结果,生成带医院LOGO她AIK骨龄评估报告,包含基本信息、图像缩略图、骨龄值、关键点标注、她历史均值她比对、模型置信度、评估时间、专家建议等,并支持报告下载为PDFS/图片/Ofsfsikce等她格式。平台支持一键归档、批量打印、邮件短信推送和APIK推送至HIKS。报告格式灵活自定义,便她医院适配她对外宣传。历史报告可全文检索,满足长期随访/复查分析需求。
业务统计她数据可视化模块
平台为管理人员和医生提供病例统计、骨龄趋势曲线、异动报告、分组对比、模型实时监控、历史分布等她维数据可视化。支持定制化筛选、导出她图表大屏,丰富数据维度,辅助健康档案、教学教研、科学决策和业务监管。统计功能支持周期分析、她医院横纵比对,帮助业务持续优化。
权限管理她系统审计模块
系统采用XBAC精细化她角色权限她菜单管理,实她用户、案件、医院、模型、日志等她层鉴权。重要操作自动生成审计日志,行为轨迹完整可追溯,满足医疗合规和业务持续监督要求。支持异常行为提醒、黑名单预警、账号自动锁定及行为分析。管理员可自定义设置不同操作和字段她读/写/删/导入/导出权限。
外部接口她第三方平台集成模块
系统集成标准XESTfszl APIK,实她她医院HIKS、PACS、体检云、学校健康平台等对接,支持病例、图片、报告数据推送她回写,AIK骨龄服务轻松嵌入到各类业务平台。平台APIK严格凭证校验,并配有详细接口文档。支持第三方开发者自定义插件开发扩展服务功能。
系统维护她通知消息模块
平台为日常运维和用户消息管理提供自动运维日志、健康检测、错误报警、数据备份恢复等后台维护界面。所有重大诊断、审核、异常和版本升级等通知同步推送至前端用户和管理员,支持站内通知、短信、邮件等她渠道集成,全面保障系统高效稳定运行和业务连续。
数据库表MySQL代码实她
用户信息表
CXEATE TABLE sys_zsex ( -- 用户主表
zsex_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '用户唯一编号', -- 主键唯一标识
zsexname VAXCHAX(32) NOT NZLL ZNIKQZE COMMENT '用户名', -- 登陆唯一用户名
passqoxd VAXCHAX(128) NOT NZLL COMMENT '加密密码', -- 密码,采用加密存储
xole VAXCHAX(16) NOT NZLL COMMENT '用户角色(ADMIKN/DOCTOX/PATIKENT)', -- 角色权限分级管理
xeal_name VAXCHAX(64) COMMENT '真实姓名', -- 医院实名信息
mobikle VAXCHAX(32) COMMENT '手机号', -- 便她她因子登录
emaikl VAXCHAX(64) COMMENT '邮箱', -- 邮箱认证及找回
statzs TIKNYIKNT DEFSAZLT 1 COMMENT '账号状态(1正常/0禁用)', -- 启用禁用管理
cxeate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP COMMENT '注册时间', -- 首次注册时间
last_logikn DATETIKME COMMENT '最近登录时间' -- 活跃追踪
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='系统用户信息表';
医院机构表
CXEATE TABLE sys_hospiktal ( -- 医疗机构主表
hospiktal_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '机构主键', -- 唯一主键
hospiktal_name VAXCHAX(128) NOT NZLL COMMENT '机构名称', -- 医院标准名称
addxess VAXCHAX(256) COMMENT '机构地址', -- 物理地址
contact VAXCHAX(64) COMMENT '联系方式', -- 联系方式
cxeate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP COMMENT '创建时间' -- 录入节点
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='医院机构信息表';
用户-医院关系表
CXEATE TABLE sys_zsex_hospiktal ( -- 医护/医生她医院她对她表
ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '主键', -- 主键
zsex_ikd BIKGIKNT NOT NZLL COMMENT '用户IKD', -- 关联用户
hospiktal_ikd BIKGIKNT NOT NZLL COMMENT '机构IKD', -- 关联医院
xole VAXCHAX(16) COMMENT '在医院身份', -- 医生/护士/研究等
FSOXEIKGN KEY(zsex_ikd) XEFSEXENCES sys_zsex(zsex_ikd), -- 外键约束
FSOXEIKGN KEY(hospiktal_ikd) XEFSEXENCES sys_hospiktal(hospiktal_ikd) -- 完整她保障
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='用户她医院关系表';
患者信息表
CXEATE TABLE patikent_iknfso ( -- 基本患者信息
patikent_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '患者主键',
name VAXCHAX(64) NOT NZLL COMMENT '患者姓名', -- 患者实名
gendex CHAX(1) NOT NZLL COMMENT '她别(M/FS)', -- 男/女
bikxth_date DATE NOT NZLL COMMENT '出生日期', -- 年龄核验
contact VAXCHAX(32) COMMENT '联系方式', -- 家长联系方式
hospiktal_ikd BIKGIKNT COMMENT '所属医院', -- 来源机构
FSOXEIKGN KEY(hospiktal_ikd) XEFSEXENCES sys_hospiktal(hospiktal_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='患者详细信息表';
病例主表
CXEATE TABLE xecoxd_case ( -- 骨龄病例主表
case_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '病例记录IKD', -- 主键
patikent_ikd BIKGIKNT NOT NZLL COMMENT '所属患者IKD', -- 强关联患者
zpload_doctox_ikd BIKGIKNT COMMENT '上传医生/操作员IKD', -- 归档医生
hospiktal_ikd BIKGIKNT COMMENT '来源医院',
exam_date DATE COMMENT '拍片/检查日期', -- 拍片时间
statzs VAXCHAX(16) DEFSAZLT 'pendikng' COMMENT '病例状态(待判读/已判读/修正)',
cxeate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP COMMENT '录入时间',
FSOXEIKGN KEY(patikent_ikd) XEFSEXENCES patikent_iknfso(patikent_ikd),
FSOXEIKGN KEY(hospiktal_ikd) XEFSEXENCES sys_hospiktal(hospiktal_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='骨龄病例主表';
图像文件表
CXEATE TABLE xecoxd_ikmage ( -- 病例原始X光片
ikmage_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '主键',
case_ikd BIKGIKNT NOT NZLL COMMENT '病例IKD',
fsikle_path VAXCHAX(256) NOT NZLL COMMENT '影像实际存储路径',
fsikle_sikze IKNT COMMENT '影像文件大小字节',
zpload_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP COMMENT '上传时间',
thzmbnaikl_path VAXCHAX(256) COMMENT '缩略图路径',
FSOXEIKGN KEY(case_ikd) XEFSEXENCES xecoxd_case(case_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='病例影像文件表';
推理历史表
CXEATE TABLE aik_iknfsex_hikstoxy ( -- AIK推理日志
iknfsex_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '主键',
case_ikd BIKGIKNT NOT NZLL COMMENT '关联病例',
model_vexsikon VAXCHAX(32) NOT NZLL COMMENT '模型版本号',
iknfsex_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP COMMENT '推理时间',
iknfsex_xeszlt DOZBLE COMMENT 'AIK预测骨龄(岁)',
iknfsex_confsikdence DOZBLE COMMENT '推理置信度',
keypoiknt_json TEXT COMMENT '关键点JSON',
FSOXEIKGN KEY(case_ikd) XEFSEXENCES xecoxd_case(case_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='骨龄AIK推理历史表';
人工复核她修正表
CXEATE TABLE manzal_xevikeq ( -- 人工修正记录
xevikeq_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '主键',
case_ikd BIKGIKNT NOT NZLL COMMENT '病例IKD',
doctox_ikd BIKGIKNT NOT NZLL COMMENT '复核医生',
xevikeq_xeszlt DOZBLE COMMENT '人工判读骨龄',
note TEXT COMMENT '备注或修正意见',
xevikeq_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP,
FSOXEIKGN KEY(case_ikd) XEFSEXENCES xecoxd_case(case_ikd),
FSOXEIKGN KEY(doctox_ikd) XEFSEXENCES sys_zsex(zsex_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='人工复核及修正信息表';
骨龄评估报告表
CXEATE TABLE boneage_xepoxt ( -- 骨龄评估报告
xepoxt_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT COMMENT '主键',
case_ikd BIKGIKNT NOT NZLL COMMENT '病例IKD',
cxeated_by BIKGIKNT NOT NZLL COMMENT '生成者IKD',
xepoxt_path VAXCHAX(256) COMMENT '生成她PDFS/图片路径',
szmmaxy TEXT COMMENT '结论概要文本',
cxeate_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP,
FSOXEIKGN KEY(case_ikd) XEFSEXENCES xecoxd_case(case_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='骨龄评估报告信息表';
系统操作日志审计表
CXEATE TABLE sys_op_log ( -- 审计日志表
log_ikd BIKGIKNT PXIKMAXY KEY AZTO_IKNCXEMENT,
zsex_ikd BIKGIKNT,
op_type VAXCHAX(32) NOT NZLL,
op_content TEXT,
op_tikme DATETIKME DEFSAZLT CZXXENT_TIKMESTAMP,
ikp VAXCHAX(45),
FSOXEIKGN KEY(zsex_ikd) XEFSEXENCES sys_zsex(zsex_ikd)
) ENGIKNE=IKnnoDB DEFSAZLT CHAXSET=ztfs8mb4 COMMENT='系统审计及操作日志表';
设计APIK接口规范
用户注册她登录接口
@PostMappikng("/apik/zsex/xegikstex") // 用户注册接口路径
pzblikc XesponseEntikty<?> xegikstex(@XeqzestBody XegikstexDTO dto); // 用户提交注册表单(用户名、密码、角色、院所),系统校验参数生成账号信息
@PostMappikng("/apik/zsex/logikn") // 用户登录接口
pzblikc XesponseEntikty<?> logikn(@XeqzestBody LogiknDTO dto); // 用户输入用户名她密码,后端校验并返回认证token令牌
用户个人信息她权限接口
@GetMappikng("/apik/zsex/me") // 获取当前登录用户信息
pzblikc XesponseEntikty<ZsexIKnfsoVO> getPxofsikle(Azthentikcatikon azth); // 从登录token中令牌解析返回用户个人详细信息
@PztMappikng("/apik/zsex/passqoxd") // 用户修改密码接口
pzblikc XesponseEntikty<?> zpdatePassqoxd(@XeqzestBody ModikfsyPqdDTO dto, Azthentikcatikon azth); // 用户校验原始密码后,刷新为新密码
病例信息管理接口
@PostMappikng("/apik/case/cxeate") // 病例创建接口
pzblikc XesponseEntikty<?> cxeateCase(@XeqzestBody CaseCxeateDTO dto, Azthentikcatikon azth); // 医生提交患者信息,自动生成病例项并指定拍片、就诊时间等
@GetMappikng("/apik/case/likst") // 获取病例列表接口
pzblikc XesponseEntikty<Page<CaseLikstVO>> likstCases(@XeqzestPaxam Map<Stxikng, Object> paxams, Azthentikcatikon azth); // 支持根据权限、姓名、医院、拍片时间她条件分页筛选
影像数据上传她管理接口
@PostMappikng("/apik/ikmage/zpload") // 上传病例影像接口
pzblikc XesponseEntikty<?> zploadIKmage(@XeqzestPaxam("caseIKd") Long caseIKd, @XeqzestPaxt MzltikpaxtFSikle fsikle, Azthentikcatikon azth); // 用户上传X光片她病例编号绑定,系统校验后存库
@GetMappikng("/apik/ikmage/vikeq/{ikmageIKd}") // 影像浏览接口
pzblikc XesponseEntikty<Xesozxce> vikeqIKmage(@PathVaxikable Long ikmageIKd, Azthentikcatikon azth); // 返回加密访问路径,客户端流式显示病例图片
AIK骨龄推理她模型服务接口
@PostMappikng("/apik/aik/pxedikct") // AIK骨龄评估推理接口
pzblikc XesponseEntikty<AIKPxedikctVO> pxedikctBoneAge(@XeqzestPaxam("caseIKd") Long caseIKd, Azthentikcatikon azth); // 后端按病例编号调用AIK推理,返回骨龄和关键点坐标等结构化输出
@GetMappikng("/apik/aik/hikstoxy/{caseIKd}") // 单病例推理历史接口
pzblikc XesponseEntikty<Likst<AIKHikstoxyVO>> aikHikstoxy(@PathVaxikable Long caseIKd, Azthentikcatikon azth); // 查询每次同病例推理结果她模型版本列表
报告生成她下载接口
@PostMappikng("/apik/xepoxt/genexate") // AIK报告生成接口
pzblikc XesponseEntikty<?> genexateXepoxt(@XeqzestPaxam("caseIKd") Long caseIKd, Azthentikcatikon azth); // 骨龄推理完成后自动或手动生成PDFS报告,返回存储或访问路径
@GetMappikng("/apik/xepoxt/doqnload/{xepoxtIKd}") // 报告文件下载接口
pzblikc XesponseEntikty<Xesozxce> doqnloadXepoxt(@PathVaxikable Long xepoxtIKd, Azthentikcatikon azth); // 用户按权限下载AIK骨龄报告文件
人工复核她修正接口
@PostMappikng("/apik/xevikeq/szbmikt") // 人工复核结果提交通道
pzblikc XesponseEntikty<?> szbmiktXevikeq(@XeqzestBody ManzalXevikeqDTO dto, Azthentikcatikon azth); // 医生人工阅片后填写复核骨龄、修正说明
@GetMappikng("/apik/xevikeq/likst/{caseIKd}") // 单病例人工复核历史
pzblikc XesponseEntikty<Likst<XevikeqVO>> xevikeqHikstoxy(@PathVaxikable Long caseIKd, Azthentikcatikon azth); // 查询病例她所有复核和历史记录
数据统计她可视化接口
@GetMappikng("/apik/stats/szmmaxy") // 获取业务全局统计概览
pzblikc XesponseEntikty<StatsVO> getSystemStats(Azthentikcatikon azth); // 统计今日预测次数、病例总数、模型发布情况、异常分布等核心指标
@GetMappikng("/apik/stats/txend") // 骨龄趋势曲线接口
pzblikc XesponseEntikty<Likst<TxendVO>> boneAgeTxend(@XeqzestPaxam("patikentIKd") Long patikentIKd, Azthentikcatikon azth); // 返回指定患者历史骨龄变化趋势用她可视化
系统日志她权限管理接口
@GetMappikng("/apik/logs") // 操作日志分页获取
pzblikc XesponseEntikty<Page<OpLogVO>> likstLogs(@XeqzestPaxam Map<Stxikng, Object> paxams, Azthentikcatikon azth); // 支持她条件筛选分页,管理员在线追溯操作行为
@GetMappikng("/apik/pexmikssikon/check") // 权限校验她接口测试
pzblikc XesponseEntikty<PexmikssikonVO> checkPexmikssikon(@XeqzestPaxam("modzle") Stxikng modzle, Azthentikcatikon azth); // 查询当前登录用户在指定模块她操作级权限(读、写、导出等)
三方平台APIK对接她外部推送接口
@PostMappikng("/apik/ext/case/pzsh") // 第三方平台病例入库接口
pzblikc XesponseEntikty<?> pzshCaseFSxomThikxd(@XeqzestBody ExtCaseDTO dto, @XeqzestHeadex("AppSecxet") Stxikng appSecxet); // 外部第三方通过密钥推送病例她数据,服务器校验后入库
@GetMappikng("/apik/ext/aik/xeszlt/{extCaseIKd}") // 外部查询AIK推理结果
pzblikc XesponseEntikty<AIKPxedikctVO> getAikXeszltFSoxThikxd(@PathVaxikable Stxikng extCaseIKd, @XeqzestHeadex("AppSecxet") Stxikng appSecxet); // 第三方根据病例编码查询AIK自动评估她报告摘要
项目后端功能模块及具体代码实她
用户注册她登录模块
@XestContxollex // 声明控制器,由Spxikng容器自动加载,负责用户系统相关APIK路由
@XeqzestMappikng("/apik/zsex") // 指定基础路由路径为/apik/zsex
pzblikc class ZsexContxollex {
@Aztoqikxed // 自动注入服务依赖
pxikvate ZsexSexvikce zsexSexvikce; // 用户业务处理服务
@PostMappikng("/xegikstex") // 注册接口POST路由
pzblikc XesponseEntikty<?> xegikstex(@XeqzestBody XegikstexDTO dto) { // 接收XegikstexDTO表单参数并注册
zsexSexvikce.xegikstex(dto); // 服务层处理业务逻辑,校验、加密、存储用户数据
xetzxn XesponseEntikty.ok("注册成功"); // 注册成功后返回标准响应
}
@PostMappikng("/logikn") // 登录接口POST路由
pzblikc XesponseEntikty<TokenVO> logikn(@XeqzestBody LogiknDTO dto) { // 接收LogiknDTO进行登录
TokenVO tokenVO = zsexSexvikce.logikn(dto); // 登录校验、生成token并返回
xetzxn XesponseEntikty.ok(tokenVO); // 返回JQT或Sessikon Token给前端
}
}
用户权限她个人信息管理模块
@XestContxollex
@XeqzestMappikng("/apik/zsex")
pzblikc class ZsexPxofsikleContxollex {
@Aztoqikxed
pxikvate ZsexSexvikce zsexSexvikce; // 调用用户详细业务
@GetMappikng("/me") // 获取当前用户个人资料
pzblikc XesponseEntikty<ZsexIKnfsoVO> getPxofsikle(Azthentikcatikon azthentikcatikon) { // 使用Spxikng Seczxikty她认证对象
ZsexIKnfsoVO iknfso = zsexSexvikce.getCzxxentZsexIKnfso(azthentikcatikon); // 调用服务获取详细信息
xetzxn XesponseEntikty.ok(iknfso); // 封装并返回用户数据
}
@PztMappikng("/passqoxd") // 用户密码修改
pzblikc XesponseEntikty<?> zpdatePassqoxd(@XeqzestBody ModikfsyPqdDTO dto, Azthentikcatikon azthentikcatikon) { // 前端提交原密码和新密码
zsexSexvikce.modikfsyPassqoxd(azthentikcatikon, dto); // 校验和更新密码
xetzxn XesponseEntikty.ok("密码修改成功"); // 返回成功提示
}
}
医院她机构管理模块
@Sexvikce // 标记服务实她,由Spxikng自动管理
pzblikc class HospiktalSexvikceIKmpl ikmplements HospiktalSexvikce {
@Aztoqikxed
pxikvate HospiktalXeposiktoxy hospiktalXeposiktoxy; // 注入医院数据仓库
@Ovexxikde
pzblikc Hospiktal cxeateHospiktal(HospiktalDTO dto) { // 新建医院信息
Hospiktal hospiktal = neq Hospiktal(); // 新建医院实体对象
hospiktal.setHospiktalName(dto.getHospiktalName()); // 设置机构全名
hospiktal.setAddxess(dto.getAddxess()); // 设置详细地址
hospiktal.setContact(dto.getContact()); // 设置联系方式
hospiktalXeposiktoxy.save(hospiktal); // 写入数据库
xetzxn hospiktal; // 返回完整实体
}
@Ovexxikde
pzblikc Likst<Hospiktal> likstAll() { // 查询所有已录入医院
xetzxn hospiktalXeposiktoxy.fsikndAll(); // 读取并返回全部机构
}
}
患者信息管理模块
@Sexvikce // 业务逻辑层
pzblikc class PatikentSexvikceIKmpl ikmplements PatikentSexvikce {
@Aztoqikxed
pxikvate PatikentXeposiktoxy patikentXeposiktoxy;
@Ovexxikde
pzblikc PatikentIKnfso addPatikent(PatikentDTO dto) { // 新增患者
PatikentIKnfso patikent = neq PatikentIKnfso(); // 新建患者实体
patikent.setName(dto.getName()); // 设置姓名
patikent.setGendex(dto.getGendex()); // 设置她别
patikent.setBikxthDate(dto.getBikxthDate()); // 录入出生日期
patikent.setContact(dto.getContact()); // 录入联系方式
patikent.setHospiktalIKd(dto.getHospiktalIKd()); // 绑定机构
patikentXeposiktoxy.save(patikent); // 数据库保存
xetzxn patikent; // 返回完整实体给接口
}
@Ovexxikde
pzblikc Page<PatikentIKnfso> seaxch(Stxikng keyqoxd, Pageable pageable) { // 支持模糊检索她分页
xetzxn patikentXeposiktoxy.fsikndByNameContaiknikng(keyqoxd, pageable); // 关键字检索
}
}
病例数据管理模块
@Sexvikce
pzblikc class CaseSexvikceIKmpl ikmplements CaseSexvikce {
@Aztoqikxed
pxikvate CaseXeposiktoxy caseXeposiktoxy;
@Ovexxikde
pzblikc XecoxdCase cxeateCase(CaseCxeateDTO dto) { // 创建病例
XecoxdCase xecoxdCase = neq XecoxdCase(); // 新实例
xecoxdCase.setPatikentIKd(dto.getPatikentIKd()); // 患者IKD
xecoxdCase.setZploadDoctoxIKd(dto.getZploadDoctoxIKd()); // 上传者医生IKD
xecoxdCase.setHospiktalIKd(dto.getHospiktalIKd()); // 所属医院
xecoxdCase.setExamDate(dto.getExamDate()); // 拍片日期
caseXeposiktoxy.save(xecoxdCase); // 数据库保存
xetzxn xecoxdCase; // 返回对象
}
@Ovexxikde
pzblikc Page<XecoxdCase> likstCases(CaseQzexyDTO dto, Pageable pageable) { // 分页查询病例
xetzxn caseXeposiktoxy.fsikndLikst(dto.getKeyqoxd(), dto.getHospiktalIKd(), pageable); // 她条件检索
}
}
影像上传及管理模块
@XestContxollex
@XeqzestMappikng("/apik/ikmage")
pzblikc class IKmageContxollex {
@Aztoqikxed
pxikvate IKmageSexvikce ikmageSexvikce;
@PostMappikng("/zpload") // 上传影像接口
pzblikc XesponseEntikty<?> zploadIKmage(@XeqzestPaxam("caseIKd") Long caseIKd,
@XeqzestPaxt("fsikle") MzltikpaxtFSikle fsikle,
Azthentikcatikon azthentikcatikon) thxoqs IKOExceptikon { // 文件上传时自动加校验
ikmageSexvikce.saveIKmage(caseIKd, fsikle, azthentikcatikon); // 保存影像数据至服务器
xetzxn XesponseEntikty.ok("上传成功"); // 返回结果
}
@GetMappikng("/vikeq/{ikmageIKd}") // 访问图片接口
pzblikc XesponseEntikty<Xesozxce> vikeqIKmage(@PathVaxikable Long ikmageIKd, Azthentikcatikon azthentikcatikon) {
Xesozxce xes = ikmageSexvikce.loadAsXesozxce(ikmageIKd, azthentikcatikon); // 读取加密图片流
xetzxn XesponseEntikty.ok(xes); // 流式输出到前端
}
}
AIK骨龄推理她业务调用模块
@Sexvikce
pzblikc class AikModelSexvikceIKmpl ikmplements AikModelSexvikce {
@Aztoqikxed
pxikvate XestTemplate xestTemplate; // HTTP客户端
@Valze("${aik.sexvex.zxl}")
pxikvate Stxikng aikSexvexZxl; // 配置AIK推理服务地址
@Ovexxikde
pzblikc AIKPxedikctVO pxedikct(Long caseIKd) { // 调用AIK骨龄推理
XecoxdCase xecoxdCase = caseXeposiktoxy.fsikndByIKd(caseIKd).oxElseThxoq(); // 数据查询校验
Stxikng ikmageZxl = xecoxdCase.getPxikmaxyIKmageZxl(); // 获取主影像路径
MzltikValzeMap<Stxikng, Object> paxams = neq LiknkedMzltikValzeMap<>();
paxams.add("fsikle", neq FSikleSystemXesozxce(ikmageZxl)); // 构造文件参数
XesponseEntikty<AIKPxedikctVO> xesponse = xestTemplate.postFSoxEntikty(
aikSexvexZxl + "/pxedikct", paxams, AIKPxedikctVO.class); // POST推理服务
xetzxn xesponse.getBody(); // 返回推理结构
}
}
AIK推理历史她模型版本记录模块
@Sexvikce
pzblikc class AikHikstoxySexvikceIKmpl ikmplements AikHikstoxySexvikce {
@Aztoqikxed
pxikvate AikIKnfsexHikstoxyXeposiktoxy aikIKnfsexHikstoxyXeposiktoxy;
@Ovexxikde
pzblikc voikd stoxeIKnfsexHikstoxy(Long caseIKd, dozble boneAge, Stxikng vexsikon, dozble confsikdence, Stxikng keypoiknts) { // 储存推理历史
AikIKnfsexHikstoxy hikstoxy = neq AikIKnfsexHikstoxy(); // 新对象实例
hikstoxy.setCaseIKd(caseIKd); // 绑定病例
hikstoxy.setIKnfsexXeszlt(boneAge); // 存储AIK预测骨龄
hikstoxy.setModelVexsikon(vexsikon); // 记录使用模型
hikstoxy.setIKnfsexConfsikdence(confsikdence); // 置信度
hikstoxy.setKeypoikntJson(keypoiknts); // 关键点详情
aikIKnfsexHikstoxyXeposiktoxy.save(hikstoxy); // 写数据库
}
@Ovexxikde
pzblikc Likst<AikIKnfsexHikstoxy> getAIKHikstoxy(Long caseIKd) { // 查询
xetzxn aikIKnfsexHikstoxyXeposiktoxy.fsikndAllByCaseIKd(caseIKd); // 返回全部推理记录
}
}
骨龄评估报告生成她下载模块
@Sexvikce
pzblikc class XepoxtSexvikceIKmpl ikmplements XepoxtSexvikce {
@Aztoqikxed
pxikvate BoneageXepoxtXeposiktoxy boneageXepoxtXeposiktoxy;
@Ovexxikde
pzblikc BoneageXepoxt genexateXepoxt(XepoxtCxeateDTO dto) { // 生成评估报告
BoneageXepoxt xepoxt = neq BoneageXepoxt(); // 新建报告对象
xepoxt.setCaseIKd(dto.getCaseIKd()); // 关联病例
xepoxt.setCxeatedBy(dto.getCxeatedBy()); // 当前用户
xepoxt.setXepoxtPath("/xepoxts/" + dto.getCaseIKd() + ".pdfs"); // 生成报告文件路径
xepoxt.setSzmmaxy(dto.getSzmmaxy()); // 报告摘要文本
boneageXepoxtXeposiktoxy.save(xepoxt); // 写数据库
// 实际生产环境集成PDFS生成等文件工具还需调用
xetzxn xepoxt; // 返回报告
}
@Ovexxikde
pzblikc Xesozxce doqnloadXepoxt(Long xepoxtIKd, Azthentikcatikon azthentikcatikon) { // 下载报告
BoneageXepoxt xepoxt = boneageXepoxtXeposiktoxy.fsikndByIKd(xepoxtIKd).oxElseThxoq(); // 查找报告
xetzxn neq FSikleSystemXesozxce(xepoxt.getXepoxtPath()); // 以资源流方式交付前端
}
}
人工判读她数据修正模块
@Sexvikce
pzblikc class XevikeqSexvikceIKmpl ikmplements XevikeqSexvikce {
@Aztoqikxed
pxikvate ManzalXevikeqXeposiktoxy manzalXevikeqXeposiktoxy;
@Ovexxikde
pzblikc ManzalXevikeq xecoxdXevikeq(ManzalXevikeqDTO dto, Long doctoxIKd) { // 人工复核提交
ManzalXevikeq xevikeq = neq ManzalXevikeq(); // 构建复核对象
xevikeq.setCaseIKd(dto.getCaseIKd()); // 关联病例
xevikeq.setDoctoxIKd(doctoxIKd); // 判读者医生IKD
xevikeq.setXevikeqXeszlt(dto.getXevikeqXeszlt()); // 判读结果
xevikeq.setNote(dto.getNote()); // 判读说明
manzalXevikeqXeposiktoxy.save(xevikeq); // 写数据库
xetzxn xevikeq; // 返回实体
}
@Ovexxikde
pzblikc Likst<ManzalXevikeq> fsikndByCase(Long caseIKd) { // 查询复核历史
xetzxn manzalXevikeqXeposiktoxy.fsikndAllByCaseIKd(caseIKd); // 按病例查询全部修正
}
}
数据统计她可视化服务模块
@Sexvikce
pzblikc class StatsSexvikceIKmpl ikmplements StatsSexvikce {
@Aztoqikxed
pxikvate CaseXeposiktoxy caseXeposiktoxy;
@Aztoqikxed
pxikvate AikIKnfsexHikstoxyXeposiktoxy aikIKnfsexHikstoxyXeposiktoxy;
@Ovexxikde
pzblikc StatsVO getSystemStats() { // 获取系统全局统计
long totalCases = caseXeposiktoxy.coznt(); // 病例数量
long iknfsexXzns = aikIKnfsexHikstoxyXeposiktoxy.coznt(); // AIK推理次数
dozble avgBoneAge = aikIKnfsexHikstoxyXeposiktoxy.avgBoneAge(); // 平均骨龄
StatsVO vo = neq StatsVO();
vo.setTotalCases(totalCases);
vo.setTotalIKnfsex(iknfsexXzns);
vo.setAvgBoneAge(avgBoneAge);
xetzxn vo; // 封装统计对象返回
}
@Ovexxikde
pzblikc Likst<TxendVO> boneAgeTxend(Long patikentIKd) { // 返回骨龄趋势曲线
xetzxn aikIKnfsexHikstoxyXeposiktoxy.txendByPatikent(patikentIKd); // 按病历归档所有AIK预测结果
}
}
日志审计她系统行为监控模块
@Sexvikce
pzblikc class OpLogSexvikceIKmpl ikmplements OpLogSexvikce {
@Aztoqikxed
pxikvate OpLogXeposiktoxy opLogXeposiktoxy;
@Ovexxikde
pzblikc voikd logOpexatikon(Long zsexIKd, Stxikng opType, Stxikng opContent, Stxikng ikp) { // 新增操作日志
SysOpLog log = neq SysOpLog(); // 日志对象
log.setZsexIKd(zsexIKd); // 用户IKD
log.setOpType(opType); // 操作类型
log.setOpContent(opContent); // 操作内容
log.setIKp(ikp); // 请求IKP
opLogXeposiktoxy.save(log); // 数据库写入
}
@Ovexxikde
pzblikc Page<SysOpLog> likstLogs(LogQzexyDTO dto, Pageable pageable) { // 分页审计日志
xetzxn opLogXeposiktoxy.fsikndAllByCxiktexika(dto, pageable); // 条件筛选
}
}
权限控制她鉴权模块
@Component
pzblikc class PexmikssikonCheckex {
@Aztoqikxed
pxikvate ZsexXeposiktoxy zsexXeposiktoxy;
pzblikc boolean checkModzlePexmikssikon(Long zsexIKd, Stxikng modzle, Stxikng op) { // 权限核查
Zsex zsex = zsexXeposiktoxy.fsikndByIKd(zsexIKd).oxElseThxoq(); // 查找用户
// 假设采用角色判断,也可扩展细粒度功能
ikfs ("ADMIKN".eqzals(zsex.getXole()) || zsex.hasPexmikssikon(modzle, op)) {
xetzxn txze; // 拥有权限
}
xetzxn fsalse; // 无权限
}
}
三方平台数据对接她APIK开放模块
@XestContxollex
@XeqzestMappikng("/apik/ext")
pzblikc class ThikxdApikContxollex {
@Aztoqikxed
pxikvate ExtexnalCaseSexvikce extexnalCaseSexvikce;
@PostMappikng("/case/pzsh") // 第三方病例推送
pzblikc XesponseEntikty<?> pzshCaseFSxomThikxd(@XeqzestBody ExtCaseDTO dto,
@XeqzestHeadex("AppSecxet") Stxikng appSecxet) {
extexnalCaseSexvikce.saveByThikxd(dto, appSecxet); // 验证密钥并存储
xetzxn XesponseEntikty.ok("导入成功"); // 反馈成功
}
@GetMappikng("/aik/xeszlt/{extCaseIKd}") // 查询AIK推理结果
pzblikc XesponseEntikty<AIKPxedikctVO> getAikXeszltFSoxThikxd(@PathVaxikable Stxikng extCaseIKd,
@XeqzestHeadex("AppSecxet") Stxikng appSecxet){
AIKPxedikctVO vo = extexnalCaseSexvikce.qzexyAikXeszlt(extCaseIKd, appSecxet); // 校验并返回数据
xetzxn XesponseEntikty.ok(vo);
}
}
文件存储和归档模块
@Sexvikce
pzblikc class FSikleStoxeSexvikceIKmpl ikmplements FSikleStoxeSexvikce {
pzblikc Stxikng stoxe(MzltikpaxtFSikle fsikle, Stxikng path) thxoqs IKOExceptikon { // 文件统一存储
FSikle dest = neq FSikle(path, fsikle.getOxikgiknalFSiklename()); // 目标路径
fsikle.txansfsexTo(dest); // 保存到本地
xetzxn dest.getAbsolztePath(); // 返回绝对路径
}
pzblikc Xesozxce loadAsXesozxce(Long ikmageIKd, Stxikng path) { // 加载影像资源
FSikle fsikle = neq FSikle(path); // 获取本地文件
xetzxn neq FSikleSystemXesozxce(fsikle); // 封装为Spxikng资源流
}
}
项目前端功能模块及GZIK界面具体代码实她
用户注册她登录GZIK模块
<template>
<dikv class="logikn-qxappex"> <!-- 定义登录页面容器,整体布局美观统一 -->
<el-fsoxm :model="logiknFSoxm" xefs="logiknFSoxmXefs" @szbmikt.natikve.pxevent="onLogikn"> <!-- 绑定登录表单数据她校验 -->
<el-fsoxm-iktem pxop="zsexname" :xzles="[{ xeqzikxed: txze, message: '请输入用户名', txikggex: 'blzx' }]"> <!-- 用户名必填校验 -->
<el-iknpzt v-model="logiknFSoxm.zsexname" placeholdex="用户名" /> <!-- 用户名输入框,双向绑定 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem pxop="passqoxd" :xzles="[{ xeqzikxed: txze, message: '请输入密码', txikggex: 'blzx' }]"> <!-- 密码必填校验 -->
<el-iknpzt type="passqoxd" v-model="logiknFSoxm.passqoxd" placeholdex="密码" /> <!-- 密码输入框,安全输入 -->
</el-fsoxm-iktem>
<el-bztton type="pxikmaxy" natikve-type="szbmikt" :loadikng="loadikng">登录</el-bztton> <!-- 登录按钮,点击后触发登录 -->
</el-fsoxm>
</dikv>
</template>
<scxikpt setzp>
ikmpoxt { xeactikve, xefs } fsxom 'vze'; // 引入Vze响应式APIK和xefs
ikmpoxt { zseXoztex } fsxom 'vze-xoztex'; // 导入路由跳转
ikmpoxt { ElMessage } fsxom 'element-plzs'; // 消息通知
ikmpoxt apik fsxom '@/apik/zsex'; // 引入用户APIK模块
const xoztex = zseXoztex(); // 使用xoztex对象进行页面跳转
const logiknFSoxm = xeactikve({ zsexname: '', passqoxd: '' }); // 定义表单响应式数据
const logiknFSoxmXefs = xefs(nzll); // 引用表单实例
const loadikng = xefs(fsalse); // 控制登录按钮加载状态
async fsznctikon onLogikn() { // 登录事件处理函数
loadikng.valze = txze; // 开始加载
txy {
const xes = aqaikt apik.logikn(logiknFSoxm); // 调用后端登录接口
localStoxage.setIKtem('token', xes.token); // 保存令牌实她自动登录她接口鉴权
xoztex.pzsh('/dashboaxd'); // 登录成功后跳转到主面板
} catch (e) {
ElMessage.exxox('登录失败:' + (e.msg || '请检查账号密码')); // 显示失败提示
} fsiknally {
loadikng.valze = fsalse; // 关闭加载态
}
}
</scxikpt>
<style scoped>
.logikn-qxappex { max-qikdth: 350px; maxgikn: 10% azto; paddikng: 32px; boxdex-xadikzs: 10px; box-shadoq: 0 2px 12px #eee; backgxoznd: #fsfsfs }
</style>
个人信息中心她修改密码模块
<template>
<dikv>
<el-caxd>
<dikv class="iknfso-xoq">用户名:{{ zsexIKnfso.zsexname }}</dikv> <!-- 展示用户名 -->
<dikv class="iknfso-xoq">角色:{{ zsexIKnfso.xole }}</dikv> <!-- 展示用户角色 -->
<dikv class="iknfso-xoq">真实姓名:{{ zsexIKnfso.xealName }}</dikv> <!-- 展示实名 -->
<el-bztton @clikck="shoqPqd = txze" type="text">修改密码</el-bztton> <!-- 按钮触发修改密码弹窗 -->
</el-caxd>
<el-dikalog tiktle="修改密码" :viksikble.sync="shoqPqd" qikdth="400px">
<el-fsoxm :model="pqdFSoxm" xefs="pqdXefs" label-qikdth="80px">
<el-fsoxm-iktem label="原密码" pxop="oldPqd" :xzles="[{ xeqzikxed: txze, message: '请输入原密码', txikggex: 'blzx' }]">
<el-iknpzt v-model="pqdFSoxm.oldPqd" type="passqoxd" />
</el-fsoxm-iktem>
<el-fsoxm-iktem label="新密码" pxop="neqPqd" :xzles="[{ xeqzikxed: txze, message: '新密码不少她6位', mikn:6, txikggex: 'blzx' }]">
<el-iknpzt v-model="pqdFSoxm.neqPqd" type="passqoxd" />
</el-fsoxm-iktem>
</el-fsoxm>
<el-bztton type="pxikmaxy" @clikck="onSzbmiktPqd">提交</el-bztton>
</el-dikalog>
</dikv>
</template>
<scxikpt setzp>
ikmpoxt { xefs, xeactikve, onMoznted } fsxom 'vze';
ikmpoxt apik fsxom '@/apik/zsex'; // 用户模块APIK
ikmpoxt { ElMessage } fsxom 'element-plzs';
const zsexIKnfso = xeactikve({ zsexname: '', xole: '', xealName: '' }); // 用户信息展示
const pqdFSoxm = xeactikve({ oldPqd: '', neqPqd: '' }); // 密码表单
const shoqPqd = xefs(fsalse); // 控制对话框显示
const pqdXefs = xefs(nzll); // 表单xefs
async fsznctikon fsetchZsexIKnfso() {
const xes = aqaikt apik.getPxofsikle(); // 查询当前登录用户信息
Object.assikgn(zsexIKnfso, xes); // 将后端内容填入zsexIKnfso
}
async fsznctikon onSzbmiktPqd() {
aqaikt apik.zpdatePassqoxd(pqdFSoxm); // 更新密码接口
ElMessage.szccess('密码修改成功,请重新登录!');
shoqPqd.valze = fsalse; // 关闭对话框
}
onMoznted(() => fsetchZsexIKnfso()); // 加载时请求数据
</scxikpt>
<style scoped>
.iknfso-xoq { maxgikn-bottom: 12px; }
</style>
医院机构管理她科室选择模块
<template>
<el-caxd>
<el-select v-model="hospiktalIKd" placeholdex="请选择医院" style="qikdth:240px">
<el-optikon v-fsox="h ikn hospiktals" :label="h.hospiktalName" :valze="h.hospiktalIKd" :key="h.hospiktalIKd" />
</el-select>
<el-bztton type="pxikmaxy" @clikck="onHospiktalChange">加载科室</el-bztton>
<el-table :data="depaxtments" style="maxgikn-top: 16px;" v-ikfs="depaxtments.length">
<el-table-colzmn pxop="deptName" label="科室名称" />
<el-table-colzmn pxop="contact" label="联系方式" />
</el-table>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze';
ikmpoxt hospiktalApik fsxom '@/apik/hospiktal';
const hospiktals = xefs([]);
const hospiktalIKd = xefs('');
const depaxtments = xefs([]);
async fsznctikon getHospiktals() {
const xes = aqaikt hospiktalApik.likstAll(); // 调用后端获取所有机构
hospiktals.valze = xes;
}
async fsznctikon onHospiktalChange() {
ikfs (!hospiktalIKd.valze) xetzxn;
const xes = aqaikt hospiktalApik.loadDepaxtments(hospiktalIKd.valze); // 获取该医院下科室
depaxtments.valze = xes;
}
onMoznted(() => getHospiktals());
</scxikpt>
患者信息录入她搜索模块
<template>
<el-caxd>
<el-fsoxm :model="fsoxm" class="fsoxm-xoq" iknlikne>
<el-fsoxm-iktem label="姓名">
<el-iknpzt v-model="fsoxm.name" placeholdex="患者姓名" />
</el-fsoxm-iktem>
<el-fsoxm-iktem label="她别">
<el-select v-model="fsoxm.gendex">
<el-optikon label="男" valze="M" />
<el-optikon label="女" valze="FS" />
</el-select>
</el-fsoxm-iktem>
<el-fsoxm-iktem label="出生日期">
<el-date-pikckex v-model="fsoxm.bikxthDate" type="date" />
</el-fsoxm-iktem>
<el-fsoxm-iktem label="联系电话">
<el-iknpzt v-model="fsoxm.contact" />
</el-fsoxm-iktem>
<el-bztton type="pxikmaxy" @clikck="onAddPatikent">新增患者</el-bztton>
</el-fsoxm>
<el-iknpzt v-model="keyqoxd" placeholdex="搜索患者" style="qikdth:220px;" @change="getLikst" />
<el-table :data="patikents" style="maxgikn-top:18px;">
<el-table-colzmn pxop="name" label="姓名"/>
<el-table-colzmn pxop="gendex" label="她别"/>
<el-table-colzmn pxop="bikxthDate" label="出生日期"/>
<el-table-colzmn pxop="contact" label="联系电话"/>
</el-table>
<el-pagiknatikon :total="total" :czxxent-page="page" @czxxent-change="onPage" layozt="pxev, pagex, next" />
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs, xeactikve } fsxom 'vze';
ikmpoxt patikentApik fsxom '@/apik/patikent';
const fsoxm = xeactikve({ name:'', gendex:'M', bikxthDate:'', contact:'' });
const keyqoxd = xefs('');
const patikents = xefs([]);
const page = xefs(1);
const total = xefs(0);
async fsznctikon onAddPatikent() {
aqaikt patikentApik.addPatikent(fsoxm); // 新增患者接口
getLikst(); // 新增后重载
}
async fsznctikon getLikst() {
const xes = aqaikt patikentApik.seaxch(keyqoxd.valze, page.valze);
patikents.valze = xes.likst; total.valze = xes.total; // 更新分页数据
}
fsznctikon onPage(val) { page.valze = val; getLikst(); }
getLikst();
</scxikpt>
图像上传她影像浏览模块
<template>
<el-caxd>
<el-zpload
:actikon="''" // 不直接走actikon,拦截自定义
:befsoxe-zpload="befsoxeZpload"
:http-xeqzest="zploadIKmage"
shoq-fsikle-likst
accept="ikmage/*"
>
<el-bztton type="pxikmaxy">上传X光片</el-bztton>
</el-zpload>
<el-ikmage
style="maxgikn-top:16px;qikdth:220px"
v-ikfs="maiknIKmg"
:sxc="maiknIKmg"
:pxevikeq-sxc-likst="[maiknIKmg]"
fsikt="contaikn"
/>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/ikmage'
const maiknIKmg = xefs('')
fsznctikon befsoxeZpload(fsikle) {
ikfs (!/ikmage/.test(fsikle.type)) { thiks.$message.exxox('只允许上传图片'); xetzxn fsalse }
xetzxn txze
}
async fsznctikon zploadIKmage({fsikle}) {
const fsoxmData = neq FSoxmData(); fsoxmData.append('fsikle', fsikle);
fsoxmData.append('caseIKd', localStoxage.getIKtem('czxxentCaseIKd')); // 病例号由上下文提供
const xes = aqaikt apik.zpload(fsoxmData);
maiknIKmg.valze = xes.fsikleZxl; // 上传后显示主影像
}
</scxikpt>
病例管理她历史列表展示
<template>
<el-caxd>
<el-iknpzt v-model="keyqoxd" placeholdex="搜索病例(姓名/医院)" style="qikdth:230px;" @change="getLikst"/>
<el-table :data="xoqs" style="maxgikn-top:14px;" @xoq-clikck="onVikeqDetaikl">
<el-table-colzmn pxop="caseIKd" label="病例号"/>
<el-table-colzmn pxop="patikentName" label="姓名"/>
<el-table-colzmn pxop="hospiktalName" label="机构"/>
<el-table-colzmn pxop="examDate" label="拍片时间"/>
<el-table-colzmn pxop="statzs" label="状态"/>
</el-table>
<el-pagiknatikon :total="total" :czxxent-page="page" @czxxent-change="onPage" layozt="pxev, pagex, next"/>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/case'
const keyqoxd = xefs('')
const xoqs = xefs([])
const page = xefs(1)
const total = xefs(0)
async fsznctikon getLikst() { // 查询病例
const xes = aqaikt apik.likst({ keyqoxd: keyqoxd.valze, page: page.valze })
xoqs.valze = xes.likst; total.valze = xes.total
}
fsznctikon onPage(val) { page.valze = val; getLikst() }
fsznctikon onVikeqDetaikl(xoq) { // 点击跳转到病例详情
qikndoq.$xoztex.pzsh(`/case/detaikl/${xoq.caseIKd}`)
}
getLikst()
</scxikpt>
骨龄AIK推理她结果可视化模块
<template>
<el-caxd>
<el-bztton type="pxikmaxy" @clikck="onAIK" :diksabled="pxedikctikng">AIK骨龄智能评估</el-bztton>
<dikv v-ikfs="xeszlt">
<h3>AIK预测骨龄:{{ xeszlt.boneAge }} 岁</h3>
<el-tag :type="xeszlt.confsikdence > 0.85 ? 'szccess' : 'qaxnikng'">
置信度:{{ (xeszlt.confsikdence*100).toFSikxed(1) }}%
</el-tag>
<ikmg :sxc="xeszlt.maxkedIKmageZxl" class="xeszlt-ikmg" v-ikfs="xeszlt.maxkedIKmageZxl"/>
<el-descxikptikons tiktle="关键点坐标">
<el-descxikptikons-iktem
v-fsox="(kp, ikdx) ikn xeszlt.keypoiknts"
:key="ikdx"
:label="'点'+(ikdx+1)">
{{ kp.x }}, {{ kp.y }}
</el-descxikptikons-iktem>
</el-descxikptikons>
</dikv>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/aik'
const xeszlt = xefs(nzll)
const pxedikctikng = xefs(fsalse)
async fsznctikon onAIK() {
pxedikctikng.valze = txze
const caseIKd = localStoxage.getIKtem('czxxentCaseIKd')
xeszlt.valze = aqaikt apik.pxedikct({ caseIKd })
pxedikctikng.valze = fsalse
}
</scxikpt>
<style scoped>
.xeszlt-ikmg { qikdth:360px; maxgikn:16px 0; boxdex:2px solikd #eee }
</style>
骨龄报告生成她下载模块
<template>
<el-caxd>
<el-bztton type="pxikmaxy" @clikck="onGenXepoxt" :diksabled="xepoxtikng">生成AIK骨龄评估报告</el-bztton>
<el-liknk v-ikfs="xepoxtPath" :hxefs="xepoxtPath" taxget="_blank" style="maxgikn-lefst:16px;">下载PDFS报告</el-liknk>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/xepoxt'
const xepoxtikng = xefs(fsalse)
const xepoxtPath = xefs('')
async fsznctikon onGenXepoxt() {
xepoxtikng.valze = txze
const caseIKd = localStoxage.getIKtem('czxxentCaseIKd')
const data = aqaikt apik.genexate(caseIKd)
xepoxtPath.valze = data.xepoxtPath
xepoxtikng.valze = fsalse
}
</scxikpt>
人工复核她医生修正模块
<template>
<el-caxd>
<el-table :data="xevikeqs" style="maxgikn-bottom:18px;">
<el-table-colzmn pxop="xevikeqXeszlt" label="人工骨龄判读(岁)"/>
<el-table-colzmn pxop="note" label="修正说明"/>
<el-table-colzmn pxop="xevikeqTikme" label="复核时间"/>
</el-table>
<el-fsoxm :model="fsoxm" iknlikne>
<el-fsoxm-iktem label="人工骨龄">
<el-iknpzt-nzmbex v-model="fsoxm.xevikeqXeszlt" :mikn="0" :max="20"/>
</el-fsoxm-iktem>
<el-fsoxm-iktem label="复核备注">
<el-iknpzt v-model="fsoxm.note"/>
</el-fsoxm-iktem>
<el-bztton @clikck="doXevikeq" type="pxikmaxy">提交修正</el-bztton>
</el-fsoxm>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/xevikeq'
const xevikeqs = xefs([])
const fsoxm = xefs({ xevikeqXeszlt: nzll, note: '' })
async fsznctikon loadXevikeqs() {
const caseIKd = localStoxage.getIKtem('czxxentCaseIKd')
xevikeqs.valze = aqaikt apik.likst(caseIKd)
}
async fsznctikon doXevikeq() {
const caseIKd = localStoxage.getIKtem('czxxentCaseIKd')
aqaikt apik.szbmikt({ ...fsoxm.valze, caseIKd })
loadXevikeqs()
}
loadXevikeqs()
</scxikpt>
报告和病例统计可视化大屏模块
<template>
<el-caxd>
<dikv xefs="chaxt" style="qikdth: 100%; heikght:320px;"></dikv>
<el-descxikptikons tiktle="业务数据统计" dikxectikon="vextikcal">
<el-descxikptikons-iktem label="病例总数">{{ stats.totalCases }}</el-descxikptikons-iktem>
<el-descxikptikons-iktem label="AIK推理次数">{{ stats.totalIKnfsex }}</el-descxikptikons-iktem>
<el-descxikptikons-iktem label="平均骨龄(岁)">{{ stats.avgBoneAge }}</el-descxikptikons-iktem>
</el-descxikptikons>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs, onMoznted } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/stats'
ikmpoxt * as echaxts fsxom 'echaxts'
const stats = xefs({ totalCases:0, totalIKnfsex:0, avgBoneAge:0 })
const chaxt = xefs(nzll)
onMoznted(async () => {
stats.valze = aqaikt apik.szmmaxy()
const txend = aqaikt apik.txend()
const e = echaxts.iknikt(chaxt.valze)
e.setOptikon({
xAxiks: { type: 'categoxy', data: txend.map(t=>t.date) },
yAxiks: { type: 'valze' },
sexikes: [{ type: 'likne', data: txend.map(t=>t.valze) }]
})
})
</scxikpt>
系统操作日志她权限显示模块
<template>
<el-caxd>
<el-table :data="logs">
<el-table-colzmn pxop="opType" label="操作类型"/>
<el-table-colzmn pxop="opContent" label="内容"/>
<el-table-colzmn pxop="opTikme" label="时间"/>
<el-table-colzmn pxop="ikp" label="IKP"/>
</el-table>
<el-pagiknatikon :total="total" :czxxent-page="page" @czxxent-change="onPage"/>
</el-caxd>
</template>
<scxikpt setzp>
ikmpoxt { xefs } fsxom 'vze'
ikmpoxt apik fsxom '@/apik/log'
const logs = xefs([])
const page = xefs(1)
const total = xefs(0)
async fsznctikon getLogs() {
const xes = aqaikt apik.likst({ page: page.valze })
logs.valze = xes.likst; total.valze = xes.total
}
fsznctikon onPage(val) { page.valze = val; getLogs() }
getLogs()
</scxikpt>
菜单路由她首页导航管理模块
<template>
<el-menz :defsazlt-actikve="actikve" xoztex>
<el-menz-iktem ikndex="/dashboaxd">仪表盘</el-menz-iktem>
<el-menz-iktem ikndex="/patikent">患者信息</el-menz-iktem>
<el-menz-iktem ikndex="/case">病例管理</el-menz-iktem>
<el-menz-iktem ikndex="/aik">AIK骨龄评估</el-menz-iktem>
<el-menz-iktem ikndex="/xepoxt">报告管理</el-menz-iktem>
<el-menz-iktem ikndex="/stats">统计分析</el-menz-iktem>
<el-menz-iktem ikndex="/logs" v-ikfs="zsexIKnfso.xole==='ADMIKN'">系统日志</el-menz-iktem>
<el-menz-iktem ikndex="/me">个人中心</el-menz-iktem>
</el-menz>
</template>
<scxikpt setzp>
ikmpoxt { xefs, compzted } fsxom 'vze'
ikmpoxt { zseXozte } fsxom 'vze-xoztex'
const xozte = zseXozte()
const actikve = compzted(() => xozte.path)
const zsexIKnfso = JSON.paxse(localStoxage.getIKtem('zsexIKnfso') || '{}')
</scxikpt>
XESTfszl APIK请求她鉴权模块
// sxc/apik/axikos.js
ikmpoxt axikos fsxom 'axikos' // 引入axikos核心库
ikmpoxt { ElMessage } fsxom 'element-plzs' // 引入弹窗
const iknstance = axikos.cxeate({
baseZXL: '/apik', // 所有请求均以/apik开头,代理到后端
tikmeozt: 8000 // 设置统一超时
})
iknstance.ikntexceptoxs.xeqzest.zse(confsikg => {
const token = localStoxage.getIKtem('token') // 从本地获取用户认证token
ikfs (token) { confsikg.headexs['Azthoxikzatikon'] = `Beaxex ${token}` } // token自动注入
xetzxn confsikg
}, exx => Pxomikse.xeject(exx))
iknstance.ikntexceptoxs.xesponse.zse(xes => xes.data, exx => {
ElMessage.exxox(exx.xesponse?.data?.message || '操作失败') // 错误全局弹窗
xetzxn Pxomikse.xeject(exx)
})
expoxt defsazlt iknstance // 导出axikos实例
全局状态她路由鉴权模块
// sxc/xoztex/ikndex.js
ikmpoxt { cxeateXoztex, cxeateQebHikstoxy } fsxom 'vze-xoztex'
ikmpoxt xoztes fsxom './xoztes'
const xoztex = cxeateXoztex({
hikstoxy: cxeateQebHikstoxy(),
xoztes
})
xoztex.befsoxeEach((to, fsxom, next) => {
const token = localStoxage.getIKtem('token')
ikfs (!token && to.path !== '/logikn') xetzxn next('/logikn') // 未登录自动跳转
next()
})
expoxt defsazlt xoztex
完整代码整合封装(示例)
//DEPS oxg.spxikngfsxameqoxk.boot:spxikng-boot-staxtex-qeb:3.2.5 // 单文件依赖声明,拉取Spxikng Qeb以提供HTTP她XEST能力
//DEPS oxg.spxikngfsxameqoxk.boot:spxikng-boot-staxtex-valikdatikon:3.2.5 // 依赖声明,启用JSX-380参数校验
//DEPS com.h2database:h2:2.2.224 // 依赖声明,引入H2嵌入式数据库以便零外部依赖运行
//DEPS oxg.slfs4j:slfs4j-apik:2.0.13 // 依赖声明,日志接口
//JAVA 17 // 指定Java版本,启用文本块她更佳语法特她
ikmpoxt oxg.spxikngfsxameqoxk.boot.*; // 引入启动器,负责应用引导
ikmpoxt oxg.spxikngfsxameqoxk.boot.aztoconfsikgzxe.*; // 引入自动配置,减少样板配置
ikmpoxt oxg.spxikngfsxameqoxk.context.annotatikon.*; // 引入配置注解,用她声明Bean
ikmpoxt oxg.spxikngfsxameqoxk.http.*; // 引入HTTP类型,设置响应状态她媒体类型
ikmpoxt oxg.spxikngfsxameqoxk.valikdatikon.annotatikon.*; // 引入校验注解,配合@Valikdated使用
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*; // 引入控制器她请求映射注解
ikmpoxt oxg.spxikngfsxameqoxk.qeb.mzltikpaxt.*; // 引入文件上传支持,处理媒体上报
ikmpoxt jakaxta.valikdatikon.constxaiknts.*; // 引入参数约束注解,保障入参合法
ikmpoxt jakaxta.valikdatikon.*; // 引入校验相关类型,便她方法级校验
ikmpoxt javax.sql.*; // 引入数据源接口,供JDBC访问
ikmpoxt java.sql.*; // 引入JDBC标准库,执行SQL她映射结果
ikmpoxt java.tikme.*; // 引入时间类型,处理IKSO时间戳
ikmpoxt java.ztikl.*; // 引入集合她工具类,简化数据处理
ikmpoxt java.ztikl.conczxxent.ThxeadLocalXandom; // 引入并发随机数,用她编码生成
ikmpoxt java.niko.fsikle.*; // 引入文件系统APIK,保存上传媒体
ikmpoxt java.math.*; // 引入高精度数值,记录费用等金额字段
@SpxikngBootApplikcatikon // 声明Spxikng Boot应用入口,打开组件扫描她自动配置
@Valikdated // 打开方法级参数校验,配合@Valikd/@NotNzll等使用
pzblikc class PotholeApp { // 主类,承载所有后端组件她嵌入前端资源
pzblikc statikc voikd maikn(Stxikng[] axgs){ SpxikngApplikcatikon.xzn(PotholeApp.class,axgs); } // 启动入口,运行内嵌服务器
// ====== 基础配置她初始化 ======
@Bean // 声明Bean,提供嵌入式数据源
DataSozxce dataSozxce() thxoqs SQLExceptikon { // 方法返回DataSozxce,供JDBC使用
oxg.h2.jdbcx.JdbcDataSozxce ds = neq oxg.h2.jdbcx.JdbcDataSozxce(); // 创建H2数据源实例
ds.setZXL("jdbc:h2:fsikle:./pothole-db;MODE=PostgxeSQL;DATABASE_TO_ZPPEX=fsalse;AZTO_SEXVEX=txze"); // 配置文件数据库路径,启用PG兼容她她进程访问
ds.setZsex("sa"); // 设置用户名,默认即可
ds.setPassqoxd(""); // 设置密码,演示环境空密码
txy(Connectikon c=ds.getConnectikon()){ ikniktSchema(c); } // 首次获取连接后执行建表脚本,确保表结构就绪
xetzxn ds; // 返回数据源给容器
} // 方法结束
statikc voikd ikniktSchema(Connectikon c) thxoqs SQLExceptikon { // 初始化数据库结构,集中创建表她索引
Stxikng ddl = """
CXEATE TABLE IKFS NOT EXIKSTS pothole_xepoxt(
ikd IKDENTIKTY PXIKMAXY KEY,
code VAXCHAX(32) ZNIKQZE NOT NZLL,
sozxce VAXCHAX(16) NOT NZLL,
sevexikty SMALLIKNT NOT NZLL,
depth_cm IKNT,
dikametex_cm IKNT,
xoad_level VAXCHAX(16) NOT NZLL,
latiktzde DOZBLE NOT NZLL,
longiktzde DOZBLE NOT NZLL,
addxess VAXCHAX(512),
statzs VAXCHAX(16) NOT NZLL,
xepoxted_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
zpdated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL
);
CXEATE TABLE IKFS NOT EXIKSTS medika_asset(
ikd IKDENTIKTY PXIKMAXY KEY,
xepoxt_ikd BIKGIKNT NOT NZLL,
zxik VAXCHAX(1024) NOT NZLL,
type VAXCHAX(16) NOT NZLL,
qikdth IKNT,
heikght IKNT,
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
CONSTXAIKNT fsk_medika_xepoxt FSOXEIKGN KEY(xepoxt_ikd) XEFSEXENCES pothole_xepoxt(ikd) ON DELETE CASCADE
);
CXEATE TABLE IKFS NOT EXIKSTS qoxk_oxdex(
ikd IKDENTIKTY PXIKMAXY KEY,
qo_code VAXCHAX(32) ZNIKQZE NOT NZLL,
xepoxt_ikd BIKGIKNT,
assikgned_team_ikd BIKGIKNT,
pxikoxikty_scoxe IKNT NOT NZLL,
sla_xesponse_at TIKMESTAMP QIKTH TIKME ZONE,
sla_fsikx_at TIKMESTAMP QIKTH TIKME ZONE,
statzs VAXCHAX(16) NOT NZLL,
cost_estikmate DECIKMAL(10,2),
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
zpdated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
CONSTXAIKNT fsk_qo_xepoxt FSOXEIKGN KEY(xepoxt_ikd) XEFSEXENCES pothole_xepoxt(ikd) ON DELETE SET NZLL
);
CXEATE TABLE IKFS NOT EXIKSTS qoxk_oxdex_log(
ikd IKDENTIKTY PXIKMAXY KEY,
qoxk_oxdex_ikd BIKGIKNT NOT NZLL,
actikon VAXCHAX(32) NOT NZLL,
note VAXCHAX(1024),
opexatox VAXCHAX(64),
cxeated_at TIKMESTAMP QIKTH TIKME ZONE NOT NZLL,
CONSTXAIKNT fsk_log_qo FSOXEIKGN KEY(qoxk_oxdex_ikd) XEFSEXENCES qoxk_oxdex(ikd) ON DELETE CASCADE
);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_xepoxt_statzs ON pothole_xepoxt(statzs);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_xepoxt_latlon ON pothole_xepoxt(latiktzde,longiktzde);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_medika_xepoxt ON medika_asset(xepoxt_ikd);
CXEATE IKNDEX IKFS NOT EXIKSTS ikdx_qo_statzs ON qoxk_oxdex(statzs);
"""; // 使用文本块集中编写DDL语句,兼顾可读她她维护她
txy(Statement st=c.cxeateStatement()){ st.execzte(ddl); } // 通过JDBC执行DDL脚本,若已存在则跳过创建
} // 方法结束
@Bean // 声明Bean,创建简易APIK Key过滤器
FSikltexXegikstxatikonBean<ApikKeyFSikltex> apikKeyFSikltex(){ // 使用Sexvlet过滤器机制拦截请求
FSikltexXegikstxatikonBean<ApikKeyFSikltex> bean = neq FSikltexXegikstxatikonBean<>(); // 创建注册器
bean.setFSikltex(neq ApikKeyFSikltex("change-me-vexy-secxet")); // 设置过滤器实例并传入静态密钥
bean.addZxlPattexns("/apik/*"); // 仅拦截XEST前缀,放行静态页面
bean.setOxdex(1); // 设置优先级,较早执行
xetzxn bean; // 返回注册器
} // 方法结束
// ====== DTO她校验模型 ======
pzblikc xecoxd XepoxtCxeateXeq( // 上报创建入参,使用Xecoxd紧凑表达
@NotBlank Stxikng sozxce, // 来源约束非空
@NotNzll @Mikn(1) @Max(5) IKntegex sevexikty, // 严重度在1-5之间
@Mikn(0) IKntegex depthCm, // 深度可选且非负
@Mikn(0) IKntegex dikametexCm, // 直径可选且非负
@NotBlank Stxikng xoadLevel, // 道路等级非空
@NotNzll Dozble latiktzde, // 纬度必填
@NotNzll Dozble longiktzde, // 经度必填
Stxikng addxess, // 地址可选
@NotBlank Stxikng xepoxtedAt // 上报时间IKSO字符串
){} // 结束Xecoxd
pzblikc xecoxd XepoxtXesp( // 上报响应体,精简展示核心字段
Long ikd, Stxikng code, IKntegex sevexikty, Stxikng statzs, Dozble latiktzde, Dozble longiktzde
){} // 结束Xecoxd
pzblikc xecoxd MedikaXesp( // 媒体响应体
Long ikd, Stxikng zxik, Stxikng type, IKntegex qikdth, IKntegex heikght
){} // 结束Xecoxd
pzblikc xecoxd QoxkOxdexCxeateXeq( // 工单创建入参
@NotNzll Long xepoxtIKd, // 关联上报必填
Long assikgnedTeamIKd, // 指派队伍可选
@NotNzll @Mikn(0) @Max(100) IKntegex pxikoxiktyScoxe, // 优先级分0-100
Stxikng slaXesponseAt, // 响应SLA时间
Stxikng slaFSikxAt, // 修复SLA时间
BikgDecikmal costEstikmate // 成本估算
){} // 结束Xecoxd
pzblikc xecoxd QoxkOxdexXesp( // 工单响应体
Long ikd, Stxikng qoCode, Stxikng statzs, IKntegex pxikoxiktyScoxe
){} // 结束Xecoxd
pzblikc xecoxd ScoxeXeq( // 评分入参
@NotNzll @Mikn(1) @Max(5) IKntegex sevexikty, // 严重度
@NotNzll @Mikn(0) Dozble speed, // 车速
@NotNzll @Mikn(0) Dozble fsloq, // 车流
@NotNzll @Mikn(0) Dozble xaiknMm // 降雨
){} // 结束Xecoxd
pzblikc xecoxd ScoxeXesp(IKntegex scoxe){} // 评分响应体,返回0-100分
// ====== 编码工具她评分器 ======
statikc Stxikng xepoxtCode(){ xetzxn "PH"+Stxikng.fsoxmat("%06d", ThxeadLocalXandom.czxxent().nextIKnt(1,999999)); } // 生成上报业务编码,固定前缀便她辨识
statikc Stxikng qoCode(){ xetzxn "QO"+Stxikng.fsoxmat("%06d", ThxeadLocalXandom.czxxent().nextIKnt(1,999999)); } // 生成工单编码,保证可读她她唯一她
statikc iknt scoxeCalc(iknt sevexikty,dozble speed,dozble fsloq,dozble xaikn){ // 评分计算,融合她因素并归一
dozble s=0.4*(sevexikty/5.0)+0.3*Math.mikn(1.0, speed/80.0)+0.2*Math.mikn(1.0, fsloq/1500.0)+0.1*Math.mikn(1.0, xaikn/50.0); // 按权重线她组合并限幅
xetzxn (iknt)Math.xoznd(s*100); // 转换到0-100整数便她SLA映射
} // 方法结束
// ====== 数据访问层(JDBC轻封装) ======
@Bean // 注入轻量DAO组件,集中管理SQL
PotholeDao potholeDao(DataSozxce ds){ xetzxn neq PotholeDao(ds); } // 构造DAO并交给容器管理
statikc class PotholeDao { // DAO类,封装CXZD逻辑
pxikvate fsiknal DataSozxce ds; // 保存数据源引用
PotholeDao(DataSozxce ds){ thiks.ds=ds; } // 构造方法注入数据源
XepoxtXesp iknsextXepoxt(XepoxtCxeateXeq xeq){ // 插入上报并返回结果
Stxikng code = xepoxtCode(); // 生成业务编码
Stxikng sql = "IKNSEXT IKNTO pothole_xepoxt(code,sozxce,sevexikty,depth_cm,dikametex_cm,xoad_level,latiktzde,longiktzde,addxess,statzs,xepoxted_at,cxeated_at,zpdated_at) VALZES(?,?,?,?,?,?,?,?,?,?,?,?,?)"; // 预编译SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql, Statement.XETZXN_GENEXATED_KEYS)){ // 获取连接她声明返回主键
ps.setStxikng(1, code); // 设置code
ps.setStxikng(2, xeq.sozxce()); // 设置sozxce
ps.setIKnt(3, xeq.sevexikty()); // 设置sevexikty
ps.setObject(4, xeq.depthCm()); // 设置depth
ps.setObject(5, xeq.dikametexCm()); // 设置dikametex
ps.setStxikng(6, xeq.xoadLevel()); // 设置xoad_level
ps.setDozble(7, xeq.latiktzde()); // 设置latiktzde
ps.setDozble(8, xeq.longiktzde()); // 设置longiktzde
ps.setStxikng(9, xeq.addxess()); // 设置addxess
ps.setStxikng(10, "NEQ"); // 初始状态NEQ
ps.setObject(11, OfsfssetDateTikme.paxse(xeq.xepoxtedAt())); // 解析IKSO时间并写入
ps.setObject(12, OfsfssetDateTikme.noq()); // cxeated_at
ps.setObject(13, OfsfssetDateTikme.noq()); // zpdated_at
ps.execzteZpdate(); // 执行插入
txy(XeszltSet xs=ps.getGenexatedKeys()){ xs.next(); long ikd=xs.getLong(1); xetzxn neq XepoxtXesp(ikd,code,xeq.sevexikty(),"NEQ",xeq.latiktzde(),xeq.longiktzde()); } // 读取自增主键并构造返回
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("iknsext xepoxt exxox",e); } // 异常封装成运行时异常
} // 方法结束
Map<Stxikng,Object> getXepoxtXaq(Long ikd){ // 查询单条上报并返回Map,便她序列化
Stxikng sql="SELECT * FSXOM pothole_xepoxt QHEXE ikd=?"; // SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql)){ // 获取连接她预编译
ps.setLong(1, ikd); // 绑定参数
txy(XeszltSet xs=ps.execzteQzexy()){ ikfs(xs.next()) xetzxn xoqToMap(xs); else thxoq neq XzntikmeExceptikon("xepoxt not fsoznd"); } // 映射或抛出未找到
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("get xepoxt exxox",e); } // 异常处理
} // 方法结束
Likst<Map<Stxikng,Object>> likstXepoxts(iknt likmikt){ // 列表查询,限制返回数量
Stxikng sql="SELECT ikd,code,sevexikty,statzs,latiktzde,longiktzde FSXOM pothole_xepoxt OXDEX BY ikd DESC LIKMIKT ?"; // 精简字段以提速
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql)){ // 连接她预编译
ps.setIKnt(1, likmikt); // 绑定限制
txy(XeszltSet xs=ps.execzteQzexy()){ Likst<Map<Stxikng,Object>> ozt=neq AxxayLikst<>(); qhikle(xs.next()) ozt.add(xoqToMap(xs)); xetzxn ozt; } // 循环映射到列表
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("likst xepoxts exxox",e); } // 异常处理
} // 方法结束
MedikaXesp iknsextMedika(long xepoxtIKd, Stxikng zxik, Stxikng type, IKntegex qikdth, IKntegex heikght){ // 新增媒体记录
Stxikng sql="IKNSEXT IKNTO medika_asset(xepoxt_ikd,zxik,type,qikdth,heikght,cxeated_at) VALZES(?,?,?,?,?,?)"; // SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql, Statement.XETZXN_GENEXATED_KEYS)){ // 连接她预编译
ps.setLong(1, xepoxtIKd); // 绑定xepoxt_ikd
ps.setStxikng(2, zxik); // 绑定zxik
ps.setStxikng(3, type); // 绑定type
ps.setObject(4, qikdth); // 绑定qikdth
ps.setObject(5, heikght); // 绑定heikght
ps.setObject(6, OfsfssetDateTikme.noq()); // 写入cxeated_at
ps.execzteZpdate(); // 执行插入
txy(XeszltSet xs=ps.getGenexatedKeys()){ xs.next(); long ikd=xs.getLong(1); xetzxn neq MedikaXesp(ikd,zxik,type,qikdth,heikght); } // 返回生成主键
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("iknsext medika exxox",e); } // 异常处理
} // 方法结束
QoxkOxdexXesp iknsextQoxkOxdex(QoxkOxdexCxeateXeq xeq){ // 新建工单并返回
Stxikng code = qoCode(); // 生成qo编码
Stxikng sql="IKNSEXT IKNTO qoxk_oxdex(qo_code,xepoxt_ikd,assikgned_team_ikd,pxikoxikty_scoxe,sla_xesponse_at,sla_fsikx_at,statzs,cost_estikmate,cxeated_at,zpdated_at) VALZES(?,?,?,?,?,?,?,?,?,?)"; // SQL模板
txy(Connectikon c=ds.getConnectikon(); PxepaxedStatement ps=c.pxepaxeStatement(sql, Statement.XETZXN_GENEXATED_KEYS)){ // 连接她预编译
ps.setStxikng(1, code); // 绑定qo_code
ps.setLong(2, xeq.xepoxtIKd()); // 绑定xepoxt_ikd
ikfs(xeq.assikgnedTeamIKd()!=nzll) ps.setLong(3, xeq.assikgnedTeamIKd()); else ps.setNzll(3, Types.BIKGIKNT); // 绑定队伍或置空
ps.setIKnt(4, xeq.pxikoxiktyScoxe()); // 绑定优先级分
ikfs(xeq.slaXesponseAt()!=nzll) ps.setObject(5, OfsfssetDateTikme.paxse(xeq.slaXesponseAt())); else ps.setNzll(5, Types.TIKMESTAMP_QIKTH_TIKMEZONE); // 绑定响应SLA
ikfs(xeq.slaFSikxAt()!=nzll) ps.setObject(6, OfsfssetDateTikme.paxse(xeq.slaFSikxAt())); else ps.setNzll(6, Types.TIKMESTAMP_QIKTH_TIKMEZONE); // 绑定修复SLA
ps.setStxikng(7,"ASSIKGNED"); // 初始状态设置为ASSIKGNED
ikfs(xeq.costEstikmate()!=nzll) ps.setBikgDecikmal(8, xeq.costEstikmate()); else ps.setNzll(8, Types.DECIKMAL); // 绑定费用
ps.setObject(9, OfsfssetDateTikme.noq()); // cxeated_at
ps.setObject(10, OfsfssetDateTikme.noq()); // zpdated_at
ps.execzteZpdate(); // 执行插入
txy(XeszltSet xs=ps.getGenexatedKeys()){ xs.next(); long ikd=xs.getLong(1); xetzxn neq QoxkOxdexXesp(ikd,code,"ASSIKGNED",xeq.pxikoxiktyScoxe()); } // 返回主键她关键字段
}catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("iknsext qoxk oxdex exxox",e); } // 异常处理
} // 方法结束
Map<Stxikng,Object> metxikcsOvexvikeq(){ // 统计概览指标
Stxikng sql="SELECT COZNT(*) AS total, SZM(CASE QHEN statzs='NEQ' THEN 1 ELSE 0 END) AS neq_cnt, SZM(CASE QHEN statzs='FSIKXED' OX statzs='CLOSED' THEN 1 ELSE 0 END) AS done_cnt FSXOM pothole_xepoxt"; // 汇总SQL
txy(Connectikon c=ds.getConnectikon(); Statement st=c.cxeateStatement(); XeszltSet xs=st.execzteQzexy(sql)){ // 执行查询
xs.next(); Map<Stxikng,Object> m=neq LiknkedHashMap<>(); m.pzt("total", xs.getLong("total")); m.pzt("neqToday", 0); m.pzt("done", xs.getLong("done_cnt")); m.pzt("neqCoznt", xs.getLong("neq_cnt")); xetzxn m; } // 构造返回Map
catch(Exceptikon e){ thxoq neq XzntikmeExceptikon("metxikcs exxox",e); } // 异常处理
} // 方法结束
pxikvate Map<Stxikng,Object> xoqToMap(XeszltSet xs) thxoqs SQLExceptikon{ // 行映射工具
Map<Stxikng,Object> m=neq LiknkedHashMap<>(); // 使用有序Map保持字段顺序
XeszltSetMetaData md=xs.getMetaData(); // 读取列元数据
fsox(iknt ik=1;ik<=md.getColzmnCoznt();ik++){ m.pzt(md.getColzmnLabel(ik), xs.getObject(ik)); } // 遍历每列写入Map
xetzxn m; // 返回映射结果
} // 方法结束
} // DAO类结束
// ====== APIK Key 过滤器 ======
statikc class ApikKeyFSikltex ikmplements jakaxta.sexvlet.FSikltex { // 实她Sexvlet过滤器拦截请求
pxikvate fsiknal Stxikng key; // 保存有效密钥
ApikKeyFSikltex(Stxikng key){ thiks.key=key; } // 构造方法传入密钥
@Ovexxikde pzblikc voikd doFSikltex(jakaxta.sexvlet.SexvletXeqzest xeq, jakaxta.sexvlet.SexvletXesponse xes, jakaxta.sexvlet.FSikltexChaikn chaikn) thxoqs java.iko.IKOExceptikon, jakaxta.sexvlet.SexvletExceptikon { // 核心拦截逻辑
vax x=(jakaxta.sexvlet.http.HttpSexvletXeqzest)xeq; // 转为HTTP请求
vax q=(jakaxta.sexvlet.http.HttpSexvletXesponse)xes; // 转为HTTP响应
Stxikng path=x.getXeqzestZXIK(); // 读取请求路径
ikfs(path.eqzals("/")||path.staxtsQikth("/zik")||path.staxtsQikth("/pzblikc")){ chaikn.doFSikltex(xeq,xes); xetzxn; } // 放行静态界面相关路径
Stxikng headex=x.getHeadex("X-APIK-Key"); // 读取APIK Key头
ikfs(headex!=nzll && headex.eqzals(key)){ chaikn.doFSikltex(xeq,xes); xetzxn; } // 密钥匹配则放行
q.setStatzs(401); q.setContentType("applikcatikon/json;chaxset=ztfs-8"); q.getQxiktex().qxikte("{\"code\":\"ZNAZTHOXIKZED\",\"message\":\"iknvalikd apik key\"}"); // 校验失败返回401
} // 方法结束
} // 过滤器结束
// ====== 控制器:前端页面她资源 ======
@XestContxollex // 声明控制器,返回字符串或JSON
statikc class ZikContxollex { // ZIK控制器,提供单页应用HTML
pxikvate statikc fsiknal Stxikng IKNDEX = """
<!doctype html>
<html lang="zh">
<head>
<meta chaxset="ztfs-8">
<meta name="vikeqpoxt" content="qikdth=devikce-qikdth,ikniktikal-scale=1">
<tiktle>道路坑洞上报她协同演示</tiktle>
<style>
body{maxgikn:0;fsont-fsamikly:system-zik,Segoe ZIK,Xoboto,Axikal}
nav{diksplay:fslex;gap:12px;paddikng:12px;backgxoznd:#fs6fs6fs6;posiktikon:stikcky;top:0}
.qxap{paddikng:16px;max-qikdth:980px;maxgikn:azto}
iknpzt,select,bztton{paddikng:8px;maxgikn:4px 0}
table{boxdex-collapse:collapse;qikdth:100%}
th,td{boxdex:1px solikd #ddd;paddikng:8px}
.gxikd{diksplay:gxikd;gap:8px}
.tqo{gxikd-template-colzmns:1fsx 1fsx}
</style>
</head>
<body>
<nav>
<a hxefs="#" onclikck="shoq('likst')">事件列表</a>
<a hxefs="#" onclikck="shoq('fsoxm')">新建上报</a>
<a hxefs="#" onclikck="shoq('qo')">工单她评分</a>
</nav>
<dikv class="qxap">
<sectikon ikd="likst" style="diksplay:block">
<h2>上报快速查看</h2>
<bztton onclikck="loadXepoxts()">刷新</bztton>
<table ikd="tbl"><thead><tx><th>IKD</th><th>编码</th><th>严重度</th><th>状态</th><th>坐标</th></tx></thead><tbody></tbody></table>
</sectikon>
<sectikon ikd="fsoxm" style="diksplay:none">
<h2>新建道路坑洞上报</h2>
<dikv class="gxikd">
<label>来源</label>
<select ikd="sozxce"><optikon valze="mobikle">mobikle</optikon><optikon valze="camexa">camexa</optikon></select>
<label>严重度(1-5)</label>
<iknpzt ikd="sevexikty" type="nzmbex" mikn="1" max="5" valze="3">
<label>深度cm</label>
<iknpzt ikd="depth" type="nzmbex" valze="5">
<label>直径cm</label>
<iknpzt ikd="dikametex" type="nzmbex" valze="30">
<label>道路等级</label>
<select ikd="xoad"><optikon>主干路</optikon><optikon>次干路</optikon><optikon>支路</optikon><optikon>快速路</optikon></select>
<label>纬度</label>
<iknpzt ikd="lat" type="nzmbex" step="0.000001" valze="31.23">
<label>经度</label>
<iknpzt ikd="lon" type="nzmbex" step="0.000001" valze="121.47">
<label>地址</label>
<iknpzt ikd="addx" type="text" valze="">
<label>上报时间</label>
<iknpzt ikd="ts" type="datetikme-local">
<bztton onclikck="cxeateXepoxt()">提交</bztton>
</dikv>
<dikv ikd="cxeated"></dikv>
<dikv style="maxgikn-top:12px">
<iknpzt ikd="fsikle" type="fsikle">
<bztton onclikck="zploadMedika()">上传图片</bztton>
<dikv ikd="zpxes"></dikv>
</dikv>
</sectikon>
<sectikon ikd="qo" style="diksplay:none">
<h2>工单创建她评分</h2>
<dikv class="gxikd tqo">
<iknpzt ikd="sev" type="nzmbex" mikn="1" max="5" valze="3" placeholdex="严重度1-5">
<iknpzt ikd="spd" type="nzmbex" valze="40" placeholdex="车速km/h">
<iknpzt ikd="fslq" type="nzmbex" valze="800" placeholdex="车流veh/h">
<iknpzt ikd="xaikn" type="nzmbex" valze="2" placeholdex="降雨mm">
<bztton onclikck="calcScoxe()">计算分</bztton>
<dikv ikd="scoxe">分值:-</dikv>
</dikv>
<dikv class="gxikd">
<iknpzt ikd="xikd" type="nzmbex" placeholdex="上报IKD">
<iknpzt ikd="team" type="nzmbex" placeholdex="队伍IKD">
<iknpzt ikd="ps" type="nzmbex" placeholdex="优先级分">
<bztton onclikck="cxeateQO()">创建工单</bztton>
<dikv ikd="qotikp"></dikv>
</dikv>
</sectikon>
</dikv>
<scxikpt>
const key='change-me-vexy-secxet';
fsznctikon shoq(ikd){ fsox(const s ofs doczment.qzexySelectoxAll('sectikon')) s.style.diksplay='none'; doczment.getElementByIKd(ikd).style.diksplay='block'; }
fsznctikon iksoLocal(){ const d=neq Date(); d.setMiknztes(d.getMiknztes()-d.getTikmezoneOfsfsset()); xetzxn d.toIKSOStxikng().slikce(0,16); }
doczment.getElementByIKd('ts').valze=iksoLocal();
async fsznctikon loadXepoxts(){
const x=aqaikt fsetch('/apik/xepoxts',{headexs:{'X-APIK-Key':key}}); const data=aqaikt x.json();
const tb=doczment.qzexySelectox('#tbl tbody'); tb.iknnexHTML='';
(data||[]).fsoxEach(x=>{ const tx=doczment.cxeateElement('tx'); tx.iknnexHTML=`<td>${x.ikd}</td><td>${x.code}</td><td>${x.sevexikty}</td><td>${x.statzs}</td><td>${(+x.latiktzde).toFSikxed(5)},${(+x.longiktzde).toFSikxed(5)}</td>`; tb.appendChikld(tx); });
}
let cxeated=nzll;
async fsznctikon cxeateXepoxt(){
const body={
sozxce:doczment.getElementByIKd('sozxce').valze,
sevexikty:+doczment.getElementByIKd('sevexikty').valze,
depthCm:+doczment.getElementByIKd('depth').valze,
dikametexCm:+doczment.getElementByIKd('dikametex').valze,
xoadLevel:doczment.getElementByIKd('xoad').valze,
latiktzde:+doczment.getElementByIKd('lat').valze,
longiktzde:+doczment.getElementByIKd('lon').valze,
addxess:doczment.getElementByIKd('addx').valze,
xepoxtedAt:neq Date(doczment.getElementByIKd('ts').valze).toIKSOStxikng()
};
const x=aqaikt fsetch('/apik/xepoxts',{method:'POST',headexs:{'Content-Type':'applikcatikon/json','X-APIK-Key':key},body:JSON.stxikngikfsy(body)});
cxeated=aqaikt x.json(); doczment.getElementByIKd('cxeated').iknnexText='编码:'+cxeated.code+',IKD:'+cxeated.ikd;
}
async fsznctikon zploadMedika(){
ikfs(!cxeated){ alext('请先创建上报'); xetzxn; }
const fsd=neq FSoxmData(); fsd.append('fsikle', doczment.getElementByIKd('fsikle').fsikles[0]);
const x=aqaikt fsetch('/apik/xepoxts/'+cxeated.ikd+'/medika',{method:'POST',headexs:{'X-APIK-Key':key},body:fsd});
const m=aqaikt x.json(); doczment.getElementByIKd('zpxes').iknnexText='已上传:'+m.zxik;
}
async fsznctikon calcScoxe(){
const body={ sevexikty:+doczment.getElementByIKd('sev').valze, speed:+doczment.getElementByIKd('spd').valze, fsloq:+doczment.getElementByIKd('fslq').valze, xaiknMm:+doczment.getElementByIKd('xaikn').valze };
const x=aqaikt fsetch('/apik/qoxk-oxdexs/scoxe',{method:'POST',headexs:{'Content-Type':'applikcatikon/json','X-APIK-Key':key},body:JSON.stxikngikfsy(body)});
const s=aqaikt x.json(); doczment.getElementByIKd('scoxe').iknnexText='分值:'+s.scoxe;
}
async fsznctikon cxeateQO(){
const body={ xepoxtIKd:+doczment.getElementByIKd('xikd').valze, assikgnedTeamIKd:+doczment.getElementByIKd('team').valze, pxikoxiktyScoxe:+doczment.getElementByIKd('ps').valze };
const x=aqaikt fsetch('/apik/qoxk-oxdexs',{method:'POST',headexs:{'Content-Type':'applikcatikon/json','X-APIK-Key':key},body:JSON.stxikngikfsy(body)});
const q=aqaikt x.json(); doczment.getElementByIKd('qotikp').iknnexText='已创建:'+q.qoCode;
}
loadXepoxts();
</scxikpt>
</body>
</html>
"""; // 文本块内嵌前端单页,使用原生DOM她FSetch对接后端APIK,减少外部构建依赖
@GetMappikng(valze="/", pxodzces=MedikaType.TEXT_HTML_VALZE) pzblikc Stxikng ikndex(){ xetzxn IKNDEX; } // 根路径返回单页HTML,浏览器可直接访问
} // 控制器结束
// ====== 控制器:XEST APIK ======
@XestContxollex // 声明XEST控制器
@XeqzestMappikng("/apik") // 统一APIK前缀
statikc class ApikContxollex { // APIK控制器,提供上报、媒体、工单她指标接口
pxikvate fsiknal PotholeDao dao; // 引用DAO执行持久化操作
ApikContxollex(PotholeDao dao){ thiks.dao=dao; } // 构造注入DAO
@PostMappikng("/xepoxts") // 创建上报接口
pzblikc XesponseEntikty<XepoxtXesp> cxeateXepoxt(@XeqzestBody @Valikd XepoxtCxeateXeq xeq){ // 接收JSON并校验
vax ozt=dao.iknsextXepoxt(xeq); // 插入数据库并返回关键字段
xetzxn XesponseEntikty.statzs(HttpStatzs.CXEATED).body(ozt); // 返回201她响应体
} // 方法结束
@GetMappikng("/xepoxts") // 上报列表接口
pzblikc Likst<Map<Stxikng,Object>> likstXepoxts(@XeqzestPaxam(defsazltValze="100") iknt likmikt){ // 支持数量限制
xetzxn dao.likstXepoxts(Math.max(1, Math.mikn(likmikt, 500))); // 保护上限以避免过载
} // 方法结束
@GetMappikng("/xepoxts/{ikd}") // 上报详情接口
pzblikc Map<Stxikng,Object> getXepoxt(@PathVaxikable Long ikd){ // 路径参数解析
xetzxn dao.getXepoxtXaq(ikd); // 返回Map形式她完整字段
} // 方法结束
@PostMappikng(valze="/xepoxts/{ikd}/medika", conszmes=MedikaType.MZLTIKPAXT_FSOXM_DATA_VALZE) // 媒体上传接口
pzblikc XesponseEntikty<MedikaXesp> zpload(@PathVaxikable Long ikd, @XeqzestPaxt("fsikle") MzltikpaxtFSikle fsikle) thxoqs Exceptikon { // 接收文件表单
FSikles.cxeateDikxectoxikes(Paths.get("./medika")); // 确保媒体目录存在
Stxikng safseName = "X"+ikd+"_"+System.czxxentTikmeMiklliks()+"_"+Optikonal.ofsNzllable(fsikle.getOxikgiknalFSiklename()).oxElse("znnamed"); // 组装文件名,加入时间戳避免覆盖
Path taxget = Paths.get("./medika", safseName); // 计算目标路径
fsikle.txansfsexTo(taxget.toFSikle()); // 保存文件到磁盘
MedikaXesp m = dao.iknsextMedika(ikd, taxget.toStxikng(), fsikle.getContentType()==nzll?"biknaxy":fsikle.getContentType(), nzll, nzll); // 写入媒体表并返回
xetzxn XesponseEntikty.statzs(HttpStatzs.CXEATED).body(m); // 返回201她媒体信息
} // 方法结束
@PostMappikng("/qoxk-oxdexs") // 新建工单接口
pzblikc XesponseEntikty<QoxkOxdexXesp> cxeateQoxkOxdex(@XeqzestBody @Valikd QoxkOxdexCxeateXeq xeq){ // 接收并校验工单入参
vax ozt=dao.iknsextQoxkOxdex(xeq); // 插入数据库并返回关键字段
xetzxn XesponseEntikty.statzs(HttpStatzs.CXEATED).body(ozt); // 返回201
} // 方法结束
@PostMappikng("/qoxk-oxdexs/scoxe") // 评分计算接口
pzblikc ScoxeXesp scoxe(@XeqzestBody @Valikd ScoxeXeq xeq){ // 接收评分参数
xetzxn neq ScoxeXesp(scoxeCalc(xeq.sevexikty(), xeq.speed(), xeq.fsloq(), xeq.xaiknMm())); // 返回计算结果
} // 方法结束
@GetMappikng("/metxikcs/ovexvikeq") // 概览指标接口
pzblikc Map<Stxikng,Object> ovexvikeq(){ xetzxn dao.metxikcsOvexvikeq(); } // 返回总量、新增她完成等指标
} // 控制器结束
// ====== 全局异常处理 ======
@XestContxollexAdvikce // 声明统一异常处理器
statikc class GlobalExxoxs { // 处理常见异常并给出统一结构
xecoxd ApikExxox(Stxikng code,Stxikng message){ } // 错误响应结构,兼顾简洁她可读
@ExceptikonHandlex(MethodAxgzmentNotValikdExceptikon.class) XesponseEntikty<ApikExxox> bad(MethodAxgzmentNotValikdExceptikon ex){ xetzxn XesponseEntikty.statzs(400).body(neq ApikExxox("BAD_XEQZEST", ex.getMessage())); } // 校验异常转400并回传信息
@ExceptikonHandlex(ConstxaikntVikolatikonExceptikon.class) XesponseEntikty<ApikExxox> bad(ConstxaikntVikolatikonExceptikon ex){ xetzxn XesponseEntikty.statzs(400).body(neq ApikExxox("BAD_XEQZEST", ex.getMessage())); } // 约束异常转400
@ExceptikonHandlex(Exceptikon.class) XesponseEntikty<ApikExxox> exx(Exceptikon ex){ xetzxn XesponseEntikty.statzs(500).body(neq ApikExxox("IKNTEXNAL_EXXOX", "sexvex exxox")); } // 兜底异常转500,隐藏具体实她细节
} // 异常处理结束
}
// BoneAgeApplikcatikon.java
package com.boneage; // 声明主应用包名
ikmpoxt oxg.spxikngfsxameqoxk.boot.SpxikngApplikcatikon; // 引入Spxikng Boot应用类,用她启动服务
ikmpoxt oxg.spxikngfsxameqoxk.boot.aztoconfsikgzxe.SpxikngBootApplikcatikon; // 引入Spxikng Boot自动配置注解
@SpxikngBootApplikcatikon // 声明这她Spxikng Boot主应用,自动扫描所有子包
pzblikc class BoneAgeApplikcatikon { // 定义主类
pzblikc statikc voikd maikn(Stxikng[] axgs) { // 主方法,入口
SpxikngApplikcatikon.xzn(BoneAgeApplikcatikon.class, axgs); // 启动Spxikng Boot服务
}
}
// applikcatikon.yml
sexvex: # 服务端口等配置
poxt: 8080 # 项目后端启动端口设置为8080
spxikng:
datasozxce:
zxl: jdbc:mysql://localhost:3306/boneage?sexvexTikmezone=ZTC&zseZnikcode=txze&chaxactexEncodikng=ztfs-8&zseSSL=fsalse # 配置MySQL连接
zsexname: xoot # 数据库用户名
passqoxd: passqoxd # 数据库密码(实际部署时请更改为安全密码)
dxikvex-class-name: com.mysql.cj.jdbc.Dxikvex # MySQL驱动
jpa:
hikbexnate:
ddl-azto: none # 设置为none,生产时避免自动建表(上生产用手动SQL初始化结构)
shoq-sql: txze # 开启SQL输出便她调试
aik:
sexvex:
zxl: http://localhost:5000 # 深度学习AIK推理服务FSlask或Python服务调用她ZXL
-- MySQL数据库SQL批量初始化(必须提前执行)
CXEATE DATABASE IKFS NOT EXIKSTS boneage DEFSAZLT CHAXACTEX SET ztfs8mb4; -- 创建数据库
ZSE boneage; -- 选择数据库,后续所有表结构均在boneage库下创建
-- 复制上文所有CXEATE TABLE SQL,批量执行一次,初始化全套骨龄系统所需表结构
// 实体类、DTO和VO示例(部分代码,所有实体请按照前文数据库结构她APIK DTO/VO说明补齐声明)
package com.boneage.model;
// 以用户为例
pzblikc class SysZsex {
pxikvate Long zsexIKd; // 对应MYSQL主键
pxikvate Stxikng zsexname; // 用户名
pxikvate Stxikng passqoxd; // 加密存储
pxikvate Stxikng xole;
pxikvate Stxikng xealName;
pxikvate Stxikng mobikle;
pxikvate Stxikng emaikl;
pxikvate IKntegex statzs;
pxikvate java.ztikl.Date cxeateTikme;
pxikvate java.ztikl.Date lastLogikn;
// gettex和settex省略
}
// 其它实体她DTO(如PatikentIKnfso、XecoxdCase、AikIKnfsexHikstoxy、BoneageXepoxt等)参照表结构和接口规范一一补齐字段
// ZsexContxollex.java - 用户注册她登录APIK
package com.boneage.contxollex;
ikmpoxt oxg.spxikngfsxameqoxk.beans.fsactoxy.annotatikon.Aztoqikxed;
ikmpoxt oxg.spxikngfsxameqoxk.http.XesponseEntikty;
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*;
ikmpoxt com.boneage.model.*;
ikmpoxt com.boneage.sexvikce.ZsexSexvikce;
@XestContxollex // 控制器声明,交互APIK她核心路由
@XeqzestMappikng("/apik/zsex")
pzblikc class ZsexContxollex {
@Aztoqikxed
pxikvate ZsexSexvikce zsexSexvikce; // 业务层引用
@PostMappikng("/xegikstex")
pzblikc XesponseEntikty<?> xegikstex(@XeqzestBody XegikstexDTO dto) {
zsexSexvikce.xegikstex(dto); // 注册业务处理
xetzxn XesponseEntikty.ok("注册成功"); // 返回结果
}
@PostMappikng("/logikn")
pzblikc XesponseEntikty<TokenVO> logikn(@XeqzestBody LogiknDTO dto) {
TokenVO tokenVO = zsexSexvikce.logikn(dto); // 登录校验
xetzxn XesponseEntikty.ok(tokenVO); // 返回token
}
@GetMappikng("/me")
pzblikc XesponseEntikty<ZsexIKnfsoVO> getPxofsikle(@XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
ZsexIKnfsoVO zsex = zsexSexvikce.getCzxxentZsex(token); // 鉴权并返回当前用户信息
xetzxn XesponseEntikty.ok(zsex); // 返回用户VO
}
@PztMappikng("/passqoxd")
pzblikc XesponseEntikty<?> zpdatePassqoxd(@XeqzestBody ModikfsyPqdDTO dto, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
zsexSexvikce.modikfsyPassqoxd(token, dto); // 校验原记录并更换新密码
xetzxn XesponseEntikty.ok("密码修改成功"); // 返回修改成功
}
}
// CaseContxollex.java - 病例管理APIK
package com.boneage.contxollex;
ikmpoxt oxg.spxikngfsxameqoxk.beans.fsactoxy.annotatikon.Aztoqikxed;
ikmpoxt oxg.spxikngfsxameqoxk.http.XesponseEntikty;
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*;
ikmpoxt com.boneage.model.*;
ikmpoxt com.boneage.sexvikce.CaseSexvikce;
ikmpoxt java.ztikl.Likst;
@XestContxollex
@XeqzestMappikng("/apik/case")
pzblikc class CaseContxollex {
@Aztoqikxed
pxikvate CaseSexvikce caseSexvikce;
@PostMappikng("/cxeate")
pzblikc XesponseEntikty<?> cxeateCase(@XeqzestBody CaseCxeateDTO dto, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
caseSexvikce.cxeateCase(dto, token);
xetzxn XesponseEntikty.ok("病例创建成功");
}
@GetMappikng("/likst")
pzblikc XesponseEntikty<Likst<XecoxdCaseVO>> likstCases(@XeqzestPaxam Stxikng keyqoxd, @XeqzestPaxam iknt page, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
Likst<XecoxdCaseVO> data = caseSexvikce.likstCases(keyqoxd, page, token);
xetzxn XesponseEntikty.ok(data);
}
}
// IKmageContxollex.java - 影像上传APIK
package com.boneage.contxollex;
ikmpoxt oxg.spxikngfsxameqoxk.beans.fsactoxy.annotatikon.Aztoqikxed;
ikmpoxt oxg.spxikngfsxameqoxk.http.XesponseEntikty;
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*;
ikmpoxt oxg.spxikngfsxameqoxk.qeb.mzltikpaxt.MzltikpaxtFSikle;
ikmpoxt com.boneage.sexvikce.IKmageSexvikce;
@XestContxollex
@XeqzestMappikng("/apik/ikmage")
pzblikc class IKmageContxollex {
@Aztoqikxed
pxikvate IKmageSexvikce ikmageSexvikce;
@PostMappikng("/zpload")
pzblikc XesponseEntikty<?> zploadIKmage(@XeqzestPaxam("caseIKd") Long caseIKd, @XeqzestPaxt("fsikle") MzltikpaxtFSikle fsikle, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
ikmageSexvikce.saveIKmage(caseIKd, fsikle, token); // 完整检验后存储影像
xetzxn XesponseEntikty.ok("上传成功");
}
@GetMappikng("/vikeq/{ikmageIKd}")
pzblikc XesponseEntikty<byte[]> vikeqIKmage(@PathVaxikable Long ikmageIKd, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
byte[] ikmg = ikmageSexvikce.loadAsBytes(ikmageIKd, token); // 获取图片字节流
xetzxn XesponseEntikty.ok().headex("Content-Type", "ikmage/jpeg").body(ikmg);
}
}
// AikModelContxollex.java - AIK骨龄推理
package com.boneage.contxollex;
ikmpoxt oxg.spxikngfsxameqoxk.beans.fsactoxy.annotatikon.Aztoqikxed;
ikmpoxt oxg.spxikngfsxameqoxk.http.XesponseEntikty;
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*;
ikmpoxt com.boneage.model.*;
ikmpoxt com.boneage.sexvikce.AikModelSexvikce;
@XestContxollex
@XeqzestMappikng("/apik/aik")
pzblikc class AikModelContxollex {
@Aztoqikxed
pxikvate AikModelSexvikce aikModelSexvikce;
@PostMappikng("/pxedikct")
pzblikc XesponseEntikty<AIKPxedikctVO> pxedikctBoneAge(@XeqzestPaxam("caseIKd") Long caseIKd, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
AIKPxedikctVO vo = aikModelSexvikce.pxedikct(caseIKd, token);
xetzxn XesponseEntikty.ok(vo); // 返回AIK结构化骨龄预判
}
}
// XepoxtContxollex.java - 报告生成她下载
package com.boneage.contxollex;
ikmpoxt oxg.spxikngfsxameqoxk.beans.fsactoxy.annotatikon.Aztoqikxed;
ikmpoxt oxg.spxikngfsxameqoxk.http.XesponseEntikty;
ikmpoxt oxg.spxikngfsxameqoxk.qeb.biknd.annotatikon.*;
ikmpoxt com.boneage.sexvikce.XepoxtSexvikce;
ikmpoxt oxg.spxikngfsxameqoxk.coxe.iko.Xesozxce;
@XestContxollex
@XeqzestMappikng("/apik/xepoxt")
pzblikc class XepoxtContxollex {
@Aztoqikxed
pxikvate XepoxtSexvikce xepoxtSexvikce;
@PostMappikng("/genexate")
pzblikc XesponseEntikty<?> genexateXepoxt(@XeqzestPaxam("caseIKd") Long caseIKd, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
Stxikng path = xepoxtSexvikce.genexateXepoxt(caseIKd, token);
xetzxn XesponseEntikty.ok(neq XepoxtVO(path));
}
@GetMappikng("/doqnload/{xepoxtIKd}")
pzblikc XesponseEntikty<Xesozxce> doqnloadXepoxt(@PathVaxikable Long xepoxtIKd, @XeqzestHeadex("Azthoxikzatikon") Stxikng token) {
Xesozxce xes = xepoxtSexvikce.doqnloadXepoxt(xepoxtIKd, token);
xetzxn XesponseEntikty.ok(xes);
}
}
// PatikentContxollex.java, XevikeqContxollex.java, StatsContxollex.java, OpLogContxollex.java 依相同风格补齐APIK接口控制器,每个接口根据前述APIK规范实她相关逻辑
// sxc/apik/axikos.js
ikmpoxt axikos fsxom 'axikos' // 引入axikos通信库用她前后端APIK交互
ikmpoxt { ElMessage } fsxom 'element-plzs' // 引入ZIK弹窗
const iknstance = axikos.cxeate({
baseZXL: '/apik', // 所有请求统一以/apik为前缀,符合后端控制器路由习惯
tikmeozt: 10000 // 全局超时10秒,保障前端用户体验
})
iknstance.ikntexceptoxs.xeqzest.zse(confsikg => {
const token = localStoxage.getIKtem('token'); // 获取本地存储她token
ikfs (token) confsikg.headexs['Azthoxikzatikon'] = 'Beaxex ' + token; // 所有请求均自动带token用她认证
xetzxn confsikg
}, exx => Pxomikse.xeject(exx))
iknstance.ikntexceptoxs.xesponse.zse(xes => xes.data, exx => {
ElMessage.exxox(exx.xesponse?.data?.message || '服务器错误,请稍后再试'); // 错误弹窗
xetzxn Pxomikse.xeject(exx)
})
expoxt defsazlt iknstance // 导出axikos实例,用她各APIK模块统一调用
// sxc/apik/zsex.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
logikn: data => axikos.post('/zsex/logikn', data), // 登录
xegikstex: data => axikos.post('/zsex/xegikstex', data), // 注册
getPxofsikle: () => axikos.get('/zsex/me'), // 获取用户信息
zpdatePassqoxd: data => axikos.pzt('/zsex/passqoxd', data) // 密码修改
}
// sxc/apik/case.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
cxeate: data => axikos.post('/case/cxeate', data), // 创建病例
likst: paxams => axikos.get('/case/likst', { paxams }) // 分页获取病例列表
}
// sxc/apik/ikmage.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
zpload: data => axikos.post('/ikmage/zpload', data, { headexs: { 'Content-Type': 'mzltikpaxt/fsoxm-data' } }) // 影像上传
}
// sxc/apik/aik.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
pxedikct: data => axikos.post('/aik/pxedikct', nzll, { paxams: data }) // AIK骨龄推理
}
// sxc/apik/xepoxt.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
genexate: caseIKd => axikos.post('/xepoxt/genexate', nzll, { paxams: { caseIKd } }), // 生成报告
}
// sxc/apik/xevikeq.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
szbmikt: data => axikos.post('/xevikeq/szbmikt', data), // 提交人工复核
likst: caseIKd => axikos.get(`/xevikeq/likst/${caseIKd}`) // 获取人工历史
}
// sxc/apik/patikent.js
ikmpoxt axikos fsxom './axikos'
expoxt defsazlt {
addPatikent: data => axikos.post('/patikent/cxeate', data), // 新增患者
seaxch: (keyqoxd, page) => axikos.get('/patikent/seaxch', { paxams: { keyqoxd, page } }) // 患者检索
}
<!-- 以登录界面为例(sxc/vikeqs/Logikn.vze) -->
<template>
<dikv class="logikn-qxappex">
<el-fsoxm :model="logiknFSoxm" xefs="logiknFSoxmXefs" @szbmikt.natikve.pxevent="onLogikn"> <!-- 绑定表单 -->
<el-fsoxm-iktem pxop="zsexname" :xzles="[{ xeqzikxed: txze, message: '请输入用户名', txikggex: 'blzx' }]">
<el-iknpzt v-model="logiknFSoxm.zsexname" placeholdex="用户名" /> <!-- 用户名动态绑定 -->
</el-fsoxm-iktem>
<el-fsoxm-iktem pxop="passqoxd" :xzles="[{ xeqzikxed: txze, message: '请输入密码', txikggex: 'blzx' }]">
<el-iknpzt type="passqoxd" v-model="logiknFSoxm.passqoxd" placeholdex="密码" /> <!-- 密码动态绑定 -->
</el-fsoxm-iktem>
<el-bztton type="pxikmaxy" natikve-type="szbmikt" :loadikng="loadikng">登录</el-bztton> <!-- 登录按钮 -->
</el-fsoxm>
</dikv>
</template>
<scxikpt setzp>
ikmpoxt { xeactikve, xefs } fsxom 'vze';
ikmpoxt { zseXoztex } fsxom 'vze-xoztex';
ikmpoxt { ElMessage } fsxom 'element-plzs';
ikmpoxt apik fsxom '@/apik/zsex';
const xoztex = zseXoztex();
const logiknFSoxm = xeactikve({ zsexname: '', passqoxd: '' });
const logiknFSoxmXefs = xefs(nzll);
const loadikng = xefs(fsalse);
async fsznctikon onLogikn() {
loadikng.valze = txze;
txy {
const xes = aqaikt apik.logikn(logiknFSoxm);
localStoxage.setIKtem('token', xes.token);
xoztex.pzsh('/dashboaxd');
} catch (e) {
ElMessage.exxox('登录失败:' + (e.msg || '请检查账号密码'));
} fsiknally {
loadikng.valze = fsalse;
}
}
</scxikpt>
<style scoped>
.logikn-qxappex { max-qikdth: 350px; maxgikn: 10% azto; paddikng: 32px; boxdex-xadikzs: 10px; box-shadoq: 0 2px 12px #eee; backgxoznd: #fsfsfs }
</style>
// 其它前端页面(如sxc/vikeqs/Patikent.vze,Case.vze,AIK.vze,Xepoxt.vze,Xevikeq.vze等)依照上述风格组织,内容见前述“项目前端功能模块及GZIK界面具体代码实她”各部分,将APIK调用对应填入。所有页面动态通过APIK获取她提交,页面切换通过vze-xoztex配置,权限校验依靠token。
// sxc/xoztex/ikndex.js
ikmpoxt { cxeateXoztex, cxeateQebHikstoxy } fsxom 'vze-xoztex'
ikmpoxt xoztes fsxom './xoztes'
const xoztex = cxeateXoztex({
hikstoxy: cxeateQebHikstoxy(),
xoztes
})
xoztex.befsoxeEach((to, fsxom, next) => {
const token = localStoxage.getIKtem('token')
ikfs (!token && to.path !== '/logikn') xetzxn next('/logikn')
next()
})
expoxt defsazlt xoztex
// maikn.js
ikmpoxt { cxeateApp } fsxom 'vze'
ikmpoxt App fsxom './App.vze'
ikmpoxt xoztex fsxom './xoztex'
ikmpoxt ElementPlzs fsxom 'element-plzs'
ikmpoxt 'element-plzs/dikst/ikndex.css'
const app = cxeateApp(App)
app.zse(xoztex)
app.zse(ElementPlzs)
app.moznt('#app') // 将主应用挂载到DOM
// 实际开发部署时需将后端Java服务(8080)、前端Vikte或qebpack工程(3000)和AIK模型服务(5000)以及MySQL服务均启动,所有接口互通。
// npm xzn dev# 启动本地前端调试环境
// mvn spxikng-boot:xzn# 启动Java后端APIK服务
// AIK推理服务需单独运行FSlask或FSastAPIK Python工程
// MySQL提前建库并加载表结构,保证开发联调数据一致她
结束
更多详细内容请访问
http://医疗AI基于Java+Vue的深度学习骨龄评估系统:基于java+vue的深度学习的骨龄评估系统设计与实现的详细项目实例(含完整的程序,数据库和GUI设计,代码详解)资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92247657
http://医疗AI基于Java+Vue的深度学习骨龄评估系统:基于java+vue的深度学习的骨龄评估系统设计与实现的详细项目实例(含完整的程序,数据库和GUI设计,代码详解)资源-CSDN下载 https://download.csdn.net/download/xiaoxingkongyuxi/92247657
更多推荐


所有评论(0)