433 Commits
Author SHA1 Message Date
wanghaoandClaude 570ed10c33 revert(watch): 还原listByWatch绑定过滤改动,恢复仅排序逻辑
Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-07 15:50:32 +08:00
wanghao d47737de5e Merge remote-tracking branch 'origin/xc' into xc 2026-09-07 15:39:06 +08:00
wanghaoandClaude 991dbabe8a fix(watch): listByWatch改为只展示未绑定手表的用户
Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-07 15:34:40 +08:00
lianlonggang 2f5712a678 feat(watch): listCustom 接口与导出增加 eventId、locateTime 字段
1. AbnormalEvent 新增 eventId、locateTime 字段
2. listCustom/largeScreenListCustom/listCustomForMap 三个接口返回新字段
3. 导出 Excel 通过 @Excel 注解自动支持新字段(顺序 12、13)
4. locateTime 时间格式化为 yyyy-MM-dd HH:mm:ss
5. 修正 createMonitorData 中遗漏的 eventId 与 locateTime 字段赋值
6. DDL 表名修正为 QH_WAT_MONITOR_DATA(项目实际表名)
2026-09-03 13:43:09 +08:00
lianlonggang 41bce5c0b2 feat(watch): notice 入参新增 eventId、locateTime;新增 updateMonitorGps 接口
1. WatchNotifyVo、WatchMonitorData 新增 eventId 与 locateTime 字段
2. 新增 POST /watchApi/updateMonitorGps 接口
3. watchNo + eventId 联合定位告警数据,任一缺失则忽略
4. 已存在 lon/lat 数据则忽略本次更新,避免覆盖
5. GPS84 → GCJ02 坐标转换 + 高德地址反查(参考 createMonitorData 逻辑)
6. 仅更新 GPS 相关字段,不推送大屏与短信
2026-09-03 10:52:44 +08:00
lianlonggang 5c8b56430d feat(db): watch_monitor_data 表新增 EVENT_ID 与 LOCATE_TIME 字段
为支持手表告警事件与定时GPS位置上报的关联,新增两个字段:
- EVENT_ID(事件ID,用于告警事件与GPS位置更新关联)
- LOCATE_TIME(定位时间,手表定时上报的GPS定位时间)

同时新增 IDX_EVENT_ID 单列索引与 IDX_WATCH_NO_EVENT_ID 联合索引,
优化 watchNo + eventId 联合定位查询性能。

数据库:118-DM 达梦数据库,SQL 全大写规范。
2026-09-03 10:52:38 +08:00
wanghaoandClaude 674891eba3 fix(watch): QH_WAT_USER_DATA并发写入唯一约束冲突时忽略重复插入
notice接口并发上报同一手表同一天数据时,先删后插存在竞态,
两个线程同时插入触发唯一约束WATCH_USER_DATA_WATCH_NO,
捕获DuplicateKeyException降级为warn日志,数据不丢失

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-25 11:05:26 +08:00
wanghao a8e8b53203 style(message): 精简权限过滤兜底分支注释 2026-08-10 16:40:20 +08:00
wanghao a835b874ad fix(message): notice_role仅用于菜单可见,列表只可查看本人创建
- applyAdminPermissionFilter 白名单移除 notice_role,使其落入兜底分支
- 兜底过滤字段由 loginUser.getId() 修正为 getUsername(),对齐 createBy 存储
2026-08-10 16:36:30 +08:00
wanghao b6058c5f80 feat(message): 消息通知列表权限白名单加入notice_role + 兜底本人创建可见 2026-08-10 16:03:30 +08:00
pengjie 245cffecb0 fix: 清除因PowerShell Set-Content引入的UTF-8 BOM字符 2026-08-10 10:21:23 +08:00
pengjie b7525d6c7c fix: 修复表重命名脚本语法,改用达梦兼容的 ALTER TABLE RENAME TO 2026-08-10 10:17:16 +08:00
pengjie 1ad322c16a refactor: 缩短急救宣教相关表名 QH_EME_FIRST_AID_* -> QH_EME_FIR_AID_*,解决表名超长问题 2026-08-10 10:14:40 +08:00
lianlonggang d08d20f1ee style(all): 去客户化处理 2026-08-06 17:47:04 +08:00
pengjieandClaude c260b1d79b style: AsyncExportService import合并为通配符
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 09:57:28 +08:00
pengjieandClaude 1028a4e4e1 fix: SysUserServiceImpl注入ISysBaseAPI产生循环依赖,加@Lazy解决
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-05 09:12:37 +08:00
pengjieandClaude 4a15ebe0d2 feat: 补充IM个人资料同步 + 群成员头像导入修复
1. 在8处头像/名称变更点补充imHelloApi.accountReg()调用,同步更新IM个人资料:
   - SysUserController.updateUserAvatar 用户换头像
   - SysUserController.appEdit 移动端编辑资料
   - SysUserController.edit 后台编辑用户
   - EmergencyProfessionUserServiceImpl.customEdit 应急人员编辑
   - EmergencyCenterAdminServiceImpl.updateAdmin 中心管理员编辑
   - HealthUserEmployeeExServiceImpl.updateUserInfo 员工编辑
   - ConDoctorApiController.update 专家端个人简介修改
   - AsyncExportService.importDoctor 批量导入医生
   所有syncImProfile方法内部try-catch兜底,失败不影响主流程。
2. AddGroupMemberDTO新增memberAvatar字段,TencentCloudImUtil.addGroupMember
   导入IM账号时传递FaceUrl,EmergencyApiServiceImpl.addGroupUser构建头像映射。
3. jeecg-system-biz/pom.xml新增health-im-api依赖。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 20:03:20 +08:00
pengjie 0ae963c4cf Revert "fix(im): 邀请入群时使用真实用户身份调用IM API,系统通知显示真实用户名而非administrator"
This reverts commit 07d6f21fed.
2026-08-04 19:33:13 +08:00
pengjieandClaude 07d6f21fed fix(im): 邀请入群时使用真实用户身份调用IM API,系统通知显示真实用户名而非administrator
- AddGroupMemberDTO 新增 fromAccount 内部字段
- TencentCloudImUtil 新增带 identifier 参数的 getHttpsUrl 重载
- addGroupMember 检测 fromAccount,有则用该用户 UserSig
- EmergencyApiServiceImpl.addGroupUser 传入当前登录用户ID

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 18:58:16 +08:00
pengjieandClaude 26ae76a398 fix(im): 创建应急群聊时群名称固定为"应急求助"
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 16:18:13 +08:00
pengjieandClaude 468314df64 refactor(emergency): searchExpert改为两表合集查询,同时搜索QH_EME_PROFESS_USER和qh_con_doctor
原来只查QH_EME_PROFESS_USER再补充qh_con_doctor字段,导致只在qh_con_doctor
中的专家搜不到。改为:
1. 分别查询两表(各自过滤name/排除已入群)
2. LinkedHashMap按userId去重合并
3. sys_user兜底填充realname/avatar/phone/deptName
4. 对profUsers单独补qh_con_doctor专家字段
5. 内存分页返回

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 11:42:47 +08:00
pengjie 3b7f260103 fix(emergency): myParticipatedOrders接口补充返回当前用户自己发起/被救助的应急工单 2026-08-04 11:41:47 +08:00
pengjie 693749fece Merge remote-tracking branch 'origin/xc' into xc 2026-08-04 11:08:52 +08:00
pengjieandClaude e9360d1ad9 feat(emergency): searchExpert接口返回ConDoctor专家数据 + 修复IM UserSig缓存key未区分secretKey的问题
1. searchExpert新增ConDoctor专家字段:
   - 新增ConDoctorMapper直接查询qh_con_doctor表(同库,避免跨模块依赖)
   - EmergencyProfessionUser新增17个@TableField(exist=false)字段承载专家数据
   - searchExpert方法中批量查询ConDoctor并逐字段填充
   - 用户非专家时ConDoctor字段为null
   - 更新API文档-移动端接口.md

2. IM签名缓存优化:
   - TencentCloudImUtil新增secretKey注入
   - Redis key从 silence:im_user_sig:{sdkAppId}:{id}
     改为 silence:im_user_sig:{hash(sdkAppId+secretKey)}:{id}
   - 解决secretKey变更时旧缓存签名仍被命中导致IM请求失败的问题

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-04 11:08:03 +08:00
lianlonggang ab0470c04c style(system): 短信查询相关员工电话号码功能修正;安眼健康监测大屏跳转时,获取token方式调整为统一认证直接获取 2026-07-29 11:49:16 +08:00
lianlonggang 05c37fd532 style(system): kafka集成 2026-07-24 14:40:15 +08:00
wanghao 670d82c868 Merge remote-tracking branch 'origin/xc' into xc 2026-07-23 17:12:57 +08:00
pengjie c3afd0adfa feat(emergency): isLiaisonUser改为查EMP_CARE_EMP表FIRST_AIDER_INFO判断急救人员身份
原逻辑硬编码返回true,现查询AYGC_YWK.EMP_CARE_EMP表中FIRST_AIDER_INFO
JSON字段是否包含当前登录用户工号,有则判定为急救人员。
2026-07-23 17:12:41 +08:00
pengjieandClaude 44e8ac7da5 chore(emergency): 简化群成员上限提示文案
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 11:24:46 +08:00
pengjieandClaude 1167ac792e fix(emergency): getActualGroupMemberList兜底补拉关爱/急救人员 + addGroupUser加9人上限
- 调用getActualGroupMemberList时先执行addCareUsersToGroup重新查询安眼系统,兜底补拉发起应急时未注册的关爱联络员/急救人员
- addCareUsersToGroup改为public,暴露给EmergencyOrderApiService调用
- addGroupUser增加9人群成员上限校验

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 11:13:40 +08:00
pengjieandClaude df41e0c8ce feat(emergency): getActualGroupMemberList自动补拉预设成员 + addGroupUser加9人上限
- getActualGroupMemberList: 查询群成员时自动检查预设成员(被救助人、操作人员、专业人员、紧急联系人、健康联络员),不在群中则自动补拉
- addGroupUser: 邀请前校验群成员总数不超过9人,超限返回提示

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 11:01:23 +08:00
pengjieandClaude f6f70ebf32 fix(emergency): insertOrderForFirstAider中将initiatorType从4改为1
急救员场景工单发起类型与应急就医保持一致,统一使用1

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-22 08:56:38 +08:00
pengjie 661079dd3a chore(emergency): isLiaisonUser临时硬编码返回true
联调阶段先放行所有用户,后续切换为急救员判定时再恢复查关爱人员关系表。
2026-07-22 08:53:14 +08:00
pengjie 9111cb40c6 refactor(emergency): 一人一案接口筛选条件由身份证号改为工号
身份证号在 EMP_BASIC_INFO 中非唯一标识,改用工号(EMPLOYEE_NUM)
匹配当前登录用户档案,避免同号多档时取错记录。
2026-07-21 14:27:02 +08:00
pengjieandClaude 3aa16600d8 feat(im): 新增importAccountsWithNames方法,addGroupMember时批量导入账号名称
- 新增 importAccountsWithNames 方法,使用腾讯IM新版 AccountList 格式导入账号及昵称
- addGroupMember 改为调用新方法,一次批量导入账号+名称
- multiAccountImport 旧方法保持不变,不影响其他调用方
- AddGroupMemberDTO.memberName 移除@JsonIgnore,确保Feign跨服务传输

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 17:35:25 +08:00
pengjieandClaude 32d8ad3652 fix(im): 修复addGroupMember时memberName因@JsonIgnore在Feign传输中丢失
移除MemberListDTO.memberName上的@JsonIgnore注解,确保名称能通过Feign跨服务传输。
改为在发送腾讯IM API前手动移除memberName字段,避免发送IM不接受的字段。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 17:21:41 +08:00
pengjieandClaude b5c5251913 feat(IM): 添加群成员时同步导入成员名称
addGroupMember在导入账号后逐个调用accountImport导入成员Nick名称

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 16:59:44 +08:00
pengjieandClaude 38a0aa07dd feat: 合并急救员建群逻辑到createGroupMag,新增参与工单列表接口
1. createGroupMag增加addCareUsersToGroup调用,自动拉入关爱联络员+急救员
2. 废除createGroupMagFirstAider(标记@Deprecated)
3. 新增/myparticipatedOrders接口,app端查询当前登录人参与的应急工单列表

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-20 15:51:33 +08:00
pengjie be701122ca fix(IM): Redis签名key加入sdkAppId,防止切换AppID后签名不匹配
- key格式: silence:im_user_sig:{sdkAppId}:{identifier}
- 切换IMConfig.sdkAppId后自动缓存miss,重新生成正确签名
- 旧key作为孤儿数据24小时后自然过期
2026-07-20 10:43:07 +08:00
pengjie 1a5a2dcb3f fix(emergency): 修复添加群成员错误提示、成员查询遗漏和日志缺少异常栈
- addGroupUser: IM添加失败时改用友好提示,内部错误记日志
- EmergencyOrderApiServiceImpl: 员工成员查询去掉operatorType过滤,补充兜底覆盖;userId去重
- EmergencyCloudManager: 微服务调用失败日志补充异常栈
2026-07-20 10:26:24 +08:00
pengjie fecfb2a355 fix(IM): addGroupMember前先multiAccountImport,防止账号未注册导致添加失败 2026-07-20 10:25:08 +08:00
lianlonggangandClaude f604d18b32 feat(消息管理): list接口支持按员工电话查询esContent
- queryPageList新增employeePhone参数
- 11位纯数字还原为3~4~4全角波浪线格式后like查询esContent
- 非11位用原值兜底like

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-16 16:25:31 +08:00
pengjieandClaude 145cf89a89 fix(emergency): addCareUsersToGroup改为员工编号匹配,解决手机号不准的TODO
数据流: 安眼手机号 → AYGC_YWK.EMP_BASIC_INFO查EMPLOYEE_NUM → getUserListByWorkNoList → 本系统用户ID

变更:
- LargeScreenDataMapper: 新增selectEmployeeNumsByPhones,INSTR模糊匹配支持多手机号字段
- EmergencyApiServiceImpl: 替换getUserByPhoneList直接匹配,改为两步反查;order_operator匹配同步调整
- 移除旧TODO注释

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-16 09:43:34 +08:00
pengjie d4f1d1a7bd fix(emergency): 修复一人一案接口VARCHAR列映射Double报类型转换异常
达梦库 EMP_PERSON_FILE 表的 BMI/HEIGHT/WEIGHT/WEIGHT_TARGET 等
列为 VARCHAR 类型,MyBatis 映射到 Double 时报类型转换异常,
将对应 VO 字段改为 String,并在 getBmi() 中解析字符串做保留一位小数。
2026-07-14 16:36:55 +08:00
pengjie 2d5198ec3f feat(emergency): 新增大屏一人一案移动端接口
根据当前登录用户身份证号,从安眼库 EMP_PERSON_FILE 关联表
查询最新档案详情,供移动端展示一人一案健康数据。
2026-07-14 16:22:05 +08:00
pengjie e844fec480 feat(emergency): 新增大屏关爱联络员身份判断接口
按当前登录人工号查询关爱关系表是否存在记录,前端用于按联络员
身份差异化展示。原 isLiaisonUser 实现固定返回 false,本次接通
真实查询逻辑。
2026-07-08 17:48:03 +08:00
pengjie 194c3b2134 feat(emergency): 推送IM消息增加resourceId文章ID字段 2026-07-08 09:20:50 +08:00
pengjie a1e5625ca7 fix(emergency): 推送时封面图相对路径拼接jeecg.path.show前缀为全路径 2026-07-07 14:54:22 +08:00
pengjie e16c0c2536 feat(emergency): 推送消息增加封面图字段messageImageUrl,去掉businessId 2026-07-07 14:24:56 +08:00
pengjie 66b77cc231 fix(im): sendGroupMsgArticle补充businessID协议字段,修复客户端显示[自定义消息] 2026-07-07 14:12:03 +08:00
pengjie 9b35cfe9c2 chore(im): 文章推送增加请求参数日志,便于验证businessId等信息 2026-07-07 13:51:58 +08:00
pengjie 4898a63b4b feat(emergency): 宣教推送IM消息改为独立方法sendGroupMsgArticle,不影响已有业务
- 新增 TencentCloudImUtil.sendGroupMsgArticle: From_Account生效、不覆盖businessID、fromAdmin动态判断
- 新增 TXController.sendGroupMsgArticle / IMHelloApi.sendGroupMsgArticle
- FirstAidPushServiceImpl 推送人改为当前登录用户,调用新方法
2026-07-07 13:43:21 +08:00
pengjie 37132eeeb6 fix(im): sendGroupMsgText取消From_Account注释,使fromUserId生效 2026-07-07 12:07:17 +08:00
pengjie 8efc5bbd56 feat(emergency): 移动端宣教详情接口增加阅读记录逻辑 2026-07-07 10:55:57 +08:00
pengjie 3c65f7eb24 fix(emergency): 宣教推送IM消息发送人改为当前登录用户,不再硬编码administrator 2026-07-07 10:39:36 +08:00
pengjieandClaude bb95f1d78f fix(emergency): add接口新增成功后返回主键ID
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 10:32:15 +08:00
pengjieandClaude 1484f621bd fix(emergency): addGroupUser 增加本人判断和IM群成员去重
- 过滤当前登录用户,不允许邀请自己入群
- 查询腾讯IM实际群成员,避免重复邀请已在群中的人
- IM查询失败时降级使用本地 order_operator 数据去重
- 去重来源:本地记录 + IM实际成员 取并集

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 10:05:04 +08:00
pengjieandClaude f10221e92c feat(emergency): push/send 接入IM消息推送,SendIm 新增 businessId 字段
- SendIm 新增 businessId 字段(String),用于区分业务来源,不影响已有调用
- FirstAidPushServiceImpl.send() 步骤5从日志占位改为实际发送IM群消息
- businessId 固定为 "article",前端可据此识别宣教推送消息
- 工单无 sessionId 时跳过IM发送并 log.warn 告警

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-07 09:48:37 +08:00
pengjie 945c0b46d7 @
feat(emergency): 新增腾讯IM验证的群成员列表接口,优化searchEmployee模糊搜索

- 新增 /order/getActualGroupMemberList 接口,先查本地参与人员再调腾讯IM验证,以交集为准
- 新增 GetGroupMemberInfoVO,封装腾讯IM get_group_member_info API响应
- TencentCloudImUtil 新增 getGroupMemberInfo 方法,TXController/IMHelloApi 新增对应端点
- GroupMemberVO 新增 isSelf 字段区分本人 (0/1),默认值 0
- searchEmployee 姓名/工号/手机号改为模糊搜索(* 包装实现)
- 修复 getActualGroupMemberList IM 调用失败时静默返回空列表的 bug,失败时降级使用本地成员
- 接口文档更新变更摘要,标明新接口替换原 /order/getGroupMemberList 地址

Co-Authored-By: Claude <noreply@anthropic.com>
@
2026-07-07 09:29:19 +08:00
pengjie 74ecbe6bda docs(emergency): 移动端接口文档顶部新增本次变更摘要,供前端联调 2026-07-06 17:16:55 +08:00
pengjie fd7fd32c8d feat(emergency): 新增急救员建群接口,人员接口返回手机号
- 新增 GET /api/emergency/createGroupMagFirstAider(不发起通话,自动拉关爱/急救员)
- createGroupMag 原始逻辑不受影响,新方法独立实现
- OrderOperator.operatorType 注释同步数据库(新增3/4/5)
- EmergencyProfessionUser/GroupMemberVO 新增 phone 字段
- searchExpert/getGroupMemberList 填充手机号
- API文档-移动端接口.md 同步更新
2026-07-06 17:14:48 +08:00
pengjie 4cb3411eca feat(emergency): 新增移动端宣教详情接口,整理移动端API文档
- 新增 FirstAidResourceApiController,提供 GET /api/emergency/firstAid/resource/queryById
- RequestPrefix 新增 FIRST_AID_RESOURCE 路径常量
- API文档-应急群聊拉人.md 合并入新文档 API文档-移动端接口.md(含群聊拉人 + 宣教资源)
- API文档-急救宣教资源管理v3.md 保持管理后台接口
2026-07-06 15:45:55 +08:00
pengjie 9a022aa9cc docs(emergency): 补全应急群聊拉人API文档接口地址,添加网关前缀 /health-emergency 2026-07-06 15:24:43 +08:00
lianlonggang ebc3a026d1 style(consult): banner图片获取功能调整,每次修改nacos配置代价太大了,改成缓存配置地址 2026-07-01 10:59:18 +08:00
lianlonggang 29c2be8856 style(watch): 睡眠接口午睡(短睡)兼容问题处理 2026-07-01 10:42:14 +08:00
lianlonggang 9713b242e8 style(watch): 睡眠接口午睡(短睡)兼容问题处理 2026-06-30 16:21:06 +08:00
pengjieandClaude bbb0d5b0f8 chore(emergency): 修正部署SQL,同步实体字典引用
- STATUS DEFAULT 改为 0(草稿)
- PARENT_ID 注释修正
- 删除 first_aid_resource_status/first_aid_category_type 字典
- 同步移除实体 @Excel 注解中对应 dicCode

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-26 15:38:08 +08:00
pengjieandClaude 3c5bfc8ad1 fix(emergency): 修复宣教模块3个状态机与兼容性缺陷
- auditStatus 新增 insertStrategy=IGNORED,修复新增/复制时存0而非NULL
- submitForReview 增加PENDING重复提交拦截
- recordRead/toggleFavorite 重复键异常catch兼容DM8数据库

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-26 15:09:22 +08:00
pengjieandClaude 5612fe0752 refactor(emergency): 宣教模块代码整理与问题修复
- 修复 GlobalUtils.getLoginUser() NPE 风险(send/approve/reject)
- 批量操作改为前置校验+全量执行(batchPublish/batchTakeDown/batchDeleteResources)
- 新增 deleteResource/batchDeleteResources,已发布资源不可删除
- 标签过滤 SQL 修复边界精确匹配(避免 id=2 误匹配 12/22)
- 收藏/阅读改用 DuplicateKeyException 消除并发竞态
- fillResourceExtras/queryResourceList 批量查询分类名称,避免 N+1
- 新增 validateResource 校验内容类型、分类、标签合法性
- copyResource 补全 effectiveTime/expiryTime、版本记录、校验
- 审核下发/下架/删除增加状态机保护
- send() 使用 REQUIRES_NEW 事务隔离,确保批量推送单条失败互不影响
- 版本历史上限控制 MAX_VERSIONS=20
- 分类/标签树形重构:分类不再嵌套子分类,统一为分类+标签两级
- 清理废弃方法(listCategories/listAllCategoriesAndTags 等)
- 批量删除分类/标签增加 @Transactional 事务保护

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-26 11:23:57 +08:00
pengjieandClaude 3d8b930d1a chore(emergency): 代码整理
Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-25 17:37:17 +08:00
pengjie 8761a965aa feat(emergency): 急救宣教资源推送到应急群聊 2026-06-25 15:53:22 +08:00
pengjie 3f63abe1e3 Merge remote-tracking branch 'origin/xc' into xc 2026-06-25 15:50:57 +08:00
pengjieandClaude 2cc0668391 feat(emergency): 急救宣教资源管理v3 - 审批上架分离、分类标签树形重构
- approve()不再自动发布,需单独调用publish()上架
- publish()增加auditStatus=11前置校验
- batchPublish()跳过未审核通过的资源
- 分类管理支持树形结构(listRoot/listChildren/listAll)
- listAll返回分类+tags嵌套树
- listChildren返回子分类+标签
- 标签parentId改为必填
- 分类删除校验标签存在
- 分页列表填充resourceCount
- 审核记录按审核时间排序

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-25 15:50:24 +08:00
pengjie c8c3598edd fix(watch): 修复DM8不兼容的睡眠统计SQL函数
WatchStatUserInfoDaySleepMapper.xml 的 insertStat/updateStat 使用了
MySQL 专属的 timediff 和 time_to_sec 函数,DM8 报
"Member access [TIMEDIFF] unresolved"。改用 TIMESTAMPDIFF(SECOND,...)
兼容 DM8。

异常被 DataStepListener.handleStat 吞掉,导致任务显示成功但睡眠
数据从未写入 QH_WAT_STAT_USER_DAY_SLE,且 statCommon 循环中断使
后续 watchNo 的 updateLatestSleep 也不会执行,QH_WAT_DATA(type=4)
长期无值,后台列表睡眠列不显示。
2026-06-25 15:39:41 +08:00
pengjie 532041ff8d feat(emergency): 急救宣教资源管理模块多项优化
- 新增版本历史isCurrent标记,编辑/回滚时正确维护当前版本
- 审核记录独立表(QH_EME_FIRST_AID_RESOURCE_AUDIT),保留完整审核轨迹
- 审计表auditStatus沿用10/11/12,资源表保留冗余快照
- 标签挂载到分类下(parentId),同分类内名称唯一
- 分类/标签编辑时校验name唯一性
- 导出状态改用文本(statusText),审核通过+已下架正确显示
- 审核状态/驳回原因支持null更新(FieldStrategy.IGNORED)
- Controller POST接口统一使用@RequestBody接收参数
2026-06-24 17:36:44 +08:00
pengjie c6d12c9c68 fix(watch): 添加设备时校验watchNo唯一性,避免数据库唯一约束冲突 2026-06-24 11:00:48 +08:00
pengjie 6384021c4d feat(emergency): 急救宣教资源管理模块
新增急救宣教资源管理完整功能,包含6个子模块:

- 分类管理:树形结构,CRUD + 级联删除校验
- 标签管理:扁平结构,CRUD + 引用校验
- 资源管理:CRUD + 状态流转(草稿→审核→发布→下架) + 批量操作 + 阅读去重 + 复制
- 审核管理:待审核/已通过/已驳回列表 + 审核操作 + 审核记录
- 收藏管理:收藏/取消切换 + 状态查询 + 我的收藏列表
- 版本管理:编辑自动保存版本快照 + 回滚 + 差异对比

业务规则:
- 编辑/回滚后自动退回草稿,清除审核状态(方案A)
- 阅读去重:同用户重复阅读不增加计数
- 逻辑删除 + 租户隔离

Co-Authored-By: Claude <noreply@anthropic.com>
@
2026-06-23 17:40:30 +08:00
lianlonggangandClaude 74ebe26a6c feat(watch): 关爱人员统计新增期初未佩戴人数列
新增关爱人员未佩戴人数(仅计入统计日期起期已绑定的人数)列,
通过 bind_date <= 统计起始日期 条件过滤,仅计入统计起期已绑定但未佩戴的关爱人员。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-18 17:25:13 +08:00
lianlonggang 7acee51892 style(system): 登录后更新部门信息功能调整 2026-06-12 11:49:44 +08:00
lianlonggang 0b6b5856d1 style(watch): 关爱人员标记及相关统计功能调整 2026-06-12 11:49:00 +08:00
lianlonggang 7775f6eef4 提交一次配置文件 2026-06-03 10:27:39 +08:00
lianlonggang 99f150be25 feat(watch): 手表设备导出新增用户单位和用户部门字段 2026-05-25 14:08:23 +08:00
lianlonggang eb33cc2408 fix(watch): 修复DM8不兼容的EXTRACT时间差计算
- WatchDataMapper.xml: insertSleep/updateSleep 替换 EXTRACT(H/M FROM interval) 为 TIMESTAMPDIFF
- WatchDataWorkoutMapper.xml: 4处运动时长计算替换为 TIMESTAMPDIFF(SECOND, ...)
- WatchDeviceMapper.xml: 设备看板运动时长字段替换为 TIMESTAMPDIFF
- WatchMapper.java: getDataSteps 去掉 MAX 聚合避免 GROUP BY 报错
2026-05-25 14:07:36 +08:00
wanghao d11d605418 修改用户睡眠列表报错 2026-05-21 17:40:12 +08:00
lianlonggang 2d3f3670e0 style(watch): 统一代码格式,清理多余空行
- 规范 WatchMapper.xml 文件缩进与空行
- 规范 WatchServiceImpl.java 导入语句格式
2026-05-15 10:36:44 +08:00
lianlonggang b4111900f1 feat(watch): 手表数据统计与设备管理功能增强
- WatchDataItemVO 新增 Date 类型字段及 JsonFormat 格式化支持
- DeptStatisticDataController 部门统计数据查询逻辑优化
- IWatchDeviceService 接口扩展新业务方法声明
2026-05-14 18:48:11 +08:00
lianlonggang 74310b095c style(watch): 清理代码格式,移除多余空行
统一 health-watch-api 模块中多个文件的代码风格,
删除冗余空行,保持代码整洁一致。

涉及文件:
- DepartStatTransfer.java
- DeptData.java
- DeptStatData.java
- WearingDetailTransfer.java
2026-05-14 10:00:25 +08:00
lianlonggang 61f26973ae fix(watch): 上线修复BUG 2026-05-10 03:54:25 +08:00
lianlonggang 270c9a52f9 fix(watch): 最近一次代码合并 2026-05-09 20:23:20 +08:00
lianlonggang 86fa43c284 fix(watch): 修复手表设备绑定时部门归属逻辑错误
- 修改异步导入服务中手表设备的部门归属逻辑,以用户部门为准而非设备原有部门
- 为部门统计数据服务添加详细的JavaDoc注释说明业务逻辑
- 优化部门统计数据显示逻辑,支持一级、二级、三级部门的正确展示
- 移除手表绑定时的单位限制校验,允许跨单位绑定
- 在设备绑定时根据员工信息更新手表的单位组织编码
- 修复上传工具类中tar/apk文件上传的日志描述
- 调整Shiro配置中文件下载路径的安全策略
2026-05-06 14:59:19 +08:00
lianlonggang 42c7d223c9 feat(file-upload): 集成新疆油田OSS文件上传功能并优化设备管理
- 集成XjOssUtil实现新疆油田平台OSS文件上传下载功能
- 添加XjOssConfig配置类从Nacos读取OSS相关配置信息
- 实现OAuth2 client_credentials鉴权机制并缓存token到Redis
- 支持.tar文件强制使用MyUpload上传,其他文件根据oss_flag配置选择上传方式
- 在WatchDeviceController中禁用移动端解绑手表功能并添加删除设备时的绑定检查
- 添加/sys/upload/down/**和/sys/upload/show/**匿名访问权限支持文件下载预览
- 实现文件下载预览接口支持IOSP平台文件服务并添加安全校验机制
- 在CareUserDetailsParam中添加年份字段默认为当前年份
2026-04-21 17:11:48 +08:00
lianlonggang 15dfd6a9c7 fix(doctor): 修正医生满意度统计中的回复数计算逻辑
- 排除音视频会话数,只统计文字回复数量
- 回复数计算结果取非负值避免出现负数
- 24小时回复率统计不再包含音视频会话

fix(watch): 修复手表设备绑定时的组织代码比较逻辑

- 修正绑定验证时使用正确的组织代码字段
- 防止因字段引用错误导致的绑定权限检查失败

refactor(watch): 将警告开关异步保存以提高系统稳定性

- 使用异步任务执行外部系统调用
- 避免外部系统异常影响主业务流程
- 解绑非必要操作以增强系统容错性
2026-04-08 17:17:18 +08:00
lianlonggang 10d7a2f908 refactor(consultation): 为实体类添加缺失的 @TableId 注解并规范导入顺序
- 部分自增主键注解有问题
- 更新信创后置sql
2026-04-03 13:52:06 +08:00
lianlonggang 98a06d0c66 fix(watch,consultation,db): 修复达梦数据库兼容性及 SQL 脚本问题
- 将 ArchivesBodyTypeMapper.xml 中的双引号字符串 \"5\" 改为单引号 '5',修复达梦数据库语法兼容问题
- 更新 ConDoctorMapper.java(具体变更见 diff)
- 更新 consult.sql 数据库脚本
2026-04-02 17:46:15 +08:00
lianlonggang a7a037daf9 feat(consultation,emergency): 修复达梦数据库字段别名及资源等级查询问题
- ConResourceMapper.xml 查询中新增 RESOURCE_LEVEL 字段别名映射,解决达梦数据库大写字段名导致的 level 字段无法正确返回问题
- AedEquipmentMangerServiceImpl 相关逻辑优化
2026-04-01 14:43:03 +08:00
hejiayang cb5bbb1021 fix: 解决用户搜索中HTML转义字符显示问题(漏) 2026-03-31 14:12:37 +08:00
hejiayang a7cafe9d7f fix: 解决HTML转义安全问题
- 在多个DTO类中添加HtmlUtil导入并实现自动转义功能
- 为AccountDTO、ConDoctorDTO等类的set方法增加HTML转义处理
- 修复ConDoctorServiceImpl中的重复转义代码
- 为EmergencyProfessionUserController添加请求参数转义
- 修复OrderServiceImpl中的用户名称转义处理
- 为Watch相关服务增加数据绑定和查询时的转义处理
- 修复SysUser相关控制器和服务中的转义逻辑
2026-03-30 17:39:21 +08:00
hejiayang f273609f95 fix(system): 移除 Vue3 菜单切换功能
- 注释掉 LoginController 中的 switchVue3Menu 接口方法
- 注释掉 SysPermissionMapper 中的数据库表重命名操作
- 注释掉 SysPermissionServiceImpl 中的菜单切换实现逻辑
2026-03-30 11:28:48 +08:00
hejiayang f1abb06ae8 fix(schedules): 解决重复排班数据问题
- 根据时间查询现有排班记录并删除重复数据
- 在保存新排班前清理已存在的相同时间段排班
- 同步删除关联的排班项目记录确保数据一致性
2026-03-30 09:52:55 +08:00
hejiayang c6338a8d45 fix(wearing): 修复佩戴详情部门缓存空指针异常 2026-03-27 18:14:25 +08:00
hejiayang 66795541c0 fix(aed): 维护映射中的状态字段引用错误(如果对应的数据库字段,双引号内大小写要与数据库一致)
- 移除AedEquipmentMaintainMapper.xml中status字段的双引号引用
- 移除WatchDataMapper.xml中type字段的双引号引用
- 确保数据库字段引用的一致性,避免SQL语法错误
2026-03-27 17:33:34 +08:00
hejiayang 0fed763499 fix(mapper): 修复资源查询中的字段名大小写问题
- 修正 ConResourceMapper.xml 中 level 字段的大小写为 LEVEL
- 确保数据库查询条件匹配正确的字段名称
2026-03-27 17:14:11 +08:00
hejiayang 786b053703 fix(export): 修复导出会话数据时日期范围查询问题
- 在结束日期后添加时间戳 23:59:59 以确保包含完整当天数据
- 解决了日期范围查询可能遗漏最后时刻记录的问题
- 统一了两处相同逻辑的日期处理方式
2026-03-27 16:42:58 +08:00
wanghaoandClaude Sonnet 4.6 fda24ef418 fix(健康咨询资源): 修复 DM8 中 desc 字段保留字导致的查询异常
将数据库字段 DESC 重命名为 INTRODUCTION,并通过
@TableField(property = "\"desc\"") 使 MyBatis-Plus 生成
带引号的 SQL 别名,避免 DM8 解析 AS desc 时报语法错误

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 15:38:50 +08:00
wanghao 5a870bb767 修改system兼容性bug 2026-03-27 14:11:17 +08:00
wanghao 3005d8d80a 修改手表数据权限sql 2026-03-27 11:31:58 +08:00
wanghao fc347944a0 手表绑定增加只能绑定本单位验证 2026-03-27 10:58:40 +08:00
wanghao 7aa25f5f3a 修正手表数据权限规则部分代码部分 2026-03-27 10:57:17 +08:00
lianlonggang 4a139d9240 SQL脚本修正 2026-03-27 09:45:46 +08:00
lianlonggang 409b5bdfa0 SQL修正 2026-03-25 19:08:49 +08:00
hejiayang de4b503376 fix(watch): 漏合并 2026-03-24 16:18:19 +08:00
hejiayang a3bf6a2063 fix: 3月3日后develop分支代码迁移至xc分支 2026-03-24 12:02:26 +08:00
hejiayang 0389fc8390 fix(consultation):
bug修改, 字段删除
2026-03-23 15:27:33 +08:00
hejiayang 344cd1ff5e Merge remote-tracking branch 'origin/xc' into xc 2026-03-20 08:50:04 +08:00
hejiayang 412e66b3dd fix:
代码回滚
2026-03-20 08:46:55 +08:00
lianlonggang 0b4f4a4d74 SQL核对修正 2026-03-19 19:38:44 +08:00
wanghao 53d48fdd06 Merge branch 'xc' of https://gitea.rk-health.com/renkang/xj-platform into xc 2026-03-19 17:33:39 +08:00
wanghao e2bac530e2 refactor(系统): 将各 Controller 权限码由大写表名格式统一改为小写下划线格式
涉及 16 个 Controller 的 @RequiresPermissions 注解,将权限码从
system:QH_XXX_TABLE:action 格式统一规范为 system:snake_case:action 格式
2026-03-19 17:33:22 +08:00
hejiayang 307cc42437 Merge remote-tracking branch 'origin/xc' into xc 2026-03-19 15:29:48 +08:00
hejiayang f7f27c34c9 fix(consultation):
姓名筛选sql调整(·特殊符号执行时查询不出结果)
2026-03-19 15:28:44 +08:00
wanghao a975e9d223 Merge branch 'xc' of https://gitea.rk-health.com/renkang/xj-platform into xc 2026-03-19 14:34:08 +08:00
wanghao 96e82801e0 fix(系统): 修复达梦数据库 SQL 别名缺少双引号导致的兼容性问题 2026-03-19 14:30:51 +08:00
wanghao 86190b949f fix(健康咨询): 移除定时任务中 clientSign 参数解析逻辑,修复方法签名及缩进 2026-03-19 14:30:33 +08:00
hejiayang 2214aee8b4 fix(watch):
- 定时任务数据库配置从 cqyt_watch_prod/cqyt_system_prod 更新为 XJ_WATCH/XJ_SYSTEM;
- 定时任务sql表名替换
- 修复了SQL 查询中的逻辑连接符问题
2026-03-19 14:22:03 +08:00
hejiayang b0016073e3 fix(watch):
表名调整
2026-03-19 11:06:06 +08:00
hejiayang d3f14ed824 fix(watch):
表名调整
2026-03-19 10:10:51 +08:00
hejiayang b7906db202 fix(emergency):
mapper.java注释中漏改表名
sql脚本备注
2026-03-19 09:53:57 +08:00
hejiayang 04b00de86e fix(watch):
部分sql别名双引号回退
2026-03-19 09:29:36 +08:00
hejiayang 25643710e3 fix(sql):health-emergency和health-watch模块
部分sql别名双引号回退
2026-03-18 18:11:46 +08:00
hejiayang bac673f2e8 Merge remote-tracking branch 'origin/xc' into xc 2026-03-18 16:42:45 +08:00
hejiayang 01982703f4 fix(sql):health-emergency和health-watch模块
处理sql别名
2026-03-18 16:42:29 +08:00
lianlonggang d1e2f707ae 应急遗漏表名修正 2026-03-18 16:08:55 +08:00
lianlonggang 156ccde751 返回值为MAP的,用双引号使用别名 2026-03-18 15:16:26 +08:00
wanghao f5d1090479 Merge branch 'xc' of https://gitea.rk-health.com/renkang/xj-platform into xc 2026-03-18 14:09:25 +08:00
wanghao 21f801019a fix(System服务): 修复安眼用户信息同步任务中生日字段类型转换异常
- 将身份证号提取的生日字符串转换为 Date 对象
- 解决达梦数据库中 Druid 类型转换异常问题
2026-03-18 14:07:49 +08:00
hejiayang 961dc7c104 fix(watch-job):
空值报错处理: 在SyncUserStatJob中添加空值检查
数据库规范: "limit 1"替换为"FETCH FIRST 1 ROWS ONLY"
2026-03-18 13:49:08 +08:00
lianlonggang 7d3ee66a4b 部分调整 2026-03-17 18:55:14 +08:00
lianlonggang 2c9d1d8781 屏蔽暂不需要的打包插件 2026-03-17 17:35:36 +08:00
wanghao bc941755b3 Merge branch 'xc' of https://gitea.rk-health.com/renkang/xj-platform into xc 2026-03-17 13:47:37 +08:00
wanghao 7f27749cdf sql(System服务): 新增权限规则表字段名和表名标准化更新脚本
- 更新手表设备权限规则中的字段名为大写(dept_id → DEPT_ID)
- 更新手表数据权限规则中的字段名为大写(orgcode → ORGCODE)
- 共涉及4条权限规则的更新
2026-03-17 13:47:09 +08:00
hejiayang 618020de3a fix(watch): 消息通知列表bug修改 2026-03-16 18:09:49 +08:00
hejiayang f4598505c2 fix(watch): 年查询bug修改 2026-03-16 15:57:49 +08:00
lianlonggang 858e23a311 咨询模块及IM模块调整 2026-03-13 17:32:47 +08:00
lianlonggang b5c87ed3ff 咨询模块调整 2026-03-13 17:17:35 +08:00
wanghao ff63db7081 sql(System服务): 新增System服务数据库信创整理脚本
整合20260306和20260311删表语句及20260310表名修改语句
执行顺序:先删除130张无用表(原表名),再修改76张表为QH前缀
2026-03-13 11:59:50 +08:00
hejiayang 04c2a2218a fix(emergency,watch): 更新数据库表名前缀为标准化命名
更新health-emergency和health-watch模块中的所有实体类和XML映射文件以及mapper文件注解sql,
将数据库表名从原有命名格式统一更改为带项目前缀的标准化格式,
如QH_EME开头用于应急健康模块,QH_WAT开头用于健康监测模块
2026-03-13 11:16:02 +08:00
hejiayang c488d5c7ab fix(db): 有歧义表名修改 2026-03-13 11:08:16 +08:00
hejiayang e3f9545462 feat(db): 新增应急模块和手表模块SQL脚本
1.新增emergency.sql文件,包含应急模块36张表的重命名操作,更新达梦数据库保留字段名BEGIN、END、MODEL为BEGIN_TIME、END_TIME、MODEL_NUMBER
2.新增watch.sql文件,包含手表模块47张表的重命名操作,统一前缀从XJ_WATCH调整为QH_WAT,便于后续批量管理
2026-03-13 10:52:12 +08:00
hejiayang 065dc6e7db Merge remote-tracking branch 'origin/xc' into xc 2026-03-12 17:54:18 +08:00
lianlonggang 073efdd34f 信创sql整理 2026-03-12 17:03:50 +08:00
wanghao bc40b561dc fix(权限管理): 移除租户包权限查询逻辑解决达梦数据库表不存在问题
由于 QH_SYS_TENANT_PACK_PERMS 等租户相关表已被删除,
从 queryByUser 查询中移除租户包权限判断逻辑,
仅保留角色权限和部门角色权限查询
2026-03-12 16:56:45 +08:00
hejiayang 886c9d1dbc fix(emergency):
更新实体类中的数据库表名@TableName
更新相关XML映射文件中的表名引用,确保查询语句与新的表名保持一致。
更新mapper文件注解中表名
2026-03-12 16:06:02 +08:00
wanghaoandClaude Sonnet 4.6 b9ea233a45 sql(system服务): 新增无用表删除脚本
新增 20260311-system无用表删除.sql,包含 18 张无业务遗留表的
删除语句,提供原表名和 QH_ 命名规范两份版本,按模块分组排列。
暂缓删除 QH_SYS_QUARTZ_JOB 和 QH_SYS_TENANT,待确认启动依赖。

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-11 17:52:45 +08:00
hejiayang 670f36a57b Merge remote-tracking branch 'origin/xc' into xc 2026-03-11 09:42:13 +08:00
hejiayang 3733d442cc feat(watch): 支持Oracle数据库兼容性改进
- 将MyBatis查询中的"LIMIT 1"替换为"FETCH FIRST 1 ROWS ONLY"
- 在XML映射文件中将ROWNUM用于分页查询以提高Oracle兼容性
- 将MySQL特定函数转换为Oracle兼容语法:
  - YEAR()替换为EXTRACT(YEAR FROM)
  - TIMESTAMPDIFF()替换为EXTRACT(HOUR/MINUTE/SECOND FROM) - IFNULL()替换为NVL() - CURDATE()替换为SYSDATE
- 更新复杂查询语句使用ROW_NUMBER()窗口函数替代LEFT JOIN
- 修改表名引用从反引号到双引号以提高数据库兼容性
2026-03-11 09:41:56 +08:00
wanghaoandClaude Sonnet 4.6 061749cfb3 refactor(系统服务): 完成达梦数据库表名迁移至 QH_ 命名规范
- 更新 76 个实体类的 @TableName 注解,映射到新 QH_XXX_XXXXX 表名
- 批量替换 jeecg-system 和 jeecg-boot-base-core 中 175 个文件的
  硬编码旧表名,统一使用新 QH_ 前缀命名
- 新增 20260310-修改system数据库表名.sql,包含 76 条 ALTER TABLE
  RENAME TO 语句(分模块分组)
- 追加 5 条废弃表 DROP TABLE 语句至 20260306-信创system删表语句.sql
- 更新达梦数据库兼容性问题报告,第六节调整为 76 行,
  第八节新增 8.13 废弃表分组,合计 111 张表

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-10 16:40:09 +08:00
lianlonggang 0b403a559e refactor(consultation): 优化代码格式和空行规范
- 统一各实体类、Mapper 和 XML 文件的代码格式
- 规范化导入语句和类声明之间的空行
- 调整 ConSessionView、ConCostStatistics 等实体类格式
- 优化 Mapper 接口和 XML 映射文件的空行结构
- 提升代码可读性和一致性
- 去掉view及_qh表依赖
2026-03-09 10:55:55 +08:00
lianlonggang 8fd4cd9253 fix(consultation): 修复达梦数据库关键字冲突问题
- 将 SQL 中的 `type` 字段使用双引号转义为 `\"TYPE\"`
- 解决达梦数据库保留关键字导致的查询异常
- 影响模块:
  - ConDoctorSchedulingMapper.xml
  - ConDepartmentMapper.xml
2026-03-09 10:53:35 +08:00
hejiayang 10d5e6ca4a ```
feat: 添加MySQL驱动依赖支持Spring Batch功能

添加了MySQL连接器依赖以支持Spring Batch数据源兼容性,同时排除了Quartz自动配置避免定时任务冲突。```
2026-03-09 09:24:49 +08:00
hejiayang d98884ea4c fix(emergency): 修复mapper注解中SQL函数兼容性问题
- 将LEFT函数替换为SUBSTR函数以提高数据库兼容性
- 将now()函数替换为SYSDATE以确保达梦数据库兼容性
2026-03-06 16:26:59 +08:00
hejiayang bab238daae fix(health-emergency):修正数据库字段映射大小写问题
修改TblQimoNotice实体类中的@TableField注解,
将beginTime和endTime字段名统一改为大写BEGIN_TIME和END_TIME,以匹配数据库实际字段命名规范。
2026-03-06 16:11:02 +08:00
hejiayang 5233e6cb4b fix(mapper):修复SQL映射文件中的语法错误并优化查询性能
- 将反引号改为双引号以适配达梦数据库语法
- 替换MySQL特有函数为标准SQL函数(concat改为||操作符)
- 使用SYSDATE函数替代NOW函数
- 使用EXTRACT函数替代YEAR/MONTH函数提高兼容性
- 使用TRUNC函数替代DATE函数进行日期比较
- 将LIMIT语法改为FETCH FIRST语法(service):优化分页查询逻辑提高数据获取稳定性- 将LambdaQueryWrapper的last("limit1")改为标准分页查询
2026-03-06 16:03:14 +08:00
wanghaoandClaude Sonnet 4.6 224dae23ee fix(信创): 修复 Java Mapper @Select 注解中 MySQL 不兼容语法
将 5 个 Mapper 接口 @Select 注解内的 MySQL 特有语法替换为达梦兼容写法:
- LIMIT 1 → FETCH FIRST 1 ROWS ONLY(UserDataRecordBmiMapper × 2、
  UserDataRecordBWHMapper、UserDataRecordBloodAboMapper、
  UserDataRecordBloodPressureMapper)
- NOW() → SYSDATE(HealthHolidayMapper)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 13:34:58 +08:00
wanghao 5f790c0c7e fix(信创): 修复 Java 代码中 QueryWrapper.last() 拼接 LIMIT 语法与达梦不兼容问题
- LoginController: last("limit 1") → FETCH FIRST 1 ROWS ONLY
- WeighingDeviceServiceImpl: last("limit 1") → FETCH FIRST 1 ROWS ONLY
- SmsSendMsgHandle: last("limit "+n) → FETCH FIRST n ROWS ONLY
- HealthUserEmployeeExServiceImpl: 共 14 处 last("LIMIT 1") → FETCH FIRST 1 ROWS ONLY
- SysAnonServiceImpl: last("LIMIT 1 OFFSET 0") → FETCH FIRST 1 ROWS ONLY
2026-03-06 11:36:43 +08:00
wanghao 890cdfe40d fix(信创): 修复 Mapper XML 中 MySQL 特有语法与达梦数据库不兼容问题
- UserDataRecordBmiMapper.xml: DATE() → TRUNC(),LIMIT 1 → FETCH FIRST 1 ROWS ONLY
- SysVersionInfoMapper.xml: limit 1 → FETCH FIRST 1 ROWS ONLY
- SysUserMapper.xml: limit 1 → FETCH FIRST 1 ROWS ONLY,IFNULL() → NVL(),INSERT IGNORE → INSERT INTO
2026-03-06 11:36:19 +08:00
wanghao 909ccacaeb sql(信创): 新增达梦数据库无效表清理脚本,删除100张冗余表 2026-03-06 10:22:59 +08:00
wanghao 9b3021e847 Merge branch 'xc' of https://gitea.rk-health.com/renkang/xj-platform into xc 2026-03-06 09:54:28 +08:00
wanghao 264ca97055 refactor(缓存管理): 注释掉新疆项目中青海相关的用户和部门缓存逻辑 2026-03-06 09:51:38 +08:00
lianlonggang 9251e202f3 feat(db): 添加达梦数据库关键字转义拦截器
- 新增 DmKeywordInterceptor 拦截器,自动为达梦数据库关键字添加双引号转义
- 更新 AedEquipmentManger 实体类格式化和导入语句规范
- 支持自动识别和处理达梦数据库保留关键字,避免 SQL 语法错误
- 提升数据库兼容性,确保实体字段与达梦数据库关键字冲突时正常执行
2026-03-05 18:30:21 +08:00
wanghao 5885cd2ea0 xml修改 2026-03-05 14:47:55 +08:00
hejiayang d2a2f8291c 实体类数据库关键字注释 2026-03-05 11:15:20 +08:00
hejiayang a38f8adfc8 Merge remote-tracking branch 'origin/xc' into xc 2026-03-04 18:01:49 +08:00
hejiayang 4c98dd2d30 fix: 应急就医数据库迁移相关调整
1.启动类排除QuartzAutoConfiguration.class
2.数据库字段与达梦保留关键字段冲突调整
3.sql键名重复修改
2026-03-04 18:01:20 +08:00
wanghao b751f40bb4 systemxml改造 2026-03-04 14:59:45 +08:00
lianlonggang 61045e4389 chore(config): 更新 Nacos 配置连接信息
- 更新 Nacos 服务器地址从 localhost:8848 到 192.168.1.80:8848
- 更新认证信息(用户名: xjuser, 密码: Aa135790!123)
- 更新命名空间为 xjxc-space-common
- 新增服务发现命名空间配置 NACOS_NAMESPACE_DISCOVERY

涉及模块:
- health-consultation-start
- health-emergency-start
- health-watch-start
- jeecg-system-start
2026-03-04 09:30:06 +08:00
lianlonggang a3cb8a45ad chore(config): 标准化所有微服务的 application.yml 格式
- 统一 health-consultation、health-emergency、health-watch、health-system 四个模块的配置文件格式
- 规范 YAML 缩进和空行,提升配置文件可读性
- 保持配置内容不变,仅调整格式排版
2026-03-04 09:27:40 +08:00
lianlonggang fb5f142127 chore(deps): 移除达梦数据库 JDBC 驱动依赖并优化配置
- 删除 DmJdbcDriver8.jar 驱动文件
- 排除 QuartzAutoConfiguration 自动配置
- 统一规范 POM 文件格式和依赖管理
- 优化项目构建配置
2026-03-03 17:57:32 +08:00
wanghao 2c0d684079 信创改造 2026-03-02 15:13:07 +08:00
wanghao 98dc17552d 异常事件导出增加回传时间 2026-02-27 11:14:42 +08:00
lianlonggang e9784f7957 资源情况监测方法 2026-02-12 11:29:36 +08:00
wanghao 175238e291 调整移动端消息通知倒序 2026-02-11 16:42:30 +08:00
wanghao 3c822d581c 调整消息通知sql 2026-02-11 16:40:47 +08:00
wanghao 77d72d1d78 移动端通知接口优化 2026-02-11 15:33:14 +08:00
pengjie 9dbdd911d9 appbanner图 2026-02-11 11:03:33 +08:00
pengjie 0022497f64 appbanner图 2026-02-11 10:50:05 +08:00
pengjie 9714e97a71 appbanner图 2026-02-11 10:45:31 +08:00
wanghao 65f5755422 移动端首页通知 2026-02-11 10:07:27 +08:00
feng 677b4d5080 fix 2026-02-11 09:55:11 +08:00
wanghao 49f316dd82 移动端首页获取图片接口排除 2026-02-10 17:58:05 +08:00
pengjie b6aecf4e64 用户信息查询 2026-02-10 15:45:36 +08:00
pengjie cd7f5661de 【积分银行】积分 2026-02-09 17:03:19 +08:00
pengjie 6e005f2f4e 【积分银行】积分 2026-02-09 16:30:50 +08:00
wanghao 03f8be4c98 获取指定图片url 2026-02-09 16:23:33 +08:00
pengjie b2b96290bf Merge remote-tracking branch 'origin/develop' into develop 2026-02-06 17:49:52 +08:00
pengjie 80d1cebc0f 【积分银行】积分 2026-02-06 17:49:43 +08:00
wanghao e064e312da 消息通知的指定人员 2026-02-06 17:47:09 +08:00
pengjie 173eaa1b60 Merge remote-tracking branch 'origin/develop' into develop 2026-02-05 19:18:23 +08:00
pengjie ff4de69409 【轮播图】app轮播图 2026-02-05 19:18:15 +08:00
wanghao 6b13ab307a 消息通知 2026-02-05 19:18:12 +08:00
feng b57482b0be 健康银行表设计 2026-02-05 15:59:38 +08:00
feng 41ab299602 新微服务健康银行 2026-02-04 17:14:34 +08:00
wanghao e1eeafa13b 优化小助手端咨询列表接口 2026-02-03 10:46:46 +08:00
lianlonggang e535308048 修改包名,屏蔽短信日志 2026-02-02 19:50:20 +08:00
stan b03c4db189 update:es日志暂时去除 2026-02-02 19:14:35 +08:00
pengjie a6aebf34b4 【安眼】添加日志 2026-02-02 19:01:56 +08:00
pengjie 46c0c15bf2 【安眼】添加日志 2026-02-02 19:00:01 +08:00
pengjie e47a02f4cd 【安眼】缓存用户部门信息及登录逻辑 2026-02-02 16:14:42 +08:00
pengjie 6f404e466c Merge remote-tracking branch 'origin/develop' into develop 2026-02-02 16:10:39 +08:00
pengjie 40506c561c 【安眼】缓存用户部门信息及登录逻辑 2026-02-02 16:10:30 +08:00
pengjie c73c1089ee 【安眼】缓存用户部门信息及登录逻辑 2026-02-02 16:08:05 +08:00
lianlonggang fe014a56c8 屏蔽发短信日志 2026-02-02 12:44:46 +08:00
lianlonggang 24b1967ba9 日志文件下载功能,支持多实例日志 2026-01-29 19:36:12 +08:00
pengjie c9020ee8d3 【安眼】缓存用户token及用户登录信息 2026-01-27 10:23:34 +08:00
wanghao 1a9c557899 健康监测权限处理 2026-01-26 18:29:35 +08:00
feng 8eca7a0232 手表监测首页默认值 2026-01-26 09:08:00 +08:00
feng b580502f48 fix 2026-01-23 17:14:36 +08:00
feng 654a842ed9 fix 2026-01-23 16:57:32 +08:00
lianlonggang a58a007809 新疆短信内容中手机号码处理,避免含正常手机号的短信发不出去 2026-01-22 18:52:30 +08:00
feng fdd6fce15c 空对象处理 2026-01-22 14:47:05 +08:00
feng 3a8252e81d 安眼平台获取用户信息失败提示 2026-01-22 13:35:02 +08:00
feng 4f8ca1b97b 重构监测首页接口 2026-01-21 13:40:01 +08:00
feng 4ba67b70c5 暂时解决报错问题 2026-01-15 14:07:05 +08:00
wanghao 58acc58338 部门统计bug修复 2026-01-09 16:06:36 +08:00
pengjie 410bd6efa6 【应急单】定时解散群聊 2026-01-08 16:59:39 +08:00
pengjie a00d361f6b 【应急单】定时解散群聊 2026-01-08 16:59:01 +08:00
wanghao 464abc6521 部门统计bug修复 2026-01-08 15:19:46 +08:00
wanghao 7362ca6b8a 大屏医院咨询数量统计增加医院 2026-01-08 14:34:13 +08:00
lianlonggang 309bdc467e 短信发送任务log屏蔽 2026-01-08 13:48:01 +08:00
feng 33470b1a04 修改首页睡眠数据查询逻辑 2026-01-07 14:50:32 +08:00
wanghao 66b90d1c14 修改员工导出bug 2026-01-05 16:38:25 +08:00
wanghao de888296d9 修改员工导出bug及大屏睡眠人数 2026-01-05 15:22:16 +08:00
feng fb699c5227 睡眠接口修改 2026-01-05 13:46:38 +08:00
lianlonggang 60edd2726d 新疆短信内容中手机号码处理,避免含正常手机号的短信发不出去 2025-12-30 15:09:23 +08:00
feng 10c2b4737f fix 2025-12-29 15:56:59 +08:00
feng a6935d9f42 大屏 fix 2025-12-29 14:37:11 +08:00
feng ab7fe2a199 应急大屏开屏动画接口 2025-12-29 13:46:04 +08:00
feng 1495841ad2 加开屏动画表 2025-12-29 10:46:07 +08:00
wanghao 9f583d8850 大屏修改推送 2025-12-25 14:16:45 +08:00
feng 6381d7ab9c AED资源单位和部门分开显示 2025-12-25 13:57:16 +08:00
feng 34c3c59c0c 新疆需求整改 2025-12-25 11:16:29 +08:00
lianlonggang 7b473e5e36 AED导入功能调整,指定sheet数量,支持纯小数的高德坐标 2025-12-24 16:51:58 +08:00
pengjie f5247cc21f 【安眼】拦截器 2025-12-24 14:35:38 +08:00
feng 1c7ba176f6 新疆项目需求整改 2025-12-23 18:10:05 +08:00
feng e45f571cd2 重构手表绑定导入接口 2025-12-23 16:10:50 +08:00
feng 492e44bb80 手表日汇总数据bug修复 2025-12-19 19:33:13 +08:00
wanghao 66bb39bc18 设备导入修改及最新值 2025-12-18 17:25:00 +08:00
feng 5d66a58af9 fix 2025-12-18 14:28:49 +08:00
feng 9686399c80 修改监测数据时间统计维度 2025-12-17 17:49:36 +08:00
feng 19f6973a1f 数据查询过滤用户id为空的数据 2025-12-17 15:37:07 +08:00
feng bffd4fa7df fix 2025-12-17 14:14:26 +08:00
feng 86862ea0a7 心率异常数据去掉时间的默认值 2025-12-17 13:36:07 +08:00
wanghao da9169f774 设备导入修改及最新值 2025-12-17 13:21:42 +08:00
feng a972e556d9 静息心率数据修复 2025-12-16 17:45:13 +08:00
feng b093d95463 代码逻辑补全 2025-12-16 16:47:19 +08:00
feng a7bad9d444 异常心率列表 2025-12-16 15:42:23 +08:00
feng 9533c976f2 大屏手表监测数据统计逻辑修改 2025-12-16 10:11:41 +08:00
wanghao 79c8bd0da1 增加筛选 2025-12-15 18:03:46 +08:00
lianlonggang 7c337a40a7 高德地址转换 2025-12-15 16:18:28 +08:00
wanghao 3b25a26540 修改一键告警构建socket坐标 2025-12-15 15:59:17 +08:00
lianlonggang 428992ca6d 高德地址转换 2025-12-15 13:40:30 +08:00
wanghao a59914b95c 修改一键告警构建socket 2025-12-15 13:30:33 +08:00
wanghao 5c9cfbb7c3 短信bug修复 2025-12-15 11:11:15 +08:00
wanghao 9d157d17d5 大屏今日应急bug修复 2025-12-15 11:00:52 +08:00
lianlonggang f88bb0af52 高德地址转换 2025-12-15 08:55:43 +08:00
lianlonggang 117d9185f4 高德地址转换 2025-12-14 18:30:35 +08:00
wanghao 4d9051d5f9 大屏服务详情列表 2025-12-13 18:49:18 +08:00
feng 6c3047abf2 fix 2025-12-13 18:39:54 +08:00
wanghao 9b15cb694d 大屏websocket修改 2025-12-13 18:36:12 +08:00
wanghao 08b487accf 大屏websocket修改 2025-12-13 18:31:40 +08:00
wanghao 3bc6566f2d 大屏服务详情修改 2025-12-13 17:32:11 +08:00
wanghao fbf8b11d4f 大屏服务详情修改 2025-12-13 16:47:12 +08:00
feng f2d63353b0 sql文件更换目录 2025-12-13 16:10:40 +08:00
wanghao 707e41025c 大屏修改 2025-12-13 16:08:04 +08:00
feng 8f1d3fdf2a 大屏呼入电话数据调整 2025-12-13 15:15:55 +08:00
feng 24cd7cfdca 大屏关爱联络员暂时改为接口现查 2025-12-13 14:24:55 +08:00
wanghao 2ca6c5f1cd Merge remote-tracking branch 'origin/develop' into develop 2025-12-12 17:27:55 +08:00
wanghao 5e4f95c10d push 2025-12-12 17:27:43 +08:00
feng de69956a71 接口计算静息心率过滤空值 2025-12-12 10:57:32 +08:00
feng 7d316cdf47 修改提示 2025-12-10 16:13:02 +08:00
feng 9d6c2f2a14 全局替换小助手字样 2025-12-10 15:55:53 +08:00
feng 569e23322f 咨询服务名片消息已读接口 2025-12-10 15:02:14 +08:00
wanghao fe5ca6619c 新疆修改发送短信逻辑 2025-12-10 13:56:11 +08:00
feng f73a09e91c 临时修复首页睡眠数据bug 2025-12-09 09:56:28 +08:00
feng 9916e792db 去掉注册腾讯IM账号拼接小助手字样 2025-12-08 15:22:59 +08:00
wanghao 36bbbbf180 新疆体检相关 2025-12-05 15:06:21 +08:00
wanghao b8108c4908 新疆体检相关 2025-12-05 13:50:45 +08:00
feng 06236177a3 安眼接口加spring retry重试注解 2025-12-04 16:52:33 +08:00
pengjie 930b806a25 【安眼】信息同步修改 2025-12-04 15:05:50 +08:00
pengjie 0f6fc575d5 Merge remote-tracking branch 'origin/develop' into develop 2025-12-04 15:03:02 +08:00
pengjie 4446515656 【安眼】信息同步修改 2025-12-04 15:02:54 +08:00
feng 3c7bda80bb 修复bug , 新增aed导入的临时方法 2025-12-04 11:29:43 +08:00
pengjie f37963f141 【安眼】用户生日修复 2025-12-04 09:56:11 +08:00
wanghao 5d0dc81279 新疆大屏 2025-12-03 18:21:46 +08:00
pengjie 22626eb9d5 【安眼】用户信息同步 2025-12-03 18:20:26 +08:00
feng 0b5ec13aab 同步AED数据 2025-12-03 16:21:22 +08:00
wanghao 595d702e9e 新疆大屏 2025-12-03 15:53:18 +08:00
pengjie a4a0265a82 【安眼】用户信息同步 2025-12-02 18:09:07 +08:00
wanghao 430817655a 新疆大屏 2025-12-02 17:59:26 +08:00
feng 536ca94d8b Merge branch 'develop' of https://gitea.rk-health.com/renkang/xj-platform into develop 2025-12-02 17:54:35 +08:00
feng cb0d45c81b 通过安眼接口更新基础数据 2025-12-02 17:54:32 +08:00
pengjie a20771f420 【安眼】用户信息同步 2025-12-02 17:39:04 +08:00
pengjie 23b560ca01 【安眼】用户信息同步 2025-12-02 17:11:00 +08:00
feng cd6d48c22e 删除安眼重复接口 2025-12-02 13:35:59 +08:00
wanghao 5d6ab34fd2 Resource添加安眼id 2025-12-02 13:34:01 +08:00
feng ae4c5e7ee6 AED布点支持手动位置位置信息 2025-12-02 11:21:50 +08:00
feng e7669dc8bc 部分接口参数调整 2025-12-02 11:02:51 +08:00
wanghao 560fe0dc19 安眼接口api开发 2025-11-28 17:19:23 +08:00
wanghao 9086453544 新疆aed迁移修补sql 2025-11-28 14:37:52 +08:00
pengjie 8adaff4445 【应急就医】详情报错修复 2025-11-28 10:01:15 +08:00
wanghao 9942105af9 新疆aed迁移 2025-11-28 09:35:59 +08:00
wanghao 714bba6733 Merge remote-tracking branch 'origin/develop' into develop 2025-11-27 17:36:15 +08:00
wanghao f09b695903 新疆aed迁移 2025-11-27 17:36:05 +08:00
pengjie 019c7d600a 【im消息】下载地址替换,数据修复方法 2025-11-27 16:58:23 +08:00
pengjie 56697c1a78 【im消息】下载地址替换,数据修复方法 2025-11-27 16:30:31 +08:00
feng fe0e075220 暂未提交 2025-11-27 14:14:41 +08:00
wanghao ee7ff671b5 新疆aed迁移 2025-11-27 13:29:08 +08:00
lianlonggang d7d69755a7 咨询统计查询调整 2025-11-27 10:21:46 +08:00
lianlonggang 5f612993c6 短信发送调整 2025-11-27 10:21:16 +08:00
lianlonggang 105bf98d25 docker配置调整 2025-11-27 10:19:37 +08:00
wanghao 354d3c82a2 新疆aed迁移 2025-11-26 17:44:17 +08:00
SkyJourney 3d5f72a26b 添加Google Jib插件构建docker镜像 2025-11-26 17:21:52 +08:00
wanghao 551eaf50fb 手表部门周报默认值处理 2025-11-21 17:00:57 +08:00
feng 5aacb190ef 新增请求头 2025-11-20 15:22:19 +08:00
feng e975d62c17 安眼平台接口对接 2025-11-20 15:15:34 +08:00
pengjie ec67a5e064 Merge remote-tracking branch 'origin/develop' into develop 2025-11-12 09:58:23 +08:00
pengjie a1a9360d97 【应急单】参与人员子表数据修复sql 2025-11-12 09:58:16 +08:00
wanghao fba5870c50 专家处理 2025-11-12 09:48:11 +08:00
pengjie 7e310ba037 【统一认证】密码校验注释 2025-11-12 09:32:33 +08:00
pengjie 9e2ee4b81a Merge remote-tracking branch 'origin/develop' into develop 2025-11-11 18:08:44 +08:00
pengjie 0101561d12 【im】多方通话修改 2025-11-11 18:08:38 +08:00
wanghao 54edb2032d 专家处理 2025-11-11 17:33:17 +08:00
pengjie 34007bc02e Merge remote-tracking branch 'origin/develop' into develop 2025-11-11 16:36:15 +08:00
pengjie 348815ab68 【im】多方通话修改 2025-11-11 16:35:31 +08:00
wanghao e9be1437ce 专家处理 2025-11-11 16:01:02 +08:00
pengjie 729fcef675 【统一认证】组织更新叶子结点修改 2025-11-11 15:15:42 +08:00
pengjie 8780de32f2 【统一认证】组织更新叶子结点修改 2025-11-11 14:55:45 +08:00
pengjie 75986f785b Merge remote-tracking branch 'origin/develop' into develop 2025-11-11 14:51:02 +08:00
pengjie 40ab4b65dd 【统一认证】组织更新叶子结点修改 2025-11-11 14:50:55 +08:00
wanghao f2c6914fa9 专家处理 2025-11-10 19:25:09 +08:00
pengjie 92033a17cb 【应急】IM视频通话 2025-11-07 15:30:34 +08:00
pengjie 6d747c49fb 【登录】安眼统一认证 2025-10-31 18:01:43 +08:00
pengjie c3d68906ab 【登录】安眼统一认证 2025-10-31 17:42:53 +08:00
pengjie 5baa1b5479 【登录】安眼统一认证 2025-10-31 16:01:45 +08:00
pengjie 3016f15244 Merge remote-tracking branch 'origin/develop' into develop 2025-10-31 15:24:54 +08:00
pengjie c6bc9296e0 【登录】安眼统一认证 2025-10-31 15:24:45 +08:00
wanghao 8ee7c1f9d1 新疆新需求 2025-10-31 15:14:20 +08:00
wanghao 36c67c6c2a 新疆新需求 2025-10-31 10:53:57 +08:00
wanghao 15cbd2f085 新疆新需求 2025-10-31 10:35:54 +08:00
pengjie a18166d6a8 【登录】安眼统一认证 2025-10-30 15:14:59 +08:00
pengjie 0810aa1370 【登录】安眼统一认证 2025-10-30 13:34:26 +08:00
pengjie 137b4efa38 【登录】安眼统一认证 2025-10-30 10:57:18 +08:00
pengjie 051905a6f2 【登录】安眼统一认证 2025-10-30 10:14:51 +08:00
lianlonggang 4356433ee0 健康咨询大屏接口 2025-10-28 10:47:23 +08:00
lianlonggang e9774bc270 健康咨询大屏接口 2025-10-28 10:47:04 +08:00
feng 9f7882497b 应急工单新建表 2025-10-24 16:34:39 +08:00
feng 2a67051beb 修改跳转至安眼大屏鉴权参数 2025-10-14 13:38:15 +08:00
feng c808470275 去掉拿接口的缓存逻辑 2025-09-29 10:18:14 +08:00
feng 2b6e5d2736 安眼大屏公网访问获取token接口对接 2025-09-28 17:25:07 +08:00
lianlonggang 19e46db6a1 健康咨询大屏接口 2025-09-26 18:06:15 +08:00
feng b678b33734 咨询大屏医院过滤删除状态 2025-09-26 17:28:57 +08:00
feng 6d498f3b74 应急支持第二个专业人员加入的四方通话 2025-09-26 15:51:34 +08:00
wanghao f2503fdd52 修正移动端应急地图数据 2025-09-26 13:07:58 +08:00
wanghao eba9ee928f 修正今日应急的单位部门数据 2025-09-25 17:32:24 +08:00
wanghao 9d68bcd2ff 修正今日应急的应急数据 2025-09-25 16:27:43 +08:00
wanghao ff6e83f1c8 0919新需求开发 2025-09-25 13:58:00 +08:00
lianlonggang 273d1377b9 健康咨询大屏接口 2025-09-25 11:33:30 +08:00
lianlonggang 106863d986 健康咨询大屏接口 2025-09-25 11:21:40 +08:00
lianlonggang 688abc5703 健康咨询大屏接口 2025-09-24 17:40:11 +08:00
wanghao e9fada7bbd 新需求开发 2025-09-19 16:26:25 +08:00
wanghao 853eefb48c 新需求开发 2025-09-17 15:33:44 +08:00
wanghao 34c4e9accc 新需求开发 2025-09-17 15:16:37 +08:00
wanghao 8f73f46fcf 新需求开发 2025-09-17 14:37:07 +08:00
wanghao cd6b8e49e5 新需求开发 2025-09-17 14:30:53 +08:00
feng 3e4f5ebe3e 新加员工扩展信息 2025-09-17 14:16:18 +08:00
feng 2a7dde25ea 关爱人员预警 2025-09-17 10:42:25 +08:00
wanghao 59ade8db2b 修复大屏体检异常统计bug 2025-08-20 13:33:06 +08:00
stan a035d3f47e update:应急小组数量修改 2025-08-15 14:07:15 +08:00
wanghao 4e4cce85e8 配置安眼定时任务 2025-08-13 13:26:31 +08:00
wanghao 3a45a45f99 小屋数据迁移sql 2025-08-13 13:19:18 +08:00
wanghao 747c881b74 大屏改动 2025-08-13 11:20:53 +08:00
wanghao 23c53a782c 大屏改动 2025-08-13 10:34:55 +08:00
feng b327524823 Merge branch 'develop' of https://gitea.rk-health.com/renkang/xj-platform into develop 2025-08-08 11:05:39 +08:00
feng 63ce497551 同步油田医院数据 2025-08-08 11:05:36 +08:00
wanghao 49dd2287ad 大屏处理冲突 2025-08-08 10:41:01 +08:00
feng eeeaf9166b 替换部分vo的引用 2025-08-08 10:35:48 +08:00
feng 64b6c78b92 新增变量 2025-08-08 10:01:47 +08:00
feng a68409917e 冲突合并 2025-08-08 09:46:33 +08:00
feng 17a693f768 安眼接口对接 2025-08-08 09:45:42 +08:00
wanghao 2b2b070979 大屏修改 2025-08-08 09:34:18 +08:00
feng afa6942d77 安眼平台接口对接 2025-08-06 17:10:30 +08:00
lianlonggang e4d815ae54 应急数据调整 2025-08-03 23:16:24 +08:00
lianlonggang 476b3a00d5 应急数据调整 2025-08-03 23:10:38 +08:00
lianlonggang 7ea6dd3411 应急数据调整 2025-08-03 23:02:36 +08:00
wanghao dce89d699a 去除只接受新疆工号限制 2025-08-01 17:11:10 +08:00
wanghao 84e9334a20 修改qimo推送只接受新疆工号 2025-08-01 13:30:42 +08:00
wanghao 73aeba99b3 修改新疆大屏 2025-07-28 18:03:04 +08:00
wanghao 6372acdf2d 修改大屏异常信息经纬度 2025-07-28 17:09:03 +08:00
wanghao 180734119a 修改大屏异常信息经纬度 2025-07-28 17:04:03 +08:00
wanghao 6139c70e97 修改首页压力不显示bug 2025-07-28 15:22:41 +08:00
wanghao b182e6e59e 大屏假数据修改开发 2025-07-28 15:03:14 +08:00
wanghao 76cba3c817 大屏假数据修改开发 2025-07-25 17:18:25 +08:00
lianlonggang 36603cbda9 部分配置去掉 2025-07-23 16:37:49 +08:00
feng e334685b1d Merge branch 'develop' of https://gitea.rk-health.com/renkang/xj-platform into develop 2025-07-22 15:13:08 +08:00
feng f16b65982c 修改员工信息导入逻辑 2025-07-22 15:13:04 +08:00
wanghao 3684588a52 sql提交 2025-07-14 09:21:52 +08:00
lianlonggang 3169464045 新疆油田短信功能 2025-07-12 11:25:25 +08:00
feng df2b136b3e dockerfile old 2025-07-11 09:36:07 +08:00
feng d5cfc9e76f Merge branch 'develop' of https://gitea.rk-health.com/renkang/xj-platform into develop 2025-07-10 10:48:05 +08:00
feng d842bb1dbb 小助手自动回复 2025-07-10 10:47:51 +08:00
wanghao f28b1e755b 修改咨询档案列表bug 2025-07-10 10:30:22 +08:00
DESKTOP-BLB5287\FP e64091efde Merge branch 'develop' of https://gitea.rk-health.com/renkang/xj-platform into develop 2025-07-09 17:28:36 +08:00
DESKTOP-BLB5287\FP 34adc1a5e2 fix 2025-07-09 17:28:32 +08:00
wanghao ac42d99bd2 修改新疆大屏 2025-07-09 17:18:17 +08:00
DESKTOP-BLB5287\FP e19f3f57b9 Merge branch 'develop' of https://gitea.rk-health.com/renkang/xj-platform into develop 2025-07-09 16:57:10 +08:00
DESKTOP-BLB5287\FP 7c69912296 咨询bug 2025-07-09 16:57:07 +08:00
wanghao 75d0775ca9 修改新疆大屏及群内bug 2025-07-09 16:56:20 +08:00
wanghao ec897c0567 修改新疆大屏及小助手专家咨询量bug 2025-07-09 13:47:44 +08:00
stan 19addb4469 ADD:新版排版接口 2025-07-09 11:00:16 +08:00
wanghao 65b15b8dbd 新疆大屏开发及bug修改 2025-07-08 17:34:29 +08:00
wanghao 2b25e0bf99 新疆大屏开发及bug修改 2025-07-08 17:30:05 +08:00
wanghao 3a43716e9b 新疆大屏开发及bug修改 2025-07-08 16:49:47 +08:00
wanghao e93fb4de8d bug修改 2025-07-07 18:15:57 +08:00
wanghao 5a1d6d828b bug修改 2025-07-06 15:49:48 +08:00
wanghao c342a77af1 大屏开发 2025-07-06 14:08:36 +08:00
wanghao 3bd73a6eb8 修改群内bug 2025-07-05 17:53:09 +08:00
stan 58f859bcac add:方法名迁移 2025-07-05 11:32:02 +08:00
stan 7e83999711 add:假数据代码迁移 2025-07-05 11:31:39 +08:00
wanghao 7fb0ac6aa6 修改群内bug 2025-07-04 18:10:35 +08:00
wanghao 909c083e51 大屏 2025-07-04 14:31:20 +08:00
DESKTOP-BLB5287\FP e15aae5a26 删除青海的动态表名配置 2025-07-04 09:42:30 +08:00
DESKTOP-BLB5287\FP 2804df2af3 添加小助手同时给加角色 2025-07-03 15:22:59 +08:00
DESKTOP-BLB5287\FP c91978f42a dockerfile 2025-07-02 09:11:48 +08:00
DESKTOP-BLB5287\FP 68fd95a017 redis配置 2025-06-30 14:42:19 +08:00
1066 changed files with 44941 additions and 5549 deletions
+224
View File
@@ -0,0 +1,224 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
---
## 📌 重要提示
**在开始工作前,请务必先阅读 `skills.md` 文件,了解当前任务的具体约束和技术指南。**
- **CLAUDE.md** (本文件): 项目通用知识库,包含架构、技术栈、开发规范等基础信息
- **skills.md**: 当前任务技能包,定义特定任务的范围约束、禁止事项和专项技术指南
---
## 项目概述
新疆油田健康管理平台 - 基于 JeecgBoot 3.6.3.1-plus 框架的微服务架构后端系统。
## 技术栈
- **框架版本**: Spring Boot 2.7.18, Spring Cloud 2021.0.9, Spring Cloud Alibaba 2021.0.6.0
- **JDK**: Java 1.8
- **构建工具**: Maven 3.x
- **服务治理**: Nacos 2.3.2 (配置中心 + 服务发现)
- **持久层**: MyBatis-Plus 3.5.6, MyBatis-Plus Join 1.5.3
- **数据源**: 动态多数据源 (Dynamic Datasource 4.3.0), Druid 1.2.18
- **数据库支持**: 达梦DM8 (主要), MySQL, Oracle, PostgreSQL
- **认证授权**: Shiro 1.13.0, JWT
- **文档**: Knife4j 4.5.0
- **报表**: 积木报表 1.6.1
- **对象存储**: MinIO, 阿里云OSS, 七牛云
## 微服务模块
项目采用多模块微服务架构,各服务独立部署:
| 服务名称 | 模块路径 | 默认端口 | 说明 |
|---------|---------|---------|------|
| health-system | jeecg-system/jeecg-system-start | 7001 | 系统核心服务(用户、角色、权限、租户管理) |
| health-consultation | health-consultation/health-consultation-start | 7010 | 健康咨询服务 |
| health-emergency | health-emergency/health-emergency-start | - | 急救服务 |
| health-watch | health-watch/health-watch-start | - | 手表数据服务 |
| health-im | health-im/health-im-start | - | 即时通讯服务 |
| health-bank | health-bank/health-bank-start | - | 健康银行服务 |
## 核心架构特性
### 1. 多租户(SaaS)架构
- **实现方式**: MyBatis-Plus TenantLineInnerInterceptor 租户拦截器
- **配置位置**: `jeecg-boot-base-core/src/main/java/org/jeecg/config/mybatis/MybatisPlusSaasConfig.java`
- **租户字段**: 数据表通过 `tenant_id` 字段实现租户隔离
- **隔离范围**: 用户、角色、部门、字典、多数据源等系统模块
- **非隔离表**: 菜单表、租户表不做租户隔离
### 2. 动态多数据源
- **配置**: 支持运行时动态切换数据源
- **主要数据库**: 达梦数据库 DM8 (国产数据库)
- **注解切换**: 使用 `@DS("数据源名称")` 注解切换数据源
- **拦截器**: `DmKeywordInterceptor` 处理达梦数据库关键字转义
### 3. 安眼系统集成
- **模块位置**: `functional-modules/xj-anyan-api`
- **执行器**: `AnYanClientExecutor` 实现与安眼系统的API对接
- **功能**: 统一身份认证、用户数据同步、登录集成
### 4. 环境配置
每个微服务启动模块都包含独立的 `.env` 文件用于环境变量配置:
```
PROFILE_NAME=dev # 环境名称
SERVER_PORT=7001 # 服务端口
NACOS_SERVER_ADDR=192.168.1.80:8848 # Nacos地址
NACOS_USERNAME=xjuser # Nacos用户名
NACOS_PASSWORD=Aa135790!123 # Nacos密码
NACOS_NAMESPACE=xjxc-space-common # 配置命名空间
NACOS_NAMESPACE_DISCOVERY=workspace_lian # 服务发现命名空间
NACOS_GROUP=dev # 配置分组
```
**注意**: `.env` 文件包含敏感信息,不应提交到版本控制系统。
## 常用开发命令
### Maven 构建命令
```bash
# 全量编译打包(跳过测试)
mvn clean package -DskipTests
# 编译指定模块
mvn clean package -pl jeecg-system/jeecg-system-start -am -DskipTests
# 安装到本地仓库
mvn clean install -DskipTests
# 分包打包(依赖和应用分离,用于Docker部署)
mvn clean package -P divide-package -DskipTests
# 依赖漏洞分析
mvn clean package -P bom-analysis
```
### 启动服务
```bash
# 方式1: IDE直接运行启动类
# 例如: org.jeecg.JeecgSystemCloudApplication
# 方式2: 使用 spring-boot-maven-plugin 启动
cd jeecg-system/jeecg-system-start
mvn spring-boot:run
# 方式3: 打包后启动
java -jar jeecg-system/jeecg-system-start/target/jeecg-system-start.jar
```
### Docker 部署
```bash
# 构建 Docker 镜像(在各服务的 start 模块目录执行)
cd jeecg-system/jeecg-system-start
docker build -t health-system:latest .
# 运行容器
docker run -d \
-p 7001:7001 \
-e PROFILE_NAME=dev \
-e NACOS_SERVER_ADDR=nacos:8848 \
--name health-system \
health-system:latest
```
## 代码规范
### 包结构约定
```
com.renkang / org.jeecg
├── modules
│ └── [模块名]
│ ├── controller # 控制器层
│ ├── service # 服务层
│ │ └── impl # 服务实现
│ ├── mapper # 数据访问层
│ │ └── xml # MyBatis XML映射文件
│ ├── entity # 实体类
│ ├── vo # 视图对象
│ └── dto # 数据传输对象
```
### Mapper扫描路径
`MybatisPlusSaasConfig.java` 中配置了全局 Mapper 扫描:
```java
@MapperScan(value = {"org.jeecg.modules.**.mapper*", "com.renkang.**.mapper*", "com.jeecg.**.mapper*"})
```
### MyBatis XML 映射文件位置
- **方式1**: 放在 `src/main/resources/mapper``src/main/resources/mapper/xml`
- **方式2**: 与 Mapper 接口同目录,在 `src/main/java` 下 (已配置资源过滤)
### 多租户开发注意事项
1. 新增业务表时,需要添加 `tenant_id` 字段用于租户隔离
2. 如果某些表不需要租户隔离,需要在 `MybatisPlusSaasConfig``TenantLineHandler` 中排除
3. 租户ID由拦截器自动注入,业务代码无需手动设置
### 动态数据源使用
```java
@Service
public class UserService {
@DS("slave") // 切换到从库
public List<User> queryUsers() {
// ...
}
@DS("master") // 切换到主库
public void saveUser(User user) {
// ...
}
}
```
## 项目特定依赖
- **Aspose Words**: 文档处理库,用于生成Word报告
- **EasyExcel**: Excel导入导出
- **POI-TL**: Word模板引擎
- **极光推送**: 消息推送服务
- **微信小程序**: 小程序API集成
- **北郊工具**: 膳食摄入算法工具 (`yixiong-tools`)
- **重庆风险评估**: 健康风险评估工具 (`cqRisk`)
## 重要配置文件
- **根 pom.xml**: 定义所有依赖版本和Maven仓库配置
- **application.yml**: 各服务启动配置,从Nacos加载配置
- **.env**: 环境变量配置,控制Nacos连接和服务端口
- **Dockerfile**: 各服务的容器化配置
## 开发工具推荐
- **IDE**: IntelliJ IDEA
- **数据库工具**: DM数据库客户端 (达梦官方), Navicat (支持达梦插件)
- **API测试**: Knife4j Web UI (启动服务后访问 `/doc.html`)
- **服务监控**: Nacos 控制台
## 调试建议
1. 确保 Nacos 服务已启动并可访问
2. 检查 `.env` 文件中的 Nacos 配置是否正确
3. 在 Nacos 配置中心确认对应的配置文件存在 (`jeecg.yaml`, `health-system-dev.yaml` 等)
4. 查看日志目录 `logs/` 下的服务日志文件
5. 多租户问题排查: 检查请求Header中的 `tenant-id``X-Access-Token` (从Token解析租户ID)
+2
View File
@@ -0,0 +1,2 @@
ALTER TABLE "QH_HLT_USER_EMPLOYEE_EX" ADD "NON_CARE_FLAG" INT DEFAULT null;
COMMENT ON COLUMN "QH_HLT_USER_EMPLOYEE_EX"."NON_CARE_FLAG" IS '非关爱人员标记0-1-';
+12
View File
@@ -0,0 +1,12 @@
-- 手表用户绑定数据视图
-- 关联 QH_WAT_DEVICE QH_SYS_USER查询已绑定用户的手表设备信息
CREATE OR REPLACE VIEW V_WATCH_USER_BIND AS
SELECT
u.WORK_NO AS employee_num,
d.WATCH_NO AS watch_no,
u.REALNAME AS real_name,
d.BIND_DATE AS bind_date
FROM QH_WAT_DEVICE d
INNER JOIN QH_SYS_USER u ON d.BIND_USER_ID = u.ID
WHERE d.BIND_USER_ID IS NOT NULL
AND d.BIND_USER_ID <> '';
+200
View File
@@ -0,0 +1,200 @@
-- =====================================================
-- 急救宣教资源管理 线上部署 SQL达梦 DM8
-- 包含分类/标签资源用户行为版本历史审核记录推送记录
-- 日期: 2026-06-25
-- =====================================================
-- 1. 资源分类/标签统一表
-- TYPE: 1=分类(树形), 2=标签(扁平)
CREATE TABLE "QH_EME_FIR_AID_RES_CATE" (
"ID" VARCHAR2(32) NOT NULL,
"NAME" VARCHAR2(100) NOT NULL,
"TYPE" NUMBER(1) DEFAULT 1 NOT NULL,
"PARENT_ID" VARCHAR2(32),
"DESCRIPTION" VARCHAR2(500),
"SORT_NO" NUMBER(10) DEFAULT 0,
"STATUS" NUMBER(1) DEFAULT 1,
"CREATE_BY" VARCHAR2(50),
"CREATE_TIME" TIMESTAMP,
"UPDATE_BY" VARCHAR2(50),
"UPDATE_TIME" TIMESTAMP,
"DEL_FLAG" NUMBER(1) DEFAULT 0,
"MEMO" VARCHAR2(500),
PRIMARY KEY ("ID")
);
COMMENT ON TABLE "QH_EME_FIR_AID_RES_CATE" IS '急救宣教资源分类/标签';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."ID" IS '主键';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."NAME" IS '名称';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."TYPE" IS '类型:1=分类,2=标签';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."PARENT_ID" IS '父分类ID(根分类为NULL标签指向所属分类ID)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."DESCRIPTION" IS '描述';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."SORT_NO" IS '排序号';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."STATUS" IS '状态(1=正常,2=冻结)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."DEL_FLAG" IS '删除标记(0=正常,1=已删除)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_CATE"."MEMO" IS '备注';
CREATE INDEX "IDX_FA_CATEGORY_TYPE" ON "QH_EME_FIR_AID_RES_CATE"("TYPE");
CREATE INDEX "IDX_FA_CATEGORY_PARENT_ID" ON "QH_EME_FIR_AID_RES_CATE"("PARENT_ID");
-- 2. 资源表
CREATE TABLE "QH_EME_FIR_AID_RES" (
"ID" VARCHAR2(32) NOT NULL,
"TITLE" VARCHAR2(200) NOT NULL,
"SUMMARY" VARCHAR2(500),
"CONTENT_TYPE" VARCHAR2(32),
"CATEGORY_ID" VARCHAR2(32),
"APPLICABLE_SCENARIO" VARCHAR2(500),
"TAGS" VARCHAR2(2000),
"CONTENT" CLOB,
"COVER_IMAGE" VARCHAR2(500),
"FILE_URL" CLOB,
"EFFECTIVE_TIME" TIMESTAMP,
"EXPIRY_TIME" TIMESTAMP,
"AUDIT_STATUS" NUMBER(2),
"AUDIT_REJECT_REASON" VARCHAR2(500),
"AUDIT_BY" VARCHAR2(50),
"AUDIT_BY_ID" VARCHAR2(32),
"AUDIT_TIME" TIMESTAMP,
"VIEW_COUNT" NUMBER(10) DEFAULT 0,
"FAVORITE_COUNT" NUMBER(10) DEFAULT 0,
"STATUS" NUMBER(1) DEFAULT 0,
"CREATE_BY" VARCHAR2(50),
"CREATE_TIME" TIMESTAMP,
"UPDATE_BY" VARCHAR2(50),
"UPDATE_TIME" TIMESTAMP,
"DEL_FLAG" NUMBER(1) DEFAULT 0,
"MEMO" VARCHAR2(500),
PRIMARY KEY ("ID")
);
COMMENT ON TABLE "QH_EME_FIR_AID_RES" IS '急救宣教资源';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."ID" IS '主键';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."TITLE" IS '标题';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."SUMMARY" IS '摘要/简介';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."CONTENT_TYPE" IS '内容类型:IMAGE_TEXT=图文/VIDEO=视频/AUDIO=音频/PDF';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."CATEGORY_ID" IS '所属分类ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."APPLICABLE_SCENARIO" IS '适用场景';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."TAGS" IS '标签ID列表(逗号分隔)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."CONTENT" IS '正文内容(富文本HTML)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."COVER_IMAGE" IS '封面图URL';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."FILE_URL" IS '附件URL列表(JSON数组)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."EFFECTIVE_TIME" IS '生效时间';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."EXPIRY_TIME" IS '失效时间';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."AUDIT_STATUS" IS '审核状态(NULL=无审核/10=待审核/11=已通过/12=已驳回)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."AUDIT_REJECT_REASON" IS '驳回原因';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."AUDIT_BY" IS '审核人';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."AUDIT_BY_ID" IS '审核人ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."AUDIT_TIME" IS '审核时间';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."VIEW_COUNT" IS '浏览次数';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."FAVORITE_COUNT" IS '收藏次数';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."STATUS" IS '发布状态(0=草稿,1=已发布,2=已下架)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."DEL_FLAG" IS '删除标记(0=正常,1=已删除)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES"."MEMO" IS '备注';
CREATE INDEX "IDX_FA_RESOURCE_CATEGORY_ID" ON "QH_EME_FIR_AID_RES"("CATEGORY_ID");
CREATE INDEX "IDX_FA_RESOURCE_AUDIT_STATUS" ON "QH_EME_FIR_AID_RES"("AUDIT_STATUS");
CREATE INDEX "IDX_FA_RESOURCE_EFFECTIVE_TIME" ON "QH_EME_FIR_AID_RES"("EFFECTIVE_TIME");
-- 3. 用户行为记录表阅读+收藏
CREATE TABLE "QH_EME_FIR_AID_RES_UACT" (
"ID" VARCHAR2(32) NOT NULL,
"RESOURCE_ID" VARCHAR2(32) NOT NULL,
"USER_ID" VARCHAR2(50) NOT NULL,
"TYPE" NUMBER(1) NOT NULL,
"CREATE_TIME" TIMESTAMP,
PRIMARY KEY ("ID")
);
COMMENT ON TABLE "QH_EME_FIR_AID_RES_UACT" IS '用户行为记录(阅读/收藏)';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_UACT"."ID" IS '主键';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_UACT"."RESOURCE_ID" IS '资源ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_UACT"."USER_ID" IS '用户ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_UACT"."TYPE" IS '行为类型:1=阅读,2=收藏';
CREATE UNIQUE INDEX "UK_FA_USER_ACTION" ON "QH_EME_FIR_AID_RES_UACT"("RESOURCE_ID", "USER_ID", "TYPE");
CREATE INDEX "IDX_FA_USER_ACTION_USER_TYPE" ON "QH_EME_FIR_AID_RES_UACT"("USER_ID", "TYPE");
-- 4. 版本历史表
CREATE TABLE "QH_EME_FIR_AID_RES_VER" (
"ID" VARCHAR2(32) NOT NULL,
"RESOURCE_ID" VARCHAR2(32) NOT NULL,
"VERSION_NO" NUMBER(10) NOT NULL,
"TITLE" VARCHAR2(200),
"CONTENT" CLOB,
"CHANGE_DESCRIPTION" VARCHAR2(500),
"IS_CURRENT" NUMBER(1) DEFAULT 0,
"CREATE_BY" VARCHAR2(50),
"CREATE_TIME" TIMESTAMP,
PRIMARY KEY ("ID")
);
COMMENT ON TABLE "QH_EME_FIR_AID_RES_VER" IS '资源版本历史';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."ID" IS '主键';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."RESOURCE_ID" IS '资源ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."VERSION_NO" IS '版本号';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."TITLE" IS '版本时的标题';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."CONTENT" IS '版本时的正文内容';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."CHANGE_DESCRIPTION" IS '变更描述';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_VER"."IS_CURRENT" IS '是否当前版本(1=,0=)';
CREATE INDEX "IDX_FA_VERSION_RESOURCE_ID" ON "QH_EME_FIR_AID_RES_VER"("RESOURCE_ID");
-- 5. 审核记录表
CREATE TABLE "QH_EME_FIR_AID_RES_AUD" (
"ID" VARCHAR2(32) NOT NULL,
"RESOURCE_ID" VARCHAR2(32) NOT NULL,
"AUDIT_STATUS" NUMBER(2),
"AUDIT_REJECT_REASON" VARCHAR2(500),
"AUDIT_BY" VARCHAR2(50),
"AUDIT_BY_ID" VARCHAR2(32),
"AUDIT_TIME" TIMESTAMP,
"CREATE_TIME" TIMESTAMP DEFAULT SYSDATE,
PRIMARY KEY ("ID")
);
COMMENT ON TABLE "QH_EME_FIR_AID_RES_AUD" IS '急救宣教资源审核记录';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."ID" IS '主键';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."RESOURCE_ID" IS '关联资源ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."AUDIT_STATUS" IS '审核状态:10=待审核,11=已通过,12=已驳回';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."AUDIT_REJECT_REASON" IS '驳回原因';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."AUDIT_BY" IS '审核人';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."AUDIT_BY_ID" IS '审核人ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."AUDIT_TIME" IS '审核时间';
COMMENT ON COLUMN "QH_EME_FIR_AID_RES_AUD"."CREATE_TIME" IS '创建时间';
CREATE INDEX "IDX_FA_AUDIT_RESOURCE_ID" ON "QH_EME_FIR_AID_RES_AUD"("RESOURCE_ID");
-- 6. 推送记录表
CREATE TABLE "QH_EME_FIR_AID_PUSH_RD" (
"ID" VARCHAR2(32) NOT NULL,
"RESOURCE_ID" VARCHAR2(32) NOT NULL,
"RESOURCE_TITLE" VARCHAR2(200) DEFAULT NULL,
"ORDER_ID" VARCHAR2(32) NOT NULL,
"TARGET_USER_ID" VARCHAR2(32) DEFAULT NULL,
"PUSH_BY" VARCHAR2(50) DEFAULT NULL,
"PUSH_BY_ID" VARCHAR2(32) DEFAULT NULL,
"PUSH_TIME" TIMESTAMP DEFAULT NULL,
"TENANT_ID" VARCHAR2(32) DEFAULT NULL,
PRIMARY KEY ("ID"),
CONSTRAINT "UK_ORDER_RESOURCE" UNIQUE ("ORDER_ID", "RESOURCE_ID")
);
COMMENT ON TABLE "QH_EME_FIR_AID_PUSH_RD" IS '急救宣教推送记录表';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."ID" IS '主键';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."RESOURCE_ID" IS '资源ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."RESOURCE_TITLE" IS '资源标题冗余';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."ORDER_ID" IS '关联应急工单ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."TARGET_USER_ID" IS '推送目标用户ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."PUSH_BY" IS '推送人';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."PUSH_BY_ID" IS '推送人ID';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."PUSH_TIME" IS '推送时间';
COMMENT ON COLUMN "QH_EME_FIR_AID_PUSH_RD"."TENANT_ID" IS '租户ID';
CREATE INDEX "IDX_FA_PUSH_RECORD_ORDER_ID" ON "QH_EME_FIR_AID_PUSH_RD"("ORDER_ID");
-- 7. 字典数据
-- 7.1 内容类型
INSERT INTO "QH_SYS_DICT" ("ID", "TYPE", "DICT_NAME", "DICT_CODE", "DESCRIPTION", "DEL_FLAG", "TENANT_ID", "LOW_APP_ID", "CREATE_BY", "CREATE_TIME", "UPDATE_BY", "UPDATE_TIME")
VALUES ('1589123456789100001', 0, '内容类型', 'first_aid_content_type', '急救宣教资源内容类型', 0, 0, NULL, 'admin', SYSDATE, 'admin', SYSDATE);
INSERT INTO "QH_SYS_DICT_ITEM" ("ID", "DICT_ID", "ITEM_TEXT", "ITEM_VALUE", "DESCRIPTION", "SORT_ORDER", "STATUS", "CREATE_BY", "CREATE_TIME", "UPDATE_BY", "UPDATE_TIME") VALUES
('1589123456789100011', '1589123456789100001', '图文', 'IMAGE_TEXT', '图文内容', 1, 1, 'admin', SYSDATE, 'admin', SYSDATE);
INSERT INTO "QH_SYS_DICT_ITEM" ("ID", "DICT_ID", "ITEM_TEXT", "ITEM_VALUE", "DESCRIPTION", "SORT_ORDER", "STATUS", "CREATE_BY", "CREATE_TIME", "UPDATE_BY", "UPDATE_TIME") VALUES
('1589123456789100012', '1589123456789100001', '视频', 'VIDEO', '视频内容', 2, 1, 'admin', SYSDATE, 'admin', SYSDATE);
INSERT INTO "QH_SYS_DICT_ITEM" ("ID", "DICT_ID", "ITEM_TEXT", "ITEM_VALUE", "DESCRIPTION", "SORT_ORDER", "STATUS", "CREATE_BY", "CREATE_TIME", "UPDATE_BY", "UPDATE_TIME") VALUES
('1589123456789100013', '1589123456789100001', '音频', 'AUDIO', '音频内容', 3, 1, 'admin', SYSDATE, 'admin', SYSDATE);
INSERT INTO "QH_SYS_DICT_ITEM" ("ID", "DICT_ID", "ITEM_TEXT", "ITEM_VALUE", "DESCRIPTION", "SORT_ORDER", "STATUS", "CREATE_BY", "CREATE_TIME", "UPDATE_BY", "UPDATE_TIME") VALUES
('1589123456789100014', '1589123456789100001', 'PDF', 'PDF', 'PDF文档', 4, 1, 'admin', SYSDATE, 'admin', SYSDATE);
@@ -0,0 +1,28 @@
-- =====================================================
-- 缩短急救宣教相关表名达梦数据库
-- 原因原表名超过数据库限制
-- 日期2026-08-10
-- =====================================================
-- 1. 急救资源分类表
ALTER TABLE QH_EME_FIRST_AID_RESOURCE_CATEGORY RENAME TO QH_EME_FIR_AID_RES_CATE;
-- 2. 用户操作记录表
ALTER TABLE QH_EME_FIRST_AID_RESOURCE_USER_ACTION RENAME TO QH_EME_FIR_AID_RES_UACT;
-- 3. 资源版本表
ALTER TABLE QH_EME_FIRST_AID_RESOURCE_VERSION RENAME TO QH_EME_FIR_AID_RES_VER;
-- 4. 资源审核表
ALTER TABLE QH_EME_FIRST_AID_RESOURCE_AUDIT RENAME TO QH_EME_FIR_AID_RES_AUD;
-- 5. 推送记录表
ALTER TABLE QH_EME_FIRST_AID_PUSH_RECORD RENAME TO QH_EME_FIR_AID_PUSH_RD;
-- 6. 急救资源主表最后执行避免外键依赖
ALTER TABLE QH_EME_FIRST_AID_RESOURCE RENAME TO QH_EME_FIR_AID_RES;
-- =====================================================
-- 验证确认新表名已生效
-- SELECT TABLE_NAME FROM USER_TABLES WHERE TABLE_NAME LIKE 'QH_EME_FIR_AID_%';
-- =====================================================
@@ -0,0 +1,10 @@
-- QH_WAT_MONITOR_DATA 新增 EVENT_ID事件ID LOCATE_TIME定位时间字段
-- 数据库118-DM 达梦数据库SQL 全大写规范
ALTER TABLE "QH_WAT_MONITOR_DATA" ADD "EVENT_ID" VARCHAR(64) DEFAULT NULL;
ALTER TABLE "QH_WAT_MONITOR_DATA" ADD "LOCATE_TIME" DATETIME DEFAULT NULL;
COMMENT ON COLUMN "QH_WAT_MONITOR_DATA"."EVENT_ID" IS '事件ID';
COMMENT ON COLUMN "QH_WAT_MONITOR_DATA"."LOCATE_TIME" IS '定位时间';
CREATE INDEX "IDX_EVENT_ID" ON "QH_WAT_MONITOR_DATA"("EVENT_ID");
CREATE INDEX "IDX_WATCH_NO_EVENT_ID" ON "QH_WAT_MONITOR_DATA"("WATCH_NO", "EVENT_ID");
-146
View File
@@ -1,146 +0,0 @@
spring:
datasource:
druid:
stat-view-servlet:
enabled: true
loginUsername: admin
loginPassword: 123456
allow:
web-stat-filter:
enabled: true
dynamic:
druid:
initial-size: 5
min-idle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,wall,slf4j
connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
datasource:
master:
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?characterEncoding=UTF-8&useUnicode=true&useSSL=false&tinyInt1isBit=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
database: 0
host: jeecg-boot-redis
password:
port: 6379
rabbitmq:
host: jeecg-boot-rabbitmq
username: guest
password: guest
port: 5672
publisher-confirms: true
publisher-returns: true
virtual-host: /
listener:
simple:
acknowledge-mode: manual
concurrency: 1
max-concurrency: 1
retry:
enabled: true
minidao:
base-package: org.jeecg.modules.jmreport.*,org.jeecg.modules.drag.*
jeecg:
signatureSecret: dd05f1c54d63749eda95f9fa6d49v442a
signUrls: /sys/dict/getDictItems/*,/sys/dict/loadDict/*,/sys/dict/loadDictOrderByValue/*,/sys/dict/loadDictItem/*,/sys/dict/loadTreeData,/sys/api/queryTableDictItemsByCode,/sys/api/queryFilterTableDictInfo,/sys/api/queryTableDictByKeys,/sys/api/translateDictFromTable,/sys/api/translateDictFromTableByKeys
uploadType: local
domainUrl:
pc: http://localhost:3100
app: http://localhost:8051
path:
upload: /opt/upFiles
webapp: /opt/webapp
shiro:
excludeUrls: /test/jeecgDemo/demo3,/test/jeecgDemo/redisDemo/**,/category/**,/visual/**,/map/**,/jmreport/bigscreen2/**
oss:
endpoint: oss-cn-beijing.aliyuncs.com
accessKey: ??
secretKey: ??
bucketName: jeecgdev
staticDomain: ??
elasticsearch:
cluster-name: jeecg-ES
cluster-nodes: jeecg-boot-es:9200
check-enabled: false
file-view-domain: 127.0.0.1:8012
minio:
minio_url: http://minio.jeecg.com
minio_name: ??
minio_pass: ??
bucketName: otatest
jmreport:
mode: dev
is_verify_token: false
verify_methods: remove,delete,save,add,update
wps:
domain: https://wwo.wps.cn/office/
appid: ??
appsecret: ??
xxljob:
enabled: false
adminAddresses: http://jeecg-boot-xxljob:9080/xxl-job-admin
appname: ${spring.application.name}
accessToken: ''
logPath: logs/jeecg/job/jobhandler/
logRetentionDays: 30
redisson:
address: jeecg-boot-redis:6379
password:
type: STANDALONE
enabled: true
logging:
level:
org.jeecg.modules.system.mapper: info
cas:
prefixUrl: http://localhost:8888/cas
knife4j:
production: false
basic:
enable: false
username: jeecg
password: jeecg1314
justauth:
enabled: true
type:
GITHUB:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/github/callback
WECHAT_ENTERPRISE:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/wechat_enterprise/callback
agent-id: ??
DINGTALK:
client-id: ??
client-secret: ??
redirect-uri: http://sso.test.com:8080/jeecg-boot/thirdLogin/dingtalk/callback
cache:
type: default
prefix: 'demo::'
timeout: 1h
third-app:
enabled: false
type:
WECHAT_ENTERPRISE:
enabled: false
client-id: ??
client-secret: ??
agent-id: ??
DINGTALK:
enabled: false
client-id: ??
client-secret: ??
agent-id: ??
-13
View File
@@ -1,13 +0,0 @@
jeecg:
route:
config:
#type:database nacos yml
data-type: database
group: DEFAULT_GROUP
data-id: jeecg-gateway-router
spring:
redis:
database: 0
host: jeecg-boot-redis
port: 6379
password:
-65
View File
@@ -1,65 +0,0 @@
[
{
"id": "jeecg-system",
"order": 0,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/sys/**",
"_genkey_1": "/jmreport/**",
"_genkey_3": "/online/**",
"_genkey_4": "/generic/**"
}
}
],
"filters": [],
"uri": "lb://jeecg-system"
},
{
"id": "jeecg-demo",
"order": 1,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/mock/**",
"_genkey_1": "/test/**",
"_genkey_2": "/bigscreen/template1/**",
"_genkey_3": "/bigscreen/template2/**"
}
}
],
"filters": [],
"uri": "lb://jeecg-demo"
},
{
"id": "jeecg-system-websocket",
"order": 2,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/websocket/**",
"_genkey_1": "/newsWebsocket/**"
}
}
],
"filters": [],
"uri": "lb:ws://jeecg-system"
},
{
"id": "jeecg-demo-websocket",
"order": 3,
"predicates": [
{
"name": "Path",
"args": {
"_genkey_0": "/vxeSocket/**"
}
}
],
"filters": [],
"uri": "lb:ws://jeecg-demo"
}
]
-100
View File
@@ -1,100 +0,0 @@
server:
tomcat:
max-swallow-size: -1
error:
include-exception: true
include-stacktrace: ALWAYS
include-message: ALWAYS
compression:
enabled: true
min-response-size: 1024
mime-types: application/javascript,application/json,application/xml,text/html,text/xml,text/plain,text/css,image/*
management:
health:
mail:
enabled: false
endpoints:
web:
exposure:
include: "*"
health:
sensitive: true
endpoint:
health:
show-details: ALWAYS
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
mail:
host: smtp.163.com
username: jeecgos@163.com
password: ??
properties:
mail:
smtp:
auth: true
starttls:
enable: true
required: true
quartz:
job-store-type: jdbc
initialize-schema: embedded
auto-startup: false
startup-delay: 1s
overwrite-existing-jobs: true
properties:
org:
quartz:
scheduler:
instanceName: MyScheduler
instanceId: AUTO
jobStore:
class: org.springframework.scheduling.quartz.LocalDataSourceJobStore
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
misfireThreshold: 12000
clusterCheckinInterval: 15000
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
aop:
proxy-target-class: true
activiti:
check-process-definitions: false
async-executor-activate: false
job-executor-activate: false
jpa:
open-in-view: false
freemarker:
suffix: .ftl
content-type: text/html
charset: UTF-8
cache: false
prefer-file-system-access: false
template-loader-path:
- classpath:/templates
mvc:
static-path-pattern: /**
pathmatch:
matching-strategy: ant_path_matcher
resource:
static-locations: classpath:/static/,classpath:/public/
autoconfigure:
exclude: com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
mybatis-plus:
mapper-locations: classpath*:org/jeecg/modules/**/xml/*Mapper.xml
global-config:
banner: false
db-config:
id-type: ASSIGN_ID
table-underline: true
configuration:
call-setters-on-nulls: true
@@ -1,59 +0,0 @@
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: root
ds1:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot2?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
type: com.alibaba.druid.pool.DruidDataSource
username: root
password: root
props:
sql-show: true
rules:
replica-query:
load-balancers:
round-robin:
type: ROUND_ROBIN
props:
default: 0
data-sources:
prds:
primary-data-source-name: ds0
replica-data-source-names: ds1
load-balancer-name: round_robin
sharding:
binding-tables:
- sys_log
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
sharding-algorithms:
table-classbased:
props:
strategy: standard
algorithmClassName: org.jeecg.modules.test.sharding.algorithm.StandardModTableShardAlgorithm
type: CLASS_BASED
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{operate_type % 2}
tables:
sys_log:
actual-data-nodes: ds$->{0..1}.sys_log$->{0..1}
database-strategy:
standard:
sharding-column: operate_type
sharding-algorithm-name: database-inline
table-strategy:
standard:
sharding-algorithm-name: table-classbased
sharding-column: log_type
@@ -1,33 +0,0 @@
spring:
shardingsphere:
datasource:
names: ds0
ds0:
driverClassName: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://jeecg-boot-mysql:3306/jeecg-boot?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
props:
sql-show: true
rules:
sharding:
binding-tables: sys_log
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 123
sharding-algorithms:
table-classbased:
props:
strategy: standard
algorithmClassName: org.jeecg.modules.test.sharding.algorithm.StandardModTableShardAlgorithm
type: CLASS_BASED
tables:
sys_log:
actual-data-nodes: ds0.sys_log$->{0..1}
table-strategy:
standard:
sharding-algorithm-name: table-classbased
sharding-column: log_type
@@ -30,10 +30,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
+1
View File
@@ -20,6 +20,7 @@
<module>font-resources</module>
<module>monitoring-sync</module>
<module>renkang-test</module>
<module>xj-anyan-api</module>
</modules>
<properties>
@@ -25,6 +25,7 @@ import org.springframework.core.task.TaskExecutor;
import org.springframework.jdbc.support.JdbcTransactionManager;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.List;
@@ -125,8 +126,21 @@ public class BatchAutoConfiguration {
@BatchDataSource ObjectProvider<DataSource> batchDataSource,
BatchProperties properties
) {
return new BatchDataSourceScriptDatabaseInitializer(batchDataSource.getIfAvailable(() -> dataSource),
properties.getJdbc());
DataSource dataSourceToUse = batchDataSource.getIfAvailable(() -> dataSource);
BatchProperties.Jdbc jdbc = properties.getJdbc();
// 达梦数据库兼容处理:设置platform绕过构造器中的自动类型检测
if (!StringUtils.hasText(jdbc.getPlatform())) {
jdbc.setPlatform("dm");
}
// 重写afterPropertiesSet()跳过SQL脚本初始化:
// 父类会先断言SQL文件存在再检查initializeSchema mode,导致找不到schema-dm.sql时报错
// Batch元数据表已通过数据迁移创建,无需自动建表
return new BatchDataSourceScriptDatabaseInitializer(dataSourceToUse, jdbc) {
@Override
public void afterPropertiesSet() {
// 跳过Batch元数据表的自动建表,防止找不到schema-dm.sql文件报错
}
};
}
}
@@ -34,6 +34,12 @@ public class CustomBatchConfigurer extends BasicBatchConfigurer {
this.taskExecutor = taskExecutor;
}
@Override
public void initialize() {
// 调用父类的 initialize() 方法来初始化 JobRepository 等
super.initialize();
}
@Override
protected JobLauncher createJobLauncher() throws Exception {
SimpleJobLauncher jobLauncher = new SimpleJobLauncher();
+46
View File
@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.renkang</groupId>
<artifactId>functional-modules</artifactId>
<version>2.0.0</version>
</parent>
<artifactId>xj-anyan-api</artifactId>
<description>xj安眼系统API</description>
<version>2.0.0</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,213 @@
package com.renkang.anyan.client;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.renkang.anyan.model.dto.*;
import com.renkang.anyan.model.vo.*;
import java.util.Date;
import java.util.List;
/**
* @Description: AnYanClient xj安眼系统接口调用客户端
* @Author: feng
* @Date: 2025-8-1
* @Version: V1.0
*/
public interface AnYanClient {
/**
* 获取服务访问令牌
*
* @return
*/
String getAuthToken();
/**
* 获取服务访问令牌(密码模式)
*
* @return
*/
AuthResult getAuthTokenByPassword(String username, String password);
/**
* 刷新口令
*/
void refreshToken();
/**
* 体检医院人数统计查询
*
* @param medicalYear 体检年份
* @return
*/
List<MedicalPeopleNumStatisticsVO> examinationStatistics(Integer medicalYear);
/**
* 健康打卡数据统计查询(血压,减重,血糖)
*
* @param beginDate
* @param endDate
* @return
*/
HealthSignStatVO healthCheckStatistics(Date beginDate, Date endDate);
/**
* 健康打卡异常数据详细查询(血压)
*
* @param healthSignListParam
* @return
*/
IPage<BloodPressureList> bloodPressureList(HealthSignListParam healthSignListParam);
/**
* 健康打卡异常数据详细查询(减重)
*
* @param healthSignListParam
* @return
*/
IPage<WeightPressureList> weightPressureList(HealthSignListParam healthSignListParam);
/**
* 健康打卡异常数据详细查询(血糖)
*
* @param healthSignListParam
* @return
*/
IPage<BloodSugarClockSList> bloodSugarClockSList(HealthSignListParam healthSignListParam);
/**
* 油田医院数据查询
*
* @return
*/
List<HospitalListVO> hospitalList();
/**
* 健康小屋基础数据查询
*
* @return
*/
List<HealthRoomBaseVO> healthPlaceHomList();
/**
* 健康小屋详细数据查询
*
* @param healthRoomDTO
* @return
*/
IPage<HealthPlaceHomeDetailsVO> healthPlaceHomDetailList(HealthRoomDTO healthRoomDTO);
/**
* 当年体检异常人数统计
*
* @param medicalYear 年份
* @return
*/
MedicalAbnormalStatisticsVO medicalAbnormalStatistics(Integer medicalYear);
/**
* 当年体检异常人员列表
*
* @param param
* @return
*/
IPage<MedicalAbnormalVO> medicalAbnormalList(MedicalAbnormalDTO param);
/**
* 公网环境获取服务访问token (目前仅用于访问安眼大屏)
*
* @return
*/
String publicNetworkToken();
/**
* 公网环境获取服务访问token (目前仅用于访问安眼大屏, 不走缓存, 每次用现取)
*
* @return
*/
AuthResult publicNetworkTokenNoCache();
/**
* 获取用户信息
*
* @param token
* @return
*/
AnYanUserInfoResult getAnYanUserInfo(String token, String username);
/**
* 批量拉取安眼平台AED信息 , 用于定时任务更新本地数据的AED数据
*
* @return
*/
List<AnYanAedInfo> queryAnYanAedInfo();
/**
* 最新检查员工信息查询接口 (大屏首页重点关爱趋势预警滚动列表,固定查100前项)
*
* @return 最新的100条数据
*/
List<CarePersonList> homeCarePersonList();
/**
* 一人一案信息详情接口 (用大屏首页重点关爱趋势预警滚动列表点击数据详情)
*
* @param employeeNum 工号
* @return
*/
CareUserListVo careUserDetails(String anYanUserId, String employeeNum);
/**
* 一人一案分页列表查询接口(用大屏首页重点关爱趋势预警弹窗列表)
*
* @param param 查询条件
* @return
*/
IPage<CareUserListVo> careUserDetailsPage(CareUserDetailsParam param);
/**
* 查询关爱联络员信息及急救人员查询接口
*
* @param param 查询条件
* @return
*/
IPage<EmergencyContactVO> queryEmergencyContact(EmergencyContactParam param);
/**
* 批量更新员工关键信息接口
*
* @param employeeNumList
* @return
*/
List<AnYanUpdateUserBatchInfo> updateUserBatch(List<String> employeeNumList);
/**
* 更新单个员工扩展信息
* 是否是借调人员 这个判断是依据 数据中是否存在currentOfficeId来决定的,存在说明是借调人员,不存在说明不是借调人员
*
* @param employeeNum
* @return
*/
AnYanUserEmployee updateEmployeeNum(String employeeNum);
/**
* 体检数据
*
* @param param
* @return
*/
IPage<AnYanHealthCheckNew> queryAnYanHealthCheckNew(AnYanHealthCheckNewParam param);
/**
* 根据id查询体检数据
*
* @param id
* @return
*/
AnYanEmpHealthCheckNew queryEmpHealthCheckNew(String id);
/**
* 根据检查id、科室编号、科室名称查询体检记录
*/
List<HealthCheckProject> queryHealthCheckProject(AnYanHealthCheckProjectParam param);
}
@@ -0,0 +1,441 @@
package com.renkang.anyan.client;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.renkang.anyan.config.AnYanProperties;
import com.renkang.anyan.config.RestTemplateUtil;
import com.renkang.anyan.constants.AnYanConstants;
import com.renkang.anyan.enums.AnYanApiEnum;
import com.renkang.anyan.model.dto.*;
import com.renkang.anyan.model.vo.*;
import com.renkang.anyan.utils.PageResultToBeanUtil;
import com.renkang.anyan.utils.SM4Utils;
import com.renkang.anyan.utils.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.jeecg.common.exception.ExceptionAssertsUtil;
import org.jeecg.util.RedisClientUtil;
import org.jeecg.util.RedisKeyPrefix;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Description: AnYanClientExecutor xj安眼系统接口调用执行器
* @Author: feng
* @Date: 2025-8-1
* @Version: V1.0
*/
@Slf4j
@Service
@EnableRetry
@RequiredArgsConstructor
public class AnYanClientExecutor implements AnYanClient {
//安眼获取token访问公钥
private String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJL0JkqsUoK6kt3JyogsgqNp9VDGDp+t3ZAGMbVoMPdHNT2nfiIVh9ZMNHF7g2XiAa8O8AQWyh2PjMR0NiUSVQMCAwEAAQ==";
private final RedisClientUtil redisClientUtil;
private final AnYanProperties anYanProperties;
@Override
public String getAuthToken() {
String token = redisClientUtil.get(new RedisKeyPrefix(AnYanConstants.AN_YAN_API_TOKEN_PREFIX), "token", String.class);
if (StrUtil.isEmpty(token)) {
AuthResult authResult = getAuthTokenExecutor();
if (ObjectUtil.isNotEmpty(authResult) && StrUtil.isNotEmpty(authResult.getAccess_token())) {
token = authResult.getAccess_token();
int expireSeconds = ObjectUtil.isNotEmpty(authResult.getExpires_in()) ? authResult.getExpires_in() : 7200;
redisClientUtil.set(new RedisKeyPrefix(expireSeconds, AnYanConstants.AN_YAN_API_TOKEN_PREFIX), "token", token);
}
}
return token;
}
private AuthResult getAuthTokenExecutor() {
AuthParam authParam = new AuthParam();
authParam.setClient_id(anYanProperties.getClientId());
authParam.setClient_secret(anYanProperties.getClientSecret());
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
ResponseEntity<AuthResult> responseEntity = RestTemplateUtil.postExchange(AnYanApiEnum.QUERY_AUTH_TOKEN, headers, authParam, AuthResult.class);
if (HttpStatus.OK.equals(responseEntity.getStatusCode()) && ObjectUtils.isNotEmpty(responseEntity.getBody())) {
return responseEntity.getBody();
} else {
ExceptionAssertsUtil.fail("------安眼系统获取token失败------" + responseEntity.getBody());
}
return new AuthResult();
}
/**
* @description: 通过密码获取令牌
* @author PengJ
* @date 2025/10/24 11:15
*/
@Override
public AuthResult getAuthTokenByPassword(String username, String password) {
try {
String encryptPassword = StringUtils.encryptByPublicKey(publicKey, password);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("grant_type", "password");
formData.add("username", username);
formData.add("password", encryptPassword);
formData.add("client_id", anYanProperties.getClientId());
formData.add("client_secret", anYanProperties.getClientSecret());
ResponseEntity<AuthResult> authResult = RestTemplateUtil.postExchangeForGetTokenByPassword(AnYanApiEnum.QUERY_AUTH_TOKEN, formData, headers, AuthResult.class);
if (HttpStatus.OK.equals(authResult.getStatusCode())
&& ObjectUtils.isNotEmpty(authResult.getBody())) {
String token = authResult.getBody().getAccess_token();
int expireSeconds = ObjectUtil.isNotEmpty(authResult.getBody().getExpires_in()) ? authResult.getBody().getExpires_in() : 3600;
redisClientUtil.set(new RedisKeyPrefix(expireSeconds, AnYanConstants.AN_YAN_API_USER_TOKEN_PREFIX), username, token);
return authResult.getBody();
} else {
log.error("------安眼系统获取token失败(密码模式){}------", authResult.getBody());
ExceptionAssertsUtil.fail("------安眼系统获取token失败------" + authResult.getBody());
return null;
}
} catch (HttpClientErrorException e) {
log.error("------安眼系统获取token失败(密码模式){}------", e.getMessage());
String replace = Objects.requireNonNull(e.getMessage()).replace("401 Unauthorized: ", "");
replace = replace.replace("\"", "");
AuthResult bean = JSONUtil.toBean(replace, AuthResult.class);
ExceptionAssertsUtil.fail(bean.getMessage());
} catch (Exception e) {
ExceptionAssertsUtil.fail(e.getMessage());
}
return null;
}
/*调用安眼提供的支持公网访问的获取token接口也能通过接口的鉴权, 并且部分接口还需要传入鉴权用户的身份证号, 所以业务接口直接使用公网环境的鉴权接口
@Override
public void refreshToken() {
AuthResult authResult = getAuthTokenExecutor();
if (ObjectUtil.isNotEmpty(authResult) && StrUtil.isNotEmpty(authResult.getAccess_token())) {
int expireSeconds = ObjectUtil.isNotEmpty(authResult.getExpires_in()) ? authResult.getExpires_in() : 7200;
redisClientUtil.set(new RedisKeyPrefix(expireSeconds, AnYanConstants.AN_YAN_API_TOKEN_PREFIX), "token", authResult.getAccess_token());
}
}
private HttpHeaders buildTokenHeader() {
HttpHeaders headers = new HttpHeaders();
headers.add(AnYanConstants.AUTHORIZATION, AnYanConstants.TOKEN_TYPE + getAuthToken());
return headers;
}*/
@Override
public void refreshToken() {
AuthResult authResult = publicNetworkTokenNoCache();
if (ObjectUtil.isNotEmpty(authResult) && StrUtil.isNotEmpty(authResult.getAccess_token())) {
int expireSeconds = ObjectUtil.isNotEmpty(authResult.getExpires_in()) ? authResult.getExpires_in() : 7200;
redisClientUtil.set(new RedisKeyPrefix(expireSeconds, AnYanConstants.AN_YAN_API_TOKEN_PREFIX), "publicToken", authResult.getAccess_token());
log.info("---------刷新安眼token成功-------新的token{},剩余过期时间:{}", authResult.getAccess_token(), authResult.getExpires_in());
}
}
private HttpHeaders buildTokenHeader() {
HttpHeaders headers = new HttpHeaders();
headers.add(AnYanConstants.AUTHORIZATION, AnYanConstants.TOKEN_TYPE + publicNetworkToken());
headers.add(AnYanConstants.HEADER_PARAM_X_AUTH_ID, AnYanConstants.X_AUTH_ID_PREFIX + anYanProperties.getPublicUserCardNo());
headers.add(AnYanConstants.HEADER_PARAM_ROLES, AnYanConstants.HEADER_PARAM_ROLES_VALUES);
return headers;
}
@Override
@Retryable(maxAttempts = 2)
public List<MedicalPeopleNumStatisticsVO> examinationStatistics(Integer medicalYear) {
if (null == medicalYear) {
medicalYear = DateUtil.year(new Date());
}
JSONObject param = new JSONObject();
param.put("medicalYear", String.valueOf(medicalYear));
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HEALTH_EXAMINATION_STATISTICS, buildTokenHeader(), param);
List<Object> listMaps = (List<Object>) result.getData();
return listMaps.stream().map(lMap -> BeanUtil.toBean(lMap, MedicalPeopleNumStatisticsVO.class)).collect(Collectors.toList());
}
@Override
@Retryable(maxAttempts = 2)
public HealthSignStatVO healthCheckStatistics(Date beginDate, Date endDate) {
JSONObject param = new JSONObject();
param.put("inputDateBegin", ObjectUtil.isEmpty(beginDate) ? DateUtil.today() : DateUtil.formatDate(beginDate));
param.put("inputDateEnd", ObjectUtil.isEmpty(endDate) ? DateUtil.today() : DateUtil.formatDate(endDate));
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HEALTH_CHECK_STATISTICS, buildTokenHeader(), param);
return BeanUtil.toBean(result.getData(), HealthSignStatVO.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<BloodPressureList> bloodPressureList(HealthSignListParam healthSignListParam) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.BLOOD_PRESSURE_LIST, buildTokenHeader(), healthSignListParam);
return new PageResultToBeanUtil<BloodPressureList>().transform(result, BloodPressureList.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<WeightPressureList> weightPressureList(HealthSignListParam healthSignListParam) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.WEIGHT_PRESSURE_LIST, buildTokenHeader(), healthSignListParam);
return new PageResultToBeanUtil<WeightPressureList>().transform(result, WeightPressureList.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<BloodSugarClockSList> bloodSugarClockSList(HealthSignListParam healthSignListParam) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.BLOOD_SUGAR_CLOCKS_LIST, buildTokenHeader(), healthSignListParam);
return new PageResultToBeanUtil<BloodSugarClockSList>().transform(result, BloodSugarClockSList.class);
}
@Override
@Retryable(maxAttempts = 2)
public List<HospitalListVO> hospitalList() {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HOSPITAL_LIST, buildTokenHeader(), null);
List<Object> listMaps = (List<Object>) result.getData();
return listMaps.stream().map(lMap -> BeanUtil.toBean(lMap, HospitalListVO.class)).collect(Collectors.toList());
}
@Override
@Retryable(maxAttempts = 2)
public List<HealthRoomBaseVO> healthPlaceHomList() {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HEALTH_PLACE_ROOM_LIST, buildTokenHeader(), null);
List<Object> listMaps = (List<Object>) result.getData();
return listMaps.stream().map(lMap -> BeanUtil.toBean(lMap, HealthRoomBaseVO.class)).collect(Collectors.toList());
}
@Override
@Retryable(maxAttempts = 2)
public IPage<HealthPlaceHomeDetailsVO> healthPlaceHomDetailList(HealthRoomDTO healthRoomDTO) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HEALTH_PLACE_ROOM_INFO_LIST, buildTokenHeader(), healthRoomDTO);
return new PageResultToBeanUtil<HealthPlaceHomeDetailsVO>().transform(result, HealthPlaceHomeDetailsVO.class);
}
@Override
@Retryable(maxAttempts = 2)
public MedicalAbnormalStatisticsVO medicalAbnormalStatistics(Integer medicalYear) {
if (null == medicalYear) {
medicalYear = DateUtil.year(new Date());
}
JSONObject param = new JSONObject();
param.put("medicalYear", medicalYear);
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.MEDICAL_ABNORMAL_STATISTICS, buildTokenHeader(), param);
return BeanUtil.toBean(result.getData(), MedicalAbnormalStatisticsVO.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<MedicalAbnormalVO> medicalAbnormalList(MedicalAbnormalDTO param) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.MEDICAL_ABNORMAL_LIST, buildTokenHeader(), param);
return new PageResultToBeanUtil<MedicalAbnormalVO>().transform(result, MedicalAbnormalVO.class);
}
@Override
public String publicNetworkToken() {
String token = redisClientUtil.get(new RedisKeyPrefix(AnYanConstants.AN_YAN_API_TOKEN_PREFIX), "publicToken", String.class);
if (StrUtil.isEmpty(token)) {
AuthResult authResult = publicNetworkTokenNoCache();
if (null != authResult) {
token = authResult.getAccess_token();
int expireSeconds = ObjectUtil.isNotEmpty(authResult.getExpires_in()) ? authResult.getExpires_in() : 7200;
redisClientUtil.set(new RedisKeyPrefix(expireSeconds, AnYanConstants.AN_YAN_API_TOKEN_PREFIX), "publicToken", token);
}
}
return token;
}
/**
* @description: 旧版公网获取token实现(SM4 + AnYanResult 包裹解密),保留备份
* @author PengJ
*/
public AuthResult publicNetworkTokenNoCacheBAK() {
JSONObject param = new JSONObject();
String password = anYanProperties.getPublicPassWrod();
String userName = anYanProperties.getPublicUserNmae();
try {
String sm4Password = SM4Utils.encryptSM4(password);
String encryptPassword = StringUtils.encryptByPublicKey(publicKey, password);
param.put("password", encryptPassword);
param.put("selfPassword", sm4Password);
param.put("loginName", userName);
param.put("timeStamp", UUID.randomUUID().toString());
ResponseEntity<AnYanResult> responseEntity = RestTemplateUtil.postExchangeForGetToken(AnYanApiEnum.PUBLIC_NETWORK_AUTH_TOKEN, param, AnYanResult.class);
//安眼提供的接口包过深 ,先从AnYanResult对象中取出加密的密文,后在解密密文拿出 AuthResult对象 , 再从AuthResult对象中拿出 access_token
AnYanResult anYanResult = responseEntity.getBody();
if (ObjectUtil.isNotEmpty(anYanResult) && ObjectUtil.isNotEmpty(anYanResult.getData())) {
String decryptAuthResult = SM4Utils.decryptSM4((String) anYanResult.getData());
AuthResult authResult = JSON.parseObject(decryptAuthResult).toJavaObject(AuthResult.class);
if (null != authResult && null != authResult.getAccess_token()) {
return authResult;
} else {
ExceptionAssertsUtil.fail("获取安眼系统token失败");
}
}
} catch (Exception e) {
e.printStackTrace();
ExceptionAssertsUtil.fail("获取安眼系统token失败");
}
return null;
}
/**
* @description: 公网获取token(密码模式,调用方式与 getAuthTokenByPassword 保持一致)
* 表单提交 + 公网 host + QUERY_AUTH_TOKEN,仅 RSA 加密密码,不写 Redis(缓存由调用方负责)
* @author PengJ
*/
@Override
public AuthResult publicNetworkTokenNoCache() {
String username = anYanProperties.getPublicUserNmae();
String password = anYanProperties.getPublicPassWrod();
try {
// 仅 RSA 加密密码,与 getAuthTokenByPassword 保持一致
String encryptPassword = StringUtils.encryptByPublicKey(publicKey, password);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("grant_type", "password");
formData.add("username", username);
formData.add("password", encryptPassword);
formData.add("client_id", anYanProperties.getClientId());
formData.add("client_secret", anYanProperties.getClientSecret());
ResponseEntity<AuthResult> authResult = RestTemplateUtil.postExchangeForGetTokenByPassword(AnYanApiEnum.QUERY_AUTH_TOKEN, formData, headers, AuthResult.class);
if (HttpStatus.OK.equals(authResult.getStatusCode())
&& ObjectUtils.isNotEmpty(authResult.getBody())) {
return authResult.getBody();
} else {
log.error("------安眼系统获取公网token失败{}------", authResult.getBody());
ExceptionAssertsUtil.fail("------安眼系统获取公网token失败------" + authResult.getBody());
return null;
}
} catch (HttpClientErrorException e) {
log.error("------安眼系统获取公网token失败{}------", e.getMessage());
String replace = Objects.requireNonNull(e.getMessage()).replace("401 Unauthorized: ", "");
replace = replace.replace("\"", "");
AuthResult bean = JSONUtil.toBean(replace, AuthResult.class);
ExceptionAssertsUtil.fail(bean.getMessage());
} catch (Exception e) {
ExceptionAssertsUtil.fail(e.getMessage());
}
return null;
}
/**
* @description: 根据token获取用户信息
* @author PengJ
* @date 2025/10/24 17:00
*/
@Override
@Retryable(maxAttempts = 2)
public AnYanUserInfoResult getAnYanUserInfo(String token, String username) {
HttpHeaders headers = new HttpHeaders();
headers.add(AnYanConstants.AUTHORIZATION, AnYanConstants.TOKEN_TYPE + token);
long time1 = System.currentTimeMillis();
ResponseEntity<AnYanUserInfoResult> exchange = RestTemplateUtil.getExchange(AnYanApiEnum.USER_INFO_UNIT_V2, headers, AnYanUserInfoResult.class);
long time2 = System.currentTimeMillis();
log.info("根据token获取用户信息耗时:" + (time2 - time1) + "ms");
redisClientUtil.set(new RedisKeyPrefix(-1, AnYanConstants.AN_YAN_API_USERINFO_PREFIX), username, exchange);
return BeanUtil.toBean(exchange.getBody(), AnYanUserInfoResult.class);
}
@Override
@Retryable(maxAttempts = 2)
public List<AnYanAedInfo> queryAnYanAedInfo() {
//安眼接口本身支持分页查询 , 因此方法用于定时任务批量查询更新 , 这里一次性查询所有
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.QUERY_AN_YAN_AED, buildTokenHeader(), new AnYanAedParam());
IPage<AnYanAedInfo> aedPage = new PageResultToBeanUtil<AnYanAedInfo>().transform(result, AnYanAedInfo.class);
return aedPage.getRecords();
}
@Override
@Retryable(maxAttempts = 2)
public List<CarePersonList> homeCarePersonList() {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.CARE_PERSON_BY_TIME, buildTokenHeader(), null);
List<Object> listMaps = (List<Object>) result.getData();
return listMaps.stream().map(lMap -> BeanUtil.toBean(lMap, CarePersonList.class)).collect(Collectors.toList());
}
@Override
@Retryable(maxAttempts = 2)
public CareUserListVo careUserDetails(String anYanUserId, String employeeNum) {
JSONObject param = new JSONObject();
if (StrUtil.isNotEmpty(anYanUserId)) {
param.put("id", anYanUserId);
}
if (StrUtil.isNotEmpty(employeeNum)) {
param.put("employeeNum", employeeNum);
}
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.CARE_PERSON_Details, buildTokenHeader(), param);
return BeanUtil.toBean(result.getData(), CareUserListVo.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<CareUserListVo> careUserDetailsPage(CareUserDetailsParam param) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.CARE_PERSON_PAGE_LIST, buildTokenHeader(), param);
return new PageResultToBeanUtil<CareUserListVo>().transform(result, CareUserListVo.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<EmergencyContactVO> queryEmergencyContact(EmergencyContactParam param) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.QUERY_EMPLOYEE_CARE_PERSON, buildTokenHeader(), param);
return new PageResultToBeanUtil<EmergencyContactVO>().transform(result, EmergencyContactVO.class);
}
@Override
@Retryable(maxAttempts = 2)
public List<AnYanUpdateUserBatchInfo> updateUserBatch(List<String> employeeNumList) {
Map<String, List<String>> map = new HashMap<>(1);
map.put("employeeNumList", employeeNumList);
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.FIND_VITAL_INFO, buildTokenHeader(), map);
List<Object> listMaps = (List<Object>) result.getData();
return listMaps.stream().map(lMap -> BeanUtil.toBean(lMap, AnYanUpdateUserBatchInfo.class)).collect(Collectors.toList());
}
@Override
@Retryable(maxAttempts = 2)
public AnYanUserEmployee updateEmployeeNum(String employeeNum) {
Map<String, String> map = new HashMap<>(1);
map.put("employeeNum", employeeNum);
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.FIND_BY_EMPLOYEENUM, buildTokenHeader(), map);
return BeanUtil.toBean(result.getData(), AnYanUserEmployee.class);
}
@Override
@Retryable(maxAttempts = 2)
public IPage<AnYanHealthCheckNew> queryAnYanHealthCheckNew(AnYanHealthCheckNewParam param) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HEALTH_CHECK_NEW, buildTokenHeader(), param);
return new PageResultToBeanUtil<AnYanHealthCheckNew>().transform(result, AnYanHealthCheckNew.class);
}
@Override
@Retryable(maxAttempts = 2)
public AnYanEmpHealthCheckNew queryEmpHealthCheckNew(String id) {
AnYanResult<Object> result = RestTemplateUtil.getExchange(AnYanApiEnum.CHECK_NEW_BY_ID, buildTokenHeader(), id);
return BeanUtil.toBean(result.getData(), AnYanEmpHealthCheckNew.class);
}
@Override
@Retryable(maxAttempts = 2)
public List<HealthCheckProject> queryHealthCheckProject(AnYanHealthCheckProjectParam param) {
AnYanResult<Object> result = RestTemplateUtil.postExchange(AnYanApiEnum.HEALTH_CHECK_PROJECT, buildTokenHeader(), param);
List<Object> listMaps = (List<Object>) result.getData();
return listMaps.stream().map(lMap -> BeanUtil.toBean(lMap, HealthCheckProject.class)).collect(Collectors.toList());
}
}
@@ -0,0 +1,57 @@
package com.renkang.anyan.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
/**
* @Description: AnYanProperties
* @Author: feng
* @Date: 2025-8-1
* @Version: V1.0
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "variable.anyan")
public class AnYanProperties {
/**
* 是否启用安眼的接口
*/
private boolean enable;
/**
*服务调用地址
*/
private String host = "";
/**
* clientId
*/
private String clientId = "";
/**
*clientSecret
*/
private String clientSecret = "";
/**
*公网可访问的接口地址 (目前或者大屏调转的toekn使用)
*/
private String publicHost = "";
/**
* 公网环境的鉴权账号 (目前或者大屏调转的toekn使用)
*/
private String publicUserNmae = "";
/**
*公网环境的鉴权密码 (目前或者大屏调转的toekn使用)
*/
private String publicPassWrod = "";
/**
*鉴权用户的身份证号,部分接口的请求头参数中需要传入
*/
private String publicUserCardNo = "737720666075238400";
/**
*链接超时时间
*/
private Long timeout = 3000L;
/**
*读取超时时间
*/
private Long readTimeout = 30000L;
}
@@ -0,0 +1,79 @@
package com.renkang.anyan.config;
import com.renkang.anyan.client.AnYanClient;
import com.renkang.anyan.config.AnYanProperties;
import com.renkang.anyan.enums.AnYanApiEnum;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.exception.ExceptionAssertsUtil;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
/**
* @Description: AnYanClient xj安眼系统接口调用客户端
* @Author: feng
* @Date: 2025-8-1
* @Version: V1.0
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
public class AnYanRestTemplateConfig {
private final AnYanProperties properties;
@Bean
public RestTemplate anYanTemplate() {
ClientHttpRequestInterceptor authInterceptor = authInterceptor();
return new RestTemplateBuilder()
.setConnectTimeout(Duration.of(properties.getTimeout(), ChronoUnit.MILLIS))
.setReadTimeout(Duration.of(properties.getReadTimeout(), ChronoUnit.MILLIS))
.additionalMessageConverters(
new StringHttpMessageConverter(StandardCharsets.UTF_8),
new MappingJackson2HttpMessageConverter(),
new FormHttpMessageConverter()
)
.additionalInterceptors(authInterceptor)
.build();
}
/**
* 自定义拦截器处理接口鉴权失败, 配合spring retry使用从而避免因token失效导致的接口调用失败
*/
public ClientHttpRequestInterceptor authInterceptor() {
return (httpRequest, bytes, clientHttpRequestExecution) -> {
ClientHttpResponse execute;
try {
execute = clientHttpRequestExecution.execute(httpRequest, bytes);
HttpStatus httpStatus = execute.getStatusCode();
if (Arrays.asList(HttpStatus.UNAUTHORIZED, HttpStatus.FORBIDDEN).contains(httpStatus)) {
// 获取请求URL
String url = httpRequest.getURI().toString();
// 排除特定接口不执行刷新token逻辑(获取token接口,根据token获取个人信息接口)
if (!url.contains(AnYanApiEnum.QUERY_AUTH_TOKEN.getMethodPath()) && !url.contains(AnYanApiEnum.USER_INFO_UNIT_V2.getMethodPath())) {
log.info("---------安眼token过期,触发刷新token机制-------已过期的token{}", httpRequest.getHeaders().getFirst("authorization"));
SpringContextUtils.getBean("anYanClientExecutor", AnYanClient.class).refreshToken();
}
}
} catch (Exception e) {
log.error("---------安眼平台API调用异常-------e{},接口:{}", e.getMessage(), httpRequest.getURI(), e);
ExceptionAssertsUtil.fail("安眼平台API调用失败");
throw e;
}
return execute;
};
}
}
@@ -0,0 +1,143 @@
package com.renkang.anyan.config;
import com.alibaba.fastjson.JSON;
import com.renkang.anyan.enums.AnYanApiEnum;
import com.renkang.anyan.model.vo.AnYanResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.jeecg.common.util.SpringContextUtils;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import java.util.Objects;
/**
* @Description: RestTemplate 工具类 (更多请求方式自行封装)
* @Author: feng
* @Date: 2025-8-1
* @Version: V1.0
*/
@Slf4j
public class RestTemplateUtil {
private static RestTemplate restTemplate;
private static AnYanProperties properties;
public static RestTemplate getRestTemplate() {
if (Objects.isNull(restTemplate)) {
restTemplate = SpringContextUtils.getBean("anYanTemplate", RestTemplate.class);
}
return restTemplate;
}
public static AnYanProperties getProperties() {
if (Objects.isNull(properties)) {
properties = SpringContextUtils.getBean(AnYanProperties.class);
}
return properties;
}
/**
* 请求接口访问 token
*
* @param api 接口
* @param requestBody 参数
* @return ResponseEntity
*/
public static <T> ResponseEntity<T> postExchange(AnYanApiEnum api, HttpHeaders headers, Object requestBody, Class<T> responseType) {
String url = getProperties().getHost() + api.getMethodPath() + api.getMethodParam();
HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody);
return getRestTemplate().exchange(url, api.getHttpMethod(), requestEntity, responseType);
}
/**
* 公网请求接口访问 token(密码模式)
*
* @param api 接口
* @param formData 参数
* @return ResponseEntity
*/
public static <T> ResponseEntity<T> postExchangeForGetTokenByPassword(AnYanApiEnum api, MultiValueMap<String, String> formData, HttpHeaders headers, Class<T> responseType) {
String url = getProperties().getPublicHost() + api.getMethodPath();
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(formData, headers);
log.info("开始发送请求到: {},入参:{}", url, formData);
long time1 = System.currentTimeMillis();
ResponseEntity<T> response = getRestTemplate().exchange(url, api.getHttpMethod(), requestEntity, responseType);
log.info("收到响应: {}", response);
long time2 = System.currentTimeMillis();
log.info("获取token(密码模式) 耗时:" + (time2 - time1) + "ms");
return response;
}
/**
* 公网请求接口访问 token
*
* @param api 接口
* @param requestBody 参数
* @return ResponseEntity
*/
public static <T> ResponseEntity<T> postExchangeForGetToken(AnYanApiEnum api, Object requestBody, Class<T> responseType) {
String url = getProperties().getPublicHost() + api.getMethodPath() + api.getMethodParam();
HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody);
// 打印请求入参,便于排查
String bodyJson = JSON.toJSONString(requestBody);
log.info("安眼获取token请求入参: url={}, body={}", url, bodyJson);
// 打印等价 curlapplication/json),便于本地手动调试
log.info("安眼获取token等价curl: curl -X POST '{}' -H 'Content-Type: application/json' -d '{}'", url, bodyJson);
ResponseEntity<T> response = getRestTemplate().exchange(url, api.getHttpMethod(), requestEntity, responseType);
// 打印返回信息(状态码 + 响应体)
log.info("安眼获取token返回信息: statusCode={}, body={}", response.getStatusCode(), JSON.toJSONString(response.getBody()));
return response;
}
/**
* 通用调用方法
*
* @param api 请求接口
* @param headers 请求头
* @param requestBody body参数
* @return Result对象
*/
public static AnYanResult<Object> postExchange(AnYanApiEnum api, HttpHeaders headers, Object requestBody) {
String url = getProperties().getHost() + api.getMethodPath() + api.getMethodParam();
HttpEntity<Object> requestEntity = new HttpEntity<>(requestBody, headers);
return responseHandler(getRestTemplate().exchange(url, api.getHttpMethod(), requestEntity, AnYanResult.class));
}
/**
* 通用调用方法
*
* @param api 请求接口
* @param headers 请求头
* @return Result对象
*/
public static <T> ResponseEntity<T> getExchange(AnYanApiEnum api, HttpHeaders headers, Class<T> responseType) {
String url = getProperties().getHost() + api.getMethodPath() + api.getMethodParam();
HttpEntity<Object> requestEntity = new HttpEntity<>(headers);
return getRestTemplate().exchange(url, api.getHttpMethod(), requestEntity, responseType);
}
public static AnYanResult<Object> getExchange(AnYanApiEnum api, HttpHeaders headers, String id) {
String url = getProperties().getHost() + api.getMethodPath() + "/" + id;
HttpEntity<Object> requestEntity = new HttpEntity<>(headers);
return responseHandler(getRestTemplate().exchange(url, api.getHttpMethod(), requestEntity, AnYanResult.class));
}
/**
* ResponseEntity 转 Result
*
* @param entity
* @return
*/
private static <T extends AnYanResult<Object>> AnYanResult<Object> responseHandler(ResponseEntity<T> entity) {
if (HttpStatus.OK.equals(entity.getStatusCode()) && ObjectUtils.isNotEmpty(entity.getBody())) {
return entity.getBody();
} else {
return AnYanResult.error("外部平台API调用失败");
}
}
}
@@ -0,0 +1,56 @@
package com.renkang.anyan.constants;
import org.jeecg.util.RedisKeyPrefix;
/**
* @Description: AnYanConstants 常量
* @Author: feng
* @Date: 2025-8-01
* @Version: V1.0
*/
public interface AnYanConstants {
/**
* token传递请求头参数
*/
String AUTHORIZATION = "authorization";
/**
* token类型
*/
String TOKEN_TYPE = "bearer ";
/**
* 部分业务接口需要代入的请求头参数X-Auth-Id
*/
String HEADER_PARAM_X_AUTH_ID = "X-Auth-Id";
/**
* 请求头参数X-Auth-Id 固定前缀
*/
String X_AUTH_ID_PREFIX = "&id:";
/**
* 缓存调用安眼系统接口鉴权token缓存key
*/
String AN_YAN_API_TOKEN_PREFIX = "exterior:anyan:api:";
/**
* 缓存安眼用户登录token
*/
String AN_YAN_API_USER_TOKEN_PREFIX = "exterior:anyan:api:access_token";
/**
* 缓存安眼用户信息
*/
String AN_YAN_API_USERINFO_PREFIX = "exterior:anyan:api:userinfo";
/**
* 部分业务接口需要代入的请求头参数Roles
*/
String HEADER_PARAM_ROLES = "Roles";
/**
* 请求头Roles参数值
*/
String HEADER_PARAM_ROLES_VALUES = "company-health,aygc";
}
@@ -0,0 +1,147 @@
package com.renkang.anyan.enums;
import lombok.Getter;
import org.springframework.http.HttpMethod;
/**
* @Description: AnYanApiEnum xj安眼系统接口枚举类
* @Author: feng
* @Date: 2025-8-1
* @Version: V1.0
*/
@Getter
public enum AnYanApiEnum {
/**
* 1.获取token接口
*/
QUERY_AUTH_TOKEN("/oauth/oauth/token", "", HttpMethod.POST),
/**
* 2.体检人数统计查询
*/
HEALTH_EXAMINATION_STATISTICS("/aygc-znhpt-sys/0/aystatistics/peopleHealthExaminationStatistics", "", HttpMethod.POST),
/**
* 3.健康打卡数据统计查询(血压,减重,血糖)
*/
HEALTH_CHECK_STATISTICS("/aygc-znhpt-sys/0/aystatistics/healthCheckStatistics", "", HttpMethod.POST),
/**
* 4.健康打卡异常数据详细查询(血压)
*/
BLOOD_PRESSURE_LIST("/aygc-znhpt-sys/0/aystatistics/bloodPressureList", "", HttpMethod.POST),
/**
* 5.健康打卡异常数据详细查询(减重)
*/
WEIGHT_PRESSURE_LIST("/aygc-znhpt-sys/0/aystatistics/weightPressureList", "", HttpMethod.POST),
/**
* 6.健康打卡异常数据详细查询(血糖)
*/
BLOOD_SUGAR_CLOCKS_LIST("/aygc-znhpt-sys/0/aystatistics/bloodSugarClockSList", "", HttpMethod.POST),
/**
* 7.体检异常数据统计查询(暂定)
*/
MEDICAL_ABNORMAL_STATISTICS("/aygc-znhpt-sys/0/aystatistics/checkstatistics", "", HttpMethod.POST),
/**
* 8.体检异常数据清单(暂定)
*/
MEDICAL_ABNORMAL_LIST("/aygc-znhpt-sys/0/aystatistics/checkList", "", HttpMethod.POST),
/**
* 9.油田医院数据查询
*/
HOSPITAL_LIST("/aygc-znhpt-sys/0/aystatistics/HospitalList", "", HttpMethod.POST),
/**
* 10.健康小屋基础数据查询
*/
HEALTH_PLACE_ROOM_LIST("/aygc-znhpt-sys/0/aystatistics/HealthPlaceHomList", "", HttpMethod.POST),
/**
* 11.健康小屋详细数据查询
*/
HEALTH_PLACE_ROOM_INFO_LIST("/aygc-znhpt-sys/0/aystatistics/initEmpHealthRoomCheckinInfoPages", "", HttpMethod.POST),
/**
* 12.从外网地址获取安眼token接口
*/
PUBLIC_NETWORK_AUTH_TOKEN("/aygc-service-sys/0/api/xj/outside/getToken", "", HttpMethod.POST),
/**
* 13.获取员工信息及单位-V2
*/
USER_INFO_UNIT_V2("/iosp-iam/v2/users/self", "?containsUnit=true", HttpMethod.GET),
/**
* 14.查询AED信息
*/
QUERY_AN_YAN_AED("/aygc-znhpt-sys/0/api/aedEquipedInfo/findPage", "", HttpMethod.POST),
/**
* 15.一人一案最新检查员工列表查询接口 (大屏首页重点关爱趋势预警滚动列表,固定查100前项)
*/
CARE_PERSON_BY_TIME("/aygc-znhpt-sys/0/api/empPersonFile/listByCreateTime", "", HttpMethod.POST),
/**
* 16.一人一案信息详情接口 (用大屏首页重点关爱趋势预警滚动列表点击数据详情)
*/
CARE_PERSON_Details("/aygc-znhpt-sys/0/api/empPersonFile/fileDetails", "", HttpMethod.POST),
/**
* 17.一人一案分页列表接口 (用大屏首页重点关爱趋势二级弹窗列表)
*/
CARE_PERSON_PAGE_LIST("/aygc-znhpt-sys/0/api/empPersonFile/findPage", "", HttpMethod.POST),
/**
* 18.查询关爱联络员信息及急救人员查询接口
*/
QUERY_EMPLOYEE_CARE_PERSON("/aygc-znhpt-sys/0/api/empCareEmp/findPage", "", HttpMethod.POST),
/**
* 19.批量更新员工关键信息接口
*/
FIND_VITAL_INFO("/aygc-znhpt-sys/0/api/empBasicInfo/find-vital-info","", HttpMethod.POST),
/**
* 20.更新单个员工扩展信息
*/
FIND_BY_EMPLOYEENUM("/aygc-znhpt-sys/0/api/empBasicInfo/find-by-employeeNum","", HttpMethod.POST),
/**
* 22.体检数据
*/
HEALTH_CHECK_NEW("/aygc-znhpt-sys/0/api/empHealthCheckNew/findPage","",HttpMethod.POST),
/**
* 23.根据体检id查询体检数据
*/
CHECK_NEW_BY_ID("/aygc-znhpt-sys/0/api/empHealthCheckNew","",HttpMethod.GET),
/**
* 24.根据检查id、科室编号、科室名称查询体检记录
*/
HEALTH_CHECK_PROJECT("/aygc-znhpt-sys/0/api/empHealthCheckNew/getCheckProjects","",HttpMethod.POST);
/**
* 接口路径
*/
private String methodPath;
/**
* 接口的路径参数
*/
private String methodParam;
/**
* 接口请求方式
*/
private HttpMethod httpMethod;
AnYanApiEnum(String methodPath, String methodParam, HttpMethod httpMethod) {
this.methodPath = methodPath;
this.methodParam = methodParam;
this.httpMethod = httpMethod;
}
}
@@ -0,0 +1,39 @@
package com.renkang.anyan.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "AED接口查询参数", description = "AED接口查询参数")
public class AnYanAedParam {
/**
* 二级单位名称
*/
@Schema(title = "二级单位名称")
private String secondOfficeName;
/**
* 三级单位名称
*/
@Schema(title = "三级单位名称")
private String thirdOfficeName;
/**
* 提供地点
*/
@Schema(title = "提供地点")
private String providePlace;
/**
* pageNo
*/
@Schema(title = "pageNo")
private Integer pageNo = 1;
/**
* pageSize
*/
@Schema(title = "pageSize")
private Integer pageSize = 9999;
/**
* pageType
*/
@Schema(title = "pageType")
private String pageType = "plus";
}
@@ -0,0 +1,41 @@
package com.renkang.anyan.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统体检数据接口参数", description = "安眼系统体检数据接口参数")
public class AnYanHealthCheckNewParam {
@JsonProperty("name")
@Schema(name = "员工姓名")
private String name;
@JsonProperty("hospitalName")
@Schema(name = "医院名称")
private String hospitalName;
@JsonProperty("checkDateEnd")
@Schema(name = "结束检查日期")
private String checkDateEnd;
@JsonProperty("checkDateStart")
@Schema(name = "开始检查日期")
private String checkDateStart;
@JsonProperty("employeeNum")
@Schema(name = "统一认证编号")
private String employeeNum;
@JsonProperty("pageNo")
@Schema(name = "页码")
private Integer pageNo = 1;
@JsonProperty("pageSize")
@Schema(name = "页码")
private Integer pageSize = 10;
@Schema(title = "pageType")
private String pageType = "plus";
}
@@ -0,0 +1,14 @@
package com.renkang.anyan.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class AnYanHealthCheckProjectParam {
@Schema(title = "检查id")
private String checkId;
@Schema(title = "科室编号")
private String departNo;
@Schema(title = "科室名称")
private String departName;
}
@@ -0,0 +1,45 @@
package com.renkang.anyan.model.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统健康小屋接口参数", description = "安眼系统健康小屋接口参数")
public class AnYanHealthRoomParam {
@JsonProperty("checkInDateBegin")
@Schema(name = "检测开始时间")
private String checkInDateBegin;
@JsonProperty("checkInDateEnd")
@Schema(name = "检测结束时间")
private String checkInDateEnd;
@JsonProperty("checkType")
@Schema(name = "检测类型")
private String checkType;
@JsonProperty("healthRoomId")
@Schema(name = "小屋id")
private String healthRoomId;
@JsonProperty("name")
@Schema(name = "名称")
private String name;
@JsonProperty("pageNo")
@Schema(name = "页码")
private Integer pageNo = 1;
@JsonProperty("pageSize")
@Schema(name = "页码")
private Integer pageSize = 10;
@JsonProperty("successFlag")
@Schema(name = "是否成功空降 目前各个小屋没有这个值")
private String successFlag;
@Schema(title = "pageType")
private String pageType = "plus";
}
@@ -0,0 +1,32 @@
package com.renkang.anyan.model.dto;
import lombok.Data;
/**
* @author PengJ
* @description: 密码鉴权入参
* @date 2025/10/24 11:04
*/
@Data
public class AuthByPasswordParam {
/**
* grant_type
*/
private String grant_type = "password";
/**
* username
*/
private String username;
/**
* password
*/
private String password;
/**
* client_id
*/
private String client_id;
/**
* client_secret
*/
private String client_secret;
}
@@ -0,0 +1,19 @@
package com.renkang.anyan.model.dto;
import lombok.Data;
@Data
public class AuthParam {
/**
*grant_type
*/
private String grant_type = "client_credentials";
/**
* client_id
*/
private String client_id;
/**
* client_secret
*/
private String client_secret;
}
@@ -0,0 +1,40 @@
package com.renkang.anyan.model.dto;
import cn.hutool.core.date.DateUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "AED接口查询参数", description = "AED接口查询参数")
public class CareUserDetailsParam {
/**
* 员工姓名
*/
@Schema(title = "员工姓名")
private String name;
/**
* 工作单位
*/
@Schema(title = "工作单位")
private String workUnit;
/**
* 数据级别
*/
@Schema(title = "数据级别")
private String dataLevel = "0";
/**
* pageNo
*/
@Schema(title = "pageNo")
private Integer pageNo = 1;
/**
* pageSize
*/
@Schema(title = "pageSize")
private Integer pageSize = 10;
@Schema(title = "pageType")
private String pageType = "plus";
private int year = DateUtil.thisYear();
}
@@ -0,0 +1,42 @@
package com.renkang.anyan.model.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "应急联系人查询参数", description = "应急联系人查询参数")
public class EmergencyContactParam {
/**
* 员工ID
*/
@Schema(title = "员工ID")
private String emId;
/**
* 工号
*/
@Schema(title = "工号")
private String employeeNum;
/**
* 身份证号
*/
@Schema(title = "身份证号")
private String idNo;
/**
* 数据级别
*/
@Schema(title = "数据级别")
private String dataLevel = "0";
/**
* pageNo
*/
@Schema(title = "pageNo")
private Integer pageNo = 1;
/**
* pageSize
*/
@Schema(title = "pageSize")
private Integer pageSize = 10;
@Schema(title = "pageType")
private String pageType = "plus";
}
@@ -0,0 +1,77 @@
package com.renkang.anyan.model.dto;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data
public class HealthRoomDTO {
/**
* 健康小屋ID
*/
@Schema(title = "健康小屋ID")
@NotNull
private String healthRoomId;
/**
* 检查类型 11:BMI/21:收缩压/22:舒张压/31:血糖/41:低密度脂蛋白/42:总胆固醇/43:甘油三酯/51:尿酸/61:同型半胱氨酸 等等,缺失的可以继续加
*/
@Schema(title = "检查类型 11:BMI/21:收缩压/22:舒张压/31:血糖/41:低密度脂蛋白/42:总胆固醇/43:甘油三酯/51:尿酸/61:同型半胱氨酸 等等,缺失的可以继续加")
@Dict(dicCode = "xj_check_type")
private String checkType;
/**
* 单位名称
*/
@Schema(title = "单位名称")
private String departName;
/**
* 员工姓名
*/
@Schema(title = "员工姓名")
private String name;
/**
* 检查日期起期 参数为空时,默认为今年第一天,如:2025-01-01
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "检查日期起期 参数为空时,默认为今年第一天,如:2025-01-01")
private Date checkInDateBegin;
/**
* 检查日期止期 参数为空时,默认为今天
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "检查日期止期 参数为空时,默认为今天")
private Date checkInDateEnd;
/**
* 是否成功控降 是否成功空降,1:是/0.否
*/
@Schema(title = "是否成功控降 是否成功空降,1:是/0.否")
private String successFlag;
/**
* 页码 从1开始
*/
@Schema(title = "页码 从1开始")
private Integer pageNo;
/**
* 每页数量
*/
@Schema(title = "每页数量")
private Integer pageSize;
@Schema(title = "pageType")
private String pageType = "plus";
public HealthRoomDTO() {
Date date = new Date();
this.checkInDateBegin = DateUtil.beginOfYear(date);
this.checkInDateEnd = DateUtil.endOfDay(date);
this.pageNo = 1;
this.pageSize = 10;
}
}
@@ -0,0 +1,52 @@
package com.renkang.anyan.model.dto;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
* 健康打卡异常数据详情查询参数
*/
@Data
public class HealthSignListParam {
/**
*单位
*/
@Schema(title = "单位")
private String departName;
/**
* 员工姓名
*/
@Schema(title = "员工姓名")
private String name;
/**
* 开始时间
*/
@Schema(title = "开始时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date inputDateBegin;
/**
*结束时间
*/
@Schema(title = "结束时间")
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date inputDateEnd;
/**
* pageNo
*/
@Schema(title = "pageNo")
private Integer pageNo = 1;
/**
* pageSize
*/
@Schema(title = "pageSize")
private Integer pageSize = 10;
@Schema(title = "pageType")
private String pageType = "plus";
}
@@ -0,0 +1,71 @@
package com.renkang.anyan.model.dto;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.util.Date;
@Data
public class MedicalAbnormalDTO {
/**
* 单位名称
*/
private String departName;
/**
* 员工姓名
*/
private String name;
/**
* 体检年份
*/
private String medicalYear;
/**
* 体检医院
*/
private String hospitalName;
/**
* 检查日期起期 参数为空时,默认为今年第一天,如:2025-01-01
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "检查日期起期 参数为空时,默认为今年第一天,如:2025-01-01")
private Date checkInDateBegin;
/**
* 检查日期止期 参数为空时,默认为今天
*/
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@DateTimeFormat(pattern = "yyyy-MM-dd")
@Schema(title = "检查日期止期 参数为空时,默认为今天")
private Date checkInDateEnd;
/**
* 查询类型 1:心血管异常/2:脑血管异常/3:冠脉CT异常
*/
@Schema(title = "查询类型 1:心血管异常/2:脑血管异常/3:冠脉CT异常")
@NotNull
private String queryType;
/**
* 页码 从1开始
*/
@Schema(title = "页码 从1开始")
private Integer pageNo;
/**
* 每页数量
*/
@Schema(title = "每页数量")
private Integer pageSize;
@Schema(title = "pageType")
private String pageType = "plus";
public MedicalAbnormalDTO() {
Date date = new Date();
this.checkInDateBegin = DateUtil.beginOfYear(date);
this.checkInDateEnd = DateUtil.endOfDay(date);
this.pageNo = 1;
this.pageSize = 10;
}
}
@@ -0,0 +1,78 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
@Schema(title = "安眼系统AED数据详情", description = "安眼系统AED数据详情")
public class AnYanAedInfo {
/**
* id
*/
private String id;
/**
* 二级单位名称
*/
@Schema(title = "二级单位名称")
private String secondOfficeName;
/**
* 三级单位名称
*/
@Schema(title = "三级单位名称")
private String thirdOfficeName;
/**
* 提供地点
*/
@Schema(title = "提供地点")
private String providePlace;
/**
* 电话
*/
@Schema(title = "电话")
private String tel;
/**
* 经度
*/
@Schema(title = "经度")
private String lon;
/**
* 纬度
*/
@Schema(title = "纬度")
private String lat;
/**
* 创建时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "创建时间")
private Date createDate;
/**
* 更新时间
*/
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "更新时间")
private Date updateDate;
public String getSecondOfficeName() {
if(null != secondOfficeName){
// 处理返数据中的空格和换行符
return secondOfficeName.replaceAll(" ","").replaceAll("\r\n","");
}
return "";
}
public String getThirdOfficeName() {
if(null != thirdOfficeName){
// 处理返数据中的空格和换行符
return thirdOfficeName.replaceAll(" ","").replaceAll("\r\n","");
}
return "";
}
}
@@ -0,0 +1,31 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统体检数据接口返回", description = "安眼系统体检数据接口返回")
public class AnYanEmpHealthCheckNew {
@Schema(title = "姓名")
private String name;
@Schema(title = "性别编号")
private String sexCode;
@Schema(title = "性别")
private String sexCodeText;
@Schema(title = "出生日期")
private String birthday;
@Schema(title = "身份证号")
private String idNo;
@Schema(title = "单位全称")
private String workUnit;
@Schema(title = "民族编号")
private String nationalityValue;
@Schema(title = "民族")
private String nationalityValueText;
@Schema(title = "电话号码")
private String phone;
@Schema(title = "员工编号 非统一认证编号")
private String empId;
@Schema(title = "体检数据")
private AnYanMedicalNew empHealthCheckNew;
}
@@ -0,0 +1,22 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class AnYanHealthCheckDepart {
@Schema(title = "科室记录编号")
private String id;
@Schema(title = "科室体检编号")
private String checkId;
@Schema(title = "科室编号")
private String departNo;
@Schema(title = "科室名称")
private String departName;
@Schema(title = "小结")
private String summary;
@Schema(title = "小结日期")
private String summaryDate;
@Schema(title = "小结医生(科室医生)")
private String summaryDoctor;
}
@@ -0,0 +1,66 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统体检数据接口返回", description = "安眼系统体检数据接口返回")
public class AnYanHealthCheckNew {
@JsonProperty("id")
@Schema(name = "id")
private String id;
@JsonProperty("idCard")
@Schema(name = "身份证号")
private String idCard;
@JsonProperty("name")
@Schema(name = "员工姓名")
private String name;
@JsonProperty("hospitalName")
@Schema(name = "员工体检医院")
private String hospitalName;
@JsonProperty("checkDate")
@Schema(name = "员工体检日期")
private String checkDate;
@JsonProperty("checkDoctor")
@Schema(name = "员工体检医生")
private String checkDoctor;
@JsonProperty("checkId")
@Schema(name = "员工体检编号")
private String checkId;
@JsonProperty("workUnit")
@Schema(name = "员工单位名称")
private String workUnit;
@JsonProperty("sexCode")
@Schema(name = "员工性别编号")
private String sexCode;
@JsonProperty("sexCodeText")
@Schema(name = "员工性别")
private String sexCodeText;
@JsonProperty("nationalityValue")
@Schema(name = "员工民族编号")
private String nationalityValue;
@JsonProperty("nationalityValueText")
@Schema(name = "员工民族")
private String nationalityValueText;
@JsonProperty("phone")
@Schema(name = "员工电话号")
private String phone;
@JsonProperty("employeeNum")
@Schema(name = "员工统一认证编号")
private String employeeNum;
}
@@ -0,0 +1,70 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统健康小屋接口返回", description = "安眼系统健康小屋接口返回")
public class AnYanHealthRoom {
@JsonProperty("id")
@Schema(name = "id")
private String id;
@JsonProperty("departId")
@Schema(name = "单位id")
private String departId;
@JsonProperty("departName")
@Schema(name = "单位名称")
private String departName;
@JsonProperty("roomId")
@Schema(name = "健康小屋id")
private String roomId;
@JsonProperty("name")
@Schema(name = "名称")
private String name;
@JsonProperty("idCard")
@Schema(name = "身份证号")
private String idCard;
@JsonProperty("checkInDate")
@Schema(name = "检查日期")
private String checkInDate;
@JsonProperty("checkType")
@Schema(name = "检查类型")
private String checkType;
@JsonProperty("checkTypeName")
@Schema(name = "检查类型名称")
private String checkTypeName;
@JsonProperty("lastVal")
@Schema(name = "去年体检值 目前各个小屋没有这个值")
private String lastVal;
@JsonProperty("goalVal")
@Schema(name = "标值 各个小屋的指标并不一致")
private String goalVal;
@JsonProperty("currentVal")
@Schema(name = "当前测量值")
private String currentVal;
@JsonProperty("compareVal")
@Schema(name = "控降差值 目前各个小屋没有这个值")
private String compareVal;
@JsonProperty("successFlag")
@Schema(name = "是否成功空降 目前各个小屋没有这个值")
private String successFlag;
@JsonProperty("employeeNum")
@Schema(name = "员工统一编号")
private String employeeNum;
}
@@ -0,0 +1,41 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Data
public class AnYanMedicalNew {
@Schema(title = "查询id")
private String id;
@Schema(title = "身份证号")
private String idCard;
@Schema(title = "名称")
private String name;
@Schema(title = "医院名称")
private String hospitalName;
@Schema(title = "检查日期")
private String checkDate;
@Schema(title = "检查医生名称")
private String checkDoctor;
@Schema(title = "医院检查编号")
private String checkNo;
@Schema(title = "检查编号")
private String checkId;
@Schema(title = "体检综述")
private String checkResult;
@Schema(title = "体检建议")
private String checkSuggestion;
@Schema(title = "科室体检数据")
private List<AnYanHealthCheckDepart> healthCheckDepartList;
@Schema(title = "二级单位id")
private String secondOfficeId;
@Schema(title = "二级单位名称")
private String secondOfficeName;
@Schema(title = "三级单位id")
private String thirdOfficeId;
@Schema(title = "三级单位名称")
private String thirdOfficeName;
}
@@ -0,0 +1,170 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.constant.CommonConstant;
import java.io.Serializable;
@Data
@Schema(title = "安眼系统接口返回对象", description = "安眼系统接口返回对象")
public class AnYanResult<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 成功标志
*/
@Schema(title = "成功标志")
private boolean success = true;
/**
* 返回处理消息
*/
@Schema(title = "返回处理消息")
private String message = "";
/**
* 返回代码
*/
@Schema(title = "返回代码")
private Integer code = 0;
/**
* 返回数据对象 data
*/
@Schema(title = "返回数据对象")
private T data;
/**
* 时间戳
*/
@Schema(title = "时间戳")
private long timestamp = System.currentTimeMillis();
@JsonIgnore
private String onlTable;
public AnYanResult() {
}
/**
* 兼容VUE3版token失效不跳转登录页面
*
* @param code
* @param message
*/
public AnYanResult(Integer code, String message) {
this.code = code;
this.message = message;
}
public static <T> AnYanResult<T> ok() {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
return r;
}
public static <T> AnYanResult<T> ok(String msg) {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
r.setData((T) msg);
r.setMessage(msg);
return r;
}
public static <T> AnYanResult<T> ok(T data) {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setData(data);
return r;
}
public static <T> AnYanResult<T> OK() {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
return r;
}
/**
* 此方法是为了兼容升级所创建
*
* @param msg
* @param <T>
* @return
*/
public static <T> AnYanResult<T> OK(String msg) {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage(msg);
//Result OK(String msg)方法会造成兼容性问题 issues/I4IP3D
r.setData((T) msg);
return r;
}
public static <T> AnYanResult<T> OK(T data) {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setData(data);
return r;
}
public static <T> AnYanResult<T> OK(String msg, T data) {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(true);
r.setCode(CommonConstant.SC_OK_200);
r.setMessage(msg);
r.setData(data);
return r;
}
public static <T> AnYanResult<T> error(String msg, T data) {
AnYanResult<T> r = new AnYanResult<T>();
r.setSuccess(false);
r.setCode(CommonConstant.SC_INTERNAL_SERVER_ERROR_500);
r.setMessage(msg);
r.setData(data);
return r;
}
public static <T> AnYanResult<T> error(String msg) {
return error(CommonConstant.SC_INTERNAL_SERVER_ERROR_500, msg);
}
public static <T> AnYanResult<T> error(int code, String msg) {
AnYanResult<T> r = new AnYanResult<T>();
r.setCode(code);
r.setMessage(msg);
r.setSuccess(false);
return r;
}
/**
* 无权限访问返回结果
*/
public static <T> AnYanResult<T> noauth(String msg) {
return error(CommonConstant.SC_JEECG_NO_AUTHZ, msg);
}
public AnYanResult<T> success(String message) {
this.message = message;
this.code = CommonConstant.SC_OK_200;
this.success = true;
return this;
}
public AnYanResult<T> error500(String message) {
this.message = message;
this.code = CommonConstant.SC_INTERNAL_SERVER_ERROR_500;
this.success = false;
return this;
}
}
@@ -0,0 +1,23 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统批量更新员工关键信息接口返回对象", description = "安眼系统批量更新员工关键信息接口返回对象")
public class AnYanUpdateUserBatchInfo {
@JsonProperty("id")
private String id;
@JsonProperty("employeeNum")
private String employeeNum;
@JsonProperty("phone")
private String phone;
@JsonProperty("idNo")
private String idNo;
}
@@ -0,0 +1,141 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title = "安眼系统更新单个员工扩展信息接口返回对象", description = "安眼系统更新单个员工扩展信息接口返回对象")
public class AnYanUserEmployee {
@JsonProperty("id")
private String id;
@JsonProperty("employeeNum")
@Schema(name = "员工统一认证编号")
private String employeeNum;
@JsonProperty("name")
@Schema(name = "员工名称")
private String name;
@JsonProperty("sexCode")
@Schema(name = "员工性别编号")
private String sexCode;
@JsonProperty("sexCodeText")
@Schema(name = "员工性别")
private String sexCodeText;
@JsonProperty("nationalityValue")
@Schema(name = "员工民族编号")
private String nationalityValue;
@JsonProperty("nationalityValueText")
@Schema(name = "员工民族")
private String nationalityValueText;
@JsonProperty("phone")
@Schema(name = "员工手机号")
private String phone;
@JsonProperty("idNo")
@Schema(name = "员工身份证号")
private String idNo;
@JsonProperty("birthPlace")
@Schema(name = "出生地")
private String birthPlace;
@JsonProperty("educationCode")
@Schema(name = "文化程度编号")
private String educationCode;
@JsonProperty("educationCodeText")
@Schema(name = "文化程度")
private String educationCodeText;
@JsonProperty("marriageCode")
@Schema(name = "婚姻概况编号")
private String marriageCode;
@JsonProperty("marriageCodeText")
@Schema(name = "婚姻概况")
private String marriageCodeText;
@JsonProperty("householdTypeCode")
@Schema(name = "户籍性质")
private String householdTypeCode;
@JsonProperty("workUnitId")
@Schema(name = "单位id 原工作单位")
private String workUnitId;
@JsonProperty("parentWorkIds")
@Schema(name = "父单位id")
private String parentWorkIds;
@JsonProperty("currentOfficeId")
@Schema(name = "现工作单位id")
private String currentOfficeId;
@JsonProperty("currentOfficeIdText")
@Schema(name = "现工作单位")
private String currentOfficeIdText;
@JsonProperty("workUnit")
@Schema(name = "单位名称")
private String workUnit;
@JsonProperty("birthday")
@Schema(name = "出生日期")
private String birthday;
@JsonProperty("homeId")
@Schema(name = "健康小屋id")
private String homeId;
@JsonProperty("homePlace")
@Schema(name = "健康小屋地点")
private String homePlace;
@JsonProperty("posts")
@Schema(name = "职位编号")
private String posts;
@JsonProperty("postsText")
@Schema(name = "职位")
private String postsText;
@JsonProperty("heartBloodVesselCheck")
@Schema(name = "心血管检查 有/无")
private String heartBloodVesselCheck;
@JsonProperty("brainBloodVesselCheck")
@Schema(name = "脑血管检查 有/无")
private String brainBloodVesselCheck;
@JsonProperty("cancerCheck")
@Schema(name = "癌症筛查 有/无")
private String cancerCheck;
@JsonProperty("ctaResult")
@Schema(name = "冠状CTA检查结果 1: '无', 2: '轻度狭窄',3: '中度狭窄',4: '重度狭窄'")
private Integer ctaResult;
@JsonProperty("age")
@Schema(name = "年龄")
private Integer age;
@JsonProperty("isHealthAdmin")
@Schema(name = "是否为健康管理员 1-是 2-否")
private Integer isHealthAdmin;
@JsonProperty("inFormedBookAttached")
@Schema(name = "是否上传知情同意书 有值-是 null-否")
private String inFormedBookAttached;
@JsonProperty("existHealthAdmCommit")
@Schema(name = "是否上传保密承诺书 true-是 false-否")
private Boolean existHealthAdmCommit;
}
@@ -0,0 +1,149 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author Huawei
* @version 1.0
* @description: TODO
* @date 2025/10/24 16:59
*/
@NoArgsConstructor
@Data
public class AnYanUserInfoResult {
@JsonProperty("id")
private String id;
@JsonProperty("loginName")
private String loginName;
@JsonProperty("email")
private String email;
@JsonProperty("organizationId")
private Integer organizationId;
@JsonProperty("realName")
private String realName;
@JsonProperty("phone")
private String phone;
@JsonProperty("gender")
private Integer gender;
@JsonProperty("tenantName")
private String tenantName;
@JsonProperty("tenantNum")
private String tenantNum;
@JsonProperty("roleMergeFlag")
private Integer roleMergeFlag;
@JsonProperty("tenantId")
private Integer tenantId;
@JsonProperty("currentRoleId")
private String currentRoleId;
@JsonProperty("currentRoleCode")
private String currentRoleCode;
@JsonProperty("currentRoleName")
private String currentRoleName;
@JsonProperty("currentRoleLevel")
private String currentRoleLevel;
@JsonProperty("employeeUnitDTO")
private EmployeeUnitDTO employeeUnitDTO;
@NoArgsConstructor
@Data
public static class EmployeeUnitDTO {
/**
* 职工编号
*/
@JsonProperty("employeeNum")
private String employeeNum;
/**
* 姓名
*/
@JsonProperty("name")
private String name;
/**
* 邮箱
*/
@JsonProperty("email")
private String email;
/**
* 手机
*/
@JsonProperty("mobile")
private String mobile;
/**
* 性别,0男,1女
*/
@JsonProperty("gender")
private Integer gender;
@JsonProperty("status")
private Object status;
/**
* 启用状态
*/
@JsonProperty("enabledFlag")
private String enabledFlag;
@JsonProperty("unitName")
private String unitName;
/**
* 所属二级单位unitCode
*/
@JsonProperty("unitCode")
private String unitCode;
/**
* 科级单位名称
*/
@JsonProperty("sectionUnitName")
private String sectionUnitName;
/**
* 科级单位unitCode
*/
@JsonProperty("sectionUnitCode")
private String sectionUnitCode;
@JsonProperty("directDepartmentName")
private String directDepartmentName;
/**
* 直属部门unitCode
*/
@JsonProperty("directDepartmentCode")
private String directDepartmentCode;
/**
* 层级路径(unitCode拼接)
*/
@JsonProperty("levelPath")
private String levelPath;
/**
* 完整组织机构路径(unitName拼接)
*/
@JsonProperty("unitNamePath")
private String unitNamePath;
/**
* 固定电话
*/
@JsonProperty("attribute1")
private String attribute1;
/**
* 职务
*/
@JsonProperty("attribute2")
private String attribute2;
/**
* 生日或年龄
*/
@JsonProperty("attribute3")
private Object attribute3;
}
}
@@ -0,0 +1,39 @@
package com.renkang.anyan.model.vo;
import lombok.Data;
@Data
public class AuthResult {
/**
*范围
*/
private String scope;
/**
*有效期
*/
private Integer expires_in;
/**
* token_type
*/
private String token_type;
/**
* token
*/
private String access_token;
/**
* refresh_token
*/
private String refresh_token;
/**
* satoken
*/
private String satoken;
/**
* message
*/
private String message;
/**
* success
*/
private boolean success;
}
@@ -0,0 +1,36 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title="健康打卡血压异常数据详细", description="健康打卡血压异常数据详细")
public class BloodPressureList extends XjUserBaseInfo {
/**
*收缩压
*/
@Schema(title = "收缩压")
private String sbp;
/**
*收缩压是否异常
*/
@Schema(title = "收缩压是否异常")
private String sbpFlag;
/**
*舒张压
*/
@Schema(title = "舒张压")
private String dbp;
/**
*舒张压是否异常
*/
@Schema(title = "舒张压是否异常")
private String dbpFlag;
/**
*填报时间 YYYY-MM-DD
*/
@Schema(title = "填报时间")
private String inputDate;
}
@@ -0,0 +1,26 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title="健康打卡血糖异常数据详细", description="健康打卡血糖异常数据详细")
public class BloodSugarClockSList extends XjUserBaseInfo {
/**
*血糖值
*/
@Schema(title = "血糖值")
private String bsVal;
/**
*血糖是否异常
*/
@Schema(title = "血糖是否异常")
private String bsFlag;
/**
*填报时间 YYYY-MM-DD
*/
@Schema(title = "填报时间")
private String inputDate;
}
@@ -0,0 +1,48 @@
package com.renkang.anyan.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Data
public class CarePersonList {
/**
* 姓名
*/
@Schema(title = "姓名")
private String name;
/**
* 员工ID
*/
@Schema(title = "员工ID")
private String empId;
/**
* 性别 (返回的是'男''女',汉字)
*/
@Schema(title = "性别")
private String sex;
/**
* 年龄
*/
@Schema(title = "年龄")
private Integer age;
/**
* 身份证号
*/
@Schema(title = "身份证号")
private String idNo;
/**
* 检查年份
*/
@Schema(title = "检查年份")
private String checkYear;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "最新检查时间")
private Date createDate;
}
@@ -0,0 +1,191 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.Period;
import java.time.format.DateTimeFormatter;
@Data
@Schema(title="重点关爱趋势预警信息",description="重点关爱趋势预警信息")
public class CareUserListVo {
@Schema(title = "ID")
private String id;
@Schema(title = "员工ID")
private String empId;
@Schema(title = "员工姓名")
private String name;
@Schema(title = "年龄")
private Integer age;
@Schema(title = "性别")
private String sexCode;
@Schema(title = "身份证号")
private String idNo;
@Schema(title = "工作单位")
private String workUnit ;
@Schema(title = "单位ID")
private String workUnitId;
@Schema(title = "创建时间")
private String createDate;
@Schema(title = "修改时间")
private String updateDate;
@Schema(title = "数据级别")
private Integer dataLevel;
@Schema(title = "负面清单")
private String negativeList;
@Schema(title = "一人一案")
private String caseInfo;
@Schema(title = "肥胖人员标识(1,是,2-否)")
private Integer flagFat;
@Schema(title = "BMI")
private Double bmi;
@Schema(title = "身高")
private Double height;
@Schema(title = "体重")
private Double weight;
@Schema(title = "体重管理目标")
private Double targetWeight;
@Schema(title = "目前BMI监测值")
private String bmiCurrent;
@Schema(title = "高血压(0-否,1-是)")
private Integer hypertension;
@Schema(title = "收缩压均值")
private String systolicPressureAvg;
@Schema(title = "收缩压控制目标")
private String systolicPressureTarget;
@Schema(title = "目前收缩压检测值")
private String systolicPressureCurrent;
@Schema(title = "舒张压平均值")
private String diastolicPressureAvg;
@Schema(title = "舒张压控制目标值")
private String diastolicPressureTarget;
@Schema(title = "舒张压当前检测值")
private String diastolicPressureCurrent;
@Schema(title = "糖尿病标识(0-否,1-是)")
private Integer diabetes;
@Schema(title = "空腹葡萄糖(GLU)")
private String glu;
@Schema(title = "血糖控制目标值")
private String bloodSugarTarget;
@Schema(title = "目前血糖监测值:")
private String bloodSugarCurrent;
@Schema(title = "低密度脂蛋白")
private String ldl;
@Schema(title = "低密度脂蛋白目标控制值")
private String ldlTarget;
@Schema(title = "低密度脂蛋白当前检测值")
private String ldlCurrent;
@Schema(title = "总胆固醇")
private String tc;
@Schema(title = "总胆固醇目标控制值")
private String tcTarget;
@Schema(title = "总胆固醇目前监测值")
private String tcCurrent;
@Schema(title = "甘油三酯")
private String tg;
@Schema(title = "甘油三酯目标控制值")
private String tgTarget;
@Schema(title = "甘油三酯当前监测值")
private String tgCurrent;
@Schema(title = "尿酸")
private String uricAcid;
@Schema(title = "尿酸目标控制值")
private String uricAcidTarget;
@Schema(title = "尿酸当前监测值")
private String uricAcidCurrent;
@Schema(title = "同型半胱氨酸")
private String homocysteine;
@Schema(title = "同型半胱氨酸目标控制值")
private String homocysteineTarget;
@Schema(title = "同型半胱氨酸当前监测值")
private String homocysteineCurrent;
@Schema(title = "关爱联络员姓名")
private String careLiaisonName;
@Schema(title = "治疗方案反馈")
private String treatPlanResponse;
@Schema(title = "饮食建议反馈")
private String dietSuggestionResponse;
@Schema(title = "运动指导反馈")
private String sportGuideResponse;
@Schema(title = "日常生活管理反馈")
private String lifeManageResponse;
public Double getBmi() {
if (bmi == null) {
return null;
}
return Math.round(bmi * 10) / 10.0;
}
public Integer getAge() {
if (age != null) {
return age;
}
if (idNo == null || idNo.length() != 18) {
return null; // 身份证号无效时返回null
}
try {
// 截取出生日期字符串
String birthDateStr = idNo.substring(6, 14);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");
LocalDate birthDate = LocalDate.parse(birthDateStr, formatter);
LocalDate now = LocalDate.now();
if (birthDate.isAfter(now)) {
return null; // 出生日期在未来,返回null
}
return Period.between(birthDate, now).getYears();
} catch (Exception e) {
return null; // 解析异常返回null
}
}
}
@@ -0,0 +1,24 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class CareUserVO {
private String userId;
@Schema(title = "关爱联络员姓名")
private String careLiaisonName;
@Schema(title = "关爱联络员电话")
private String careLiaisonPhone;
@Schema(title = "关爱联络员职务")
private String careLiaisonJob;
@Schema(title = "急救人员姓名")
private String lifeguardName;
@Schema(title = "急救人员电话")
private String lifeguardPhone;
//20251223 改为两个急救人员 ,为改动最小化 没有改数据结构
@Schema(title = "急救人员2姓名")
private String lifeguardName2;
@Schema(title = "急救人员2电话")
private String lifeguardPhone2;
}
@@ -0,0 +1,99 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title="安眼员工关爱联络员信息及急救人员信息",description="安眼员工关爱联络员信息及急救人员信息")
public class EmergencyContactVO {
@Schema(title = "记录编号")
private String id;
@Schema(title = "关爱员工编号")
private String empId;
@Schema(title = "单位名称")
private String workUnit;
@Schema(title = "关爱身份证号")
private String idNo;
@Schema(title = "联系电话")
private String phone;
@Schema(title = "员工姓名")
private String name;
@Schema(title = "单位id")
private String workUnitId;
@Schema(title = "性别标识(1-男,2-女)")
private String sexCode;
@Schema(title = "性别翻译")
private String sexCodeText;
@Schema(title = "办公场所周边4分钟内是否配备AED(1是 2否)")
private Integer officeAedAvailable;
@Schema(title = "办公场所AED是否能有专人管理(1是 2否)")
private Integer officeAedManaged;
@Schema(title = "办公场所AED管理人员姓名")
private String officeAedManagerName;
@Schema(title = "办公场所AED管理人员电话")
private String officeAedManagerPhone;
@Schema(title = "当班班组是否配备取证急救人员(1是 2否)")
private Integer teamHasFirstaider;
@Schema(title = "急救人员姓名")
private String firstAiderName;
@Schema(title = "急救人员电话")
private String firstAiderPhone;
@Schema(title = "急救人2员姓名")
private String firstAiderName2;
@Schema(title = "急救人2员电话")
private String firstAiderPhone2;
@Schema(title = "重点关爱人员是否倒班(1是 2否)")
private Integer keycareShiftWork;
@Schema(title = "夜晚是否在单位公寓住宿(1是 2否)")
private Integer keycareNightStay;
@Schema(title = "是否有陪同住宿人员(1是 2否)")
private Integer hasCompanion;
@Schema(title = "陪同住宿人员是否取得急救证(1是 2否)")
private Integer companionCertified;
@Schema(title = "宿舍是否安装一键报警(1是 2否)")
private Integer dormAlarmAvailable;
@Schema(title = "公寓周边4分钟内是否配备AED(1是 2否)")
private Integer dormAedAvailable;
@Schema(title = "公寓AED是否能有专人管理(1是 2否)")
private Integer dormAedManaged;
@Schema(title = "关爱联络员id")
private String relationEmpId;
@Schema(title = "关爱联络员名称")
private String relationEmpName;
@Schema(title = "关爱联络员职务编码")
private String relationEmpPosts;
@Schema(title = "关爱联络员职务翻译")
private String relationEmpPostsText;
@Schema(title = "关爱联络员手机号")
private String relationEmpPhone;
}
@@ -0,0 +1,32 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class HealthCheckProject {
@Schema(title = "项目记录编号")
private String id;
@Schema(title = "项目体检编号")
private String checkId;
@Schema(title = "科室编号")
private String departNo;
@Schema(title = "科室名称")
private String departName;
@Schema(title = "项目编号")
private String projectNo;
@Schema(title = "项目名称")
private String projectName;
@Schema(title = "项目体检值")
private String resultValue;
@Schema(title = "是否异常")
private String abnormal;
@Schema(title = "异常结果")
private String abnormalResult;
@Schema(title = "单位")
private String unit;
@Schema(title = "参考值")
private String referenceValue;
@Schema(title = "是否心脑血管项目0否 1是")
private String bloodBrain;
}
@@ -0,0 +1,39 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title="健康小屋数据VO", description="健康小屋数据VO")
public class HealthPlaceHomListVO {
/**
* id
*/
private String id;
/**
* 单位
*/
@Schema(title = "单位")
private String departName;
/**
* 地点
*/
@Schema(title = "地点")
private String address;
/**
* 经度
*/
@Schema(title = "经度")
private String lon;
/**
* 纬度
*/
@Schema(title = "纬度")
private String lat;
/**
* 建设时间 YYYY-MM-DD
*/
@Schema(title = "建设时间(YYYY-MM-DD)")
private String buildDate;
}
@@ -0,0 +1,83 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title="健康小屋数据详情列表", description="健康小屋数据详情列表")
public class HealthPlaceHomeDetailsVO {
/**
* id
*/
@Schema(title = "id")
private String id;
/**
* 单位id
*/
@Schema(title = "单位id")
private String departId;
/**
* 单位
*/
@Schema(title = "单位")
private String departName;
/**
* 健康小屋id
*/
@Schema(title = "健康小屋id")
private String roomId;
@Schema(title = "健康小屋名称")
private String roomName;
@Schema(title = "地址")
private String address;
/**
* 姓名
*/
@Schema(title = "姓名")
private String name;
/**
* 身份证号
*/
@Schema(title = "身份证号")
private String idCard;
/**
* 检查日期
*/
@Schema(title = "检查日期")
private String checkInDate;
/**
* 检查类型 11:BMI/21:收缩压/22:舒张压/31:血糖/41:低密度脂蛋白/42:总胆固醇/43:甘油三酯/51:尿酸/61:同型半胱氨酸 等等,缺失的可以继续加
* 字典废弃 新接口返回了name
*/
@Schema(title = "检查类型")
private String checkType;
@Schema(title = "检查类型名称")
private String checkTypeName;
/**
* 去年体检值 前一年的体检值(初始值)
*/
@Schema(title = "去年体检值 前一年的体检值(初始值)")
private String lastVal;
/**
* 控降目标 一人一档(控降目标值)
*/
@Schema(title = "控降目标 一人一档(控降目标值")
private String goalVal;
/**
* 目前测量值 目前控降数值(可和健康打卡最新值一致)
*/
@Schema(title = "目前测量值 目前控降数值(可和健康打卡最新值一致)")
private String currentVal;
/**
* 控降差值 和控降目标对比
*/
@Schema(title = "控降差值 和控降目标对比")
private String compareVal;
/**
* 是否成功空降,1:是/0.否
*/
@Schema(title = "是否成功空降,1:是/0.否")
private String successFlag;
@Schema(title = "员工统一编号")
private String employeeNum;
}
@@ -0,0 +1,62 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title="新疆健康小屋对象", description="新疆健康小屋对象")
public class HealthRoomBaseVO {
/**
* id
*/
private String id;
/**
* 地址
*/
@Schema(title = "地址")
private String address;
/**
* 单位
*/
@Schema(title = "单位")
private String departName;
/**
* 经度
*/
@Schema(title = "经度")
private String lon;
/**
* 纬度
*/
@Schema(title = "纬度")
private String lat;
/**
* 建设时间 yyyy-MM-dd
*/
@Schema(title = "建设时间")
private String buildDate;
/**
* 在岗人数
*/
@Schema(title = "在岗人数")
private Integer jobNum;
/**
* 运转时间(分钟)
*/
@Schema(title = "运转时间(分钟)")
private Integer runTime;
/**
* 管理人员
*/
@Schema(title = "管理人员")
private String manager;
/**
* 联系电话
*/
@Schema(title = "联系电话")
private String phone;
}
@@ -0,0 +1,40 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title="打卡人数统计", description="打卡人数统计")
public class HealthSignStatVO {
/**
* 减重打卡总人数
*/
@Schema(title = "减重打卡总人数")
private String weigtTotalCount;
/**
* 减重异常人数
*/
@Schema(title = "减重异常人数")
private String weigtAbCount;
/**
* 血压打卡总人数
*/
@Schema(title = "血压打卡总人数")
private String bpTotalCount;
/**
* 血压异常人数
*/
@Schema(title = "血压异常人数")
private String bpAbCount;
/**
* 血糖打卡总人数
*/
@Schema(title = "血糖打卡总人数")
private String bsTotalCount;
/**
* 血糖异常人数
*/
@Schema(title = "血糖异常人数")
private String bsAbCount;
}
@@ -0,0 +1,50 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(title="油田医院VO", description="油田医院VO")
public class HospitalListVO {
/**
* id
*/
@Schema(title = "id")
private String id;
/**
* 医院名称
*/
@Schema(title = "医院名称")
private String hospitalName;
/**
* 别名
*/
@Schema(title = "别名")
private String alias;
/**
* 地点
*/
@Schema(title = "地点")
private String address;
/**
* 电话
*/
@Schema(title = "电话")
private String phone;
/**
* 经度
*/
@Schema(title = "经度")
private String lon;
/**
* 纬度
*/
@Schema(title = "纬度")
private String lat;
/**
* 签约时间 YYYY-MM-DD
*/
@Schema(title = "签约时间(YYYY-MM-DD)")
private String contractDate;
}
@@ -0,0 +1,14 @@
package com.renkang.anyan.model.vo;
import lombok.Data;
@Data
public class MedicalAbnormalStatisticsVO {
private Integer totalCount;
// 冠脉CTA异常人数
private Integer cardiovaAbCount;
// 头颅CTA异常人数
private Integer cerebrovAbCount;
// 冠脉ct异常人数 (未返回)
private Integer cctaAbCount;
}
@@ -0,0 +1,45 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
@Data
public class MedicalAbnormalVO {
/**
* 单位
*/
@Schema(title = "单位")
private String departName;
/**
* 姓名
*/
@Schema(title = "姓名")
private String name;
/**
* 身份证号
*/
@Schema(title = "身份证号")
private String idCard;
/**
* 性别
*/
@Schema(title = "性别")
@Dict(dicCode = "sex2")
private Integer sex;
/**
* 年龄
*/
@Schema(title = "年龄")
private Integer age;
/**
* 体检医院
*/
@Schema(title = "体检医院")
private String hospitalName;
/**
* 检查日期
*/
@Schema(title = "检查日期")
private String checkInDate;
}
@@ -0,0 +1,23 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
public class MedicalPeopleNumStatisticsVO {
/**
* 医院名称
*/
@Schema(title = "医院名称")
private String hospitalName;
/**
* 别名 为了显示或别的目的,医院名称过长导致不好显示,最好增加短的别名,如果没有则和hospitalName保持一致
*/
@Schema(title = "别名")
private String alias;
/**
* 已参检人数
*/
@Schema(title = "已参检人数")
private Integer checkedCount;
}
@@ -0,0 +1,41 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@EqualsAndHashCode(callSuper = true)
@Data
@Schema(title="健康打卡体重异常数据详细", description="健康打卡体重异常数据详细")
public class WeightPressureList extends XjUserBaseInfo {
/**
*腰围(CM
*/
@Schema(title = "腰围(CM")
private String waist;
/**
*身高(M
*/
@Schema(title = "身高(M")
private String height;
/**
*体重(KG
*/
@Schema(title = "体重(KG")
private String weight;
/**
*BMI
*/
@Schema(title = "BMI")
private String bmi;
/**
*BMI是否异常(1:是/0:否)
*/
@Schema(title = "BMI是否异常(1:是/0:否)")
private String bmiFlag;
/**
*填报时间 YYYY-MM-DD
*/
@Schema(title = "填报时间")
private String inputDate;
}
@@ -0,0 +1,36 @@
package com.renkang.anyan.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
* 员工基本信息
*/
@Data
public class XjUserBaseInfo {
/**
*单位
*/
@Schema(title = "单位")
private String departName;
/**
*姓名
*/
@Schema(title = "姓名")
private String name;
/**
*身份证号
*/
@Schema(title = "身份证号")
private String idCard;
/**
*年龄
*/
@Schema(title = "年龄")
private String age;
/**
*性别
*/
@Schema(title = "性别")
private String sex;
}
@@ -0,0 +1,25 @@
package com.renkang.anyan.utils;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.renkang.anyan.model.vo.AnYanResult;
import java.util.stream.Collectors;
/**
* IPage 类型装换
* @param <T>
*/
public class PageResultToBeanUtil<T> {
public IPage<T> transform(AnYanResult<Object> result, Class<T> tClass) {
IPage<?> page = BeanUtil.toBean(result.getData(), Page.class);
IPage<T> iPage = new Page<>();
BeanUtil.copyProperties(page,iPage,"records");
if(page.getTotal() > 0){
iPage.setRecords(page.getRecords().stream().map(o ->BeanUtil.toBean(o, tClass)).collect(Collectors.toList()));
}
return iPage;
}
}
@@ -0,0 +1,47 @@
package com.renkang.anyan.utils;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.SM4;
/**
* XJ安眼平台提供的数据加密工具类 仅用于安眼系统外部登录接口的对接
*/
public class SM4Utils {
private static final String privateKey = "5tgb6yhn7ujm8ik,";
// private static final String privateKey = "123456789qwertyu";
private static final String iv = "5tgb6yhn7ujm8ik,";
// private static final String iv = "abcdefghi1234567";
private static SM4 sm4 = null;
private static SM4 getInstance() {
if (sm4 == null) {
sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, privateKey.getBytes(), iv.getBytes());
}
return sm4;
}
/**
* @description 加密
* @author xj
* @time 2023/8/17
*/
public static String encryptSM4(String needEncryptText) {
return getInstance().encryptBase64(needEncryptText);
}
/**
* @description 解密
* @author xj
* @time 2023/8/17
*/
public static String decryptSM4(String encryptText) {
if (encryptText == null || encryptText.isEmpty()) return "";
return getInstance().decryptStr(encryptText);
}
}
@@ -0,0 +1,242 @@
package com.renkang.anyan.utils;
import cn.hutool.core.util.StrUtil;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* 安眼平台提供的数据加密工具类 仅用于安眼系统外部登录接口的对接
*/
public class StringUtils {
/**
* 公钥加密方法
*
* @param publicKeyText 公钥
* @param text 将要加密的信息
* @return Base64编码的字符串
* @throws Exception
*/
public static String encryptByPublicKey(String publicKeyText, String text) throws Exception {
// 返回按照 X.509 标准进行编码的密钥的字节
// x509EncodedKeySpec2 是一种规范、规格
X509EncodedKeySpec x509EncodedKeySpec2 = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKeyText));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// 让密钥工厂按照指定的 规范 生成公钥
PublicKey publicKey = keyFactory.generatePublic(x509EncodedKeySpec2);
Cipher cipher = Cipher.getInstance("RSA");
// 对加密初始化,使用加密模式,公钥加密
// Cipher.ENCRYPT_MODE 可以用 1 代替
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] result = cipher.doFinal(text.getBytes());
// 返回 Base64编码的字符串
return Base64.getEncoder().encodeToString(result);
// return org.apache.commons.codec.binary.Base64.encodeBase64String(result);
}
public static String base64Encode(String input) {
return Base64.getEncoder().encodeToString(input.getBytes());
}
public static String base64Decode(String encodedInput) {
byte[] decodedBytes = Base64.getDecoder().decode(encodedInput);
return new String(decodedBytes);
}
public static String preprocessJsonString(String jsonStr) {
String a = StrUtil.removeAll(jsonStr, ' ', '\t', '\r', '\n');
String[] searchList = {"\":\"", "\",\"", "{\"", "\"}", "\":"};
String[] replacementList = {"$is$", "$center$", "$left$", "$right$", "$start$"};
String b = org.apache.commons.lang3.StringUtils.replaceEach(a, searchList, replacementList);
String c = b.replace("\"", "\\\"");
return org.apache.commons.lang3.StringUtils.replaceEach(c, replacementList, searchList);
}
/**
* 提取文件名中的扩展名
*
* @param fileName 文件名
* @param needDot 需要点
* @return 文件扩展名,如果文件名中没有扩展名则返回 null
*/
public static String getFileExtension(String fileName, boolean needDot) {
// 检查文件名是否为 null 或空字符串
if (fileName == null || fileName.isEmpty()) {
return null;
}
// 找到最后一个点的位置
int lastDotIndex = fileName.lastIndexOf('.');
// 如果找不到点,或者点在字符串的第一个位置,返回 null
if (lastDotIndex == -1 || lastDotIndex == 0) {
return null;
}
return fileName.substring(needDot ? lastDotIndex : (lastDotIndex + 1));
}
/**
* 获取路径的最后一个元素(文件名或目录名)
*
* @param pathOrUrl 文件路径或 URL
* @return 路径的最后一个元素
*/
public static String baseName(String pathOrUrl) {
if (pathOrUrl == null || pathOrUrl.isEmpty()) {
return "";
}
try {
// 尝试将字符串解析为 URI
URI uri = new URI(pathOrUrl);
// 获取 URI 的路径部分
String path = uri.getPath();
// 使用 Path 类获取路径的最后一个元素
Path filePath = Paths.get(path);
return filePath.getFileName().toString();
} catch (URISyntaxException e) {
// 如果解析 URI 失败,则直接将字符串作为路径处理
Path filePath = Paths.get(pathOrUrl);
return filePath.getFileName().toString();
}
}
// ------------------- AES 加解密(带盐值处理)-------------------
/** 内部密钥,仅用于日志脱敏,非高强度安全场景 */
private static final String AES_PASSWORD = "XJPlatform@2024#Secure";
/** 盐值长度(字节) */
private static final int SALT_LENGTH = 8;
/** 初始化向量长度(字节) */
private static final int IV_LENGTH = 16;
/** AES 密钥长度 */
private static final int KEY_LENGTH = 128;
/** PBKDF2 迭代次数 */
private static final int PBKDF2_ITERATIONS = 1000;
/** AES 加密算法 */
private static final String AES_CIPHER = "AES/CBC/PKCS5Padding";
/**
* AES 加密(带随机盐值和 IV,每次加密结果不同)
*
* @param plainText 明文
* @return Base64 密文(格式:盐值 + IV + 加密数据)
*/
public static String aesEncrypt(String plainText) {
if (plainText == null || plainText.isEmpty()) {
return plainText;
}
try {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
random.nextBytes(salt);
random.nextBytes(iv);
SecretKeySpec keySpec = deriveKey(salt);
Cipher cipher = Cipher.getInstance(AES_CIPHER);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv));
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
byte[] combined = new byte[SALT_LENGTH + IV_LENGTH + encrypted.length];
System.arraycopy(salt, 0, combined, 0, SALT_LENGTH);
System.arraycopy(iv, 0, combined, SALT_LENGTH, IV_LENGTH);
System.arraycopy(encrypted, 0, combined, SALT_LENGTH + IV_LENGTH, encrypted.length);
return Base64.getEncoder().encodeToString(combined);
} catch (Exception e) {
System.err.println("AES加密失败: " + e.getMessage());
e.printStackTrace();
return plainText;
}
}
/**
* AES 解密(解析盐值 + IV + 加密数据)
*
* @param cipherText Base64 密文
* @return 明文
*/
public static String aesDecrypt(String cipherText) {
if (cipherText == null || cipherText.isEmpty()) {
return cipherText;
}
try {
byte[] combined = Base64.getDecoder().decode(cipherText);
byte[] salt = new byte[SALT_LENGTH];
byte[] iv = new byte[IV_LENGTH];
byte[] encrypted = new byte[combined.length - SALT_LENGTH - IV_LENGTH];
System.arraycopy(combined, 0, salt, 0, SALT_LENGTH);
System.arraycopy(combined, SALT_LENGTH, iv, 0, IV_LENGTH);
System.arraycopy(combined, SALT_LENGTH + IV_LENGTH, encrypted, 0, encrypted.length);
SecretKeySpec keySpec = deriveKey(salt);
Cipher cipher = Cipher.getInstance(AES_CIPHER);
cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv));
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, StandardCharsets.UTF_8);
} catch (Exception e) {
System.err.println("AES解密失败: " + e.getMessage());
e.printStackTrace();
return cipherText;
}
}
/**
* 通过 PBKDF2 从盐值和内部密钥派生 AES 密钥
*/
private static SecretKeySpec deriveKey(byte[] salt) {
try {
SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
KeySpec spec = new PBEKeySpec(AES_PASSWORD.toCharArray(), salt, PBKDF2_ITERATIONS, KEY_LENGTH);
SecretKey tmp = factory.generateSecret(spec);
return new SecretKeySpec(tmp.getEncoded(), "AES");
} catch (Exception e) {
throw new RuntimeException("派生AES密钥失败", e);
}
}
public static void main(String[] args) throws Exception {
// --- RSA 公钥加密测试(安眼平台)---
// String publicKey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJL0JkqsUoK6kt3JyogsgqNp9VDGDp+t3ZAGMbVoMPdHNT2nfiIVh9ZMNHF7g2XiAa8O8AQWyh2PjMR0NiUSVQMCAwEAAQ==";
// String password = "Aa%13999527523";
// String rsaEncrypted = encryptByPublicKey(publicKey, password);
// System.out.println("RSA加密: " + rsaEncrypted);
//
// // --- AES 加解密测试(日志脱敏)---
// String originalText = "Aa%13999527523";
// String aesEncrypted = aesEncrypt(originalText);
String aesDecrypted = aesDecrypt("RuKy7jFDJR0rEJiwxgcyX6RFq8qP5FEgWZuMNxuVkKlbHhjJ2SzLOA==");
// System.out.println("AES加密: " + aesEncrypted);
System.out.println("AES解密: " + aesDecrypted);
// System.out.println("匹配: " + originalText.equals(aesDecrypted));
// 验证每次加密结果不同(盐值和IV随机)
// String aesEncrypted2 = aesEncrypt(originalText);
// System.out.println("再次加密: " + aesEncrypted2);
// System.out.println("两次密文不同: " + !aesEncrypted.equals(aesEncrypted2));
}
}
+61
View File
@@ -0,0 +1,61 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>health-bank</artifactId>
<groupId>com.renkang</groupId>
<version>2.0.0</version>
</parent>
<artifactId>health-bank-api</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 控制openfeign中bcpkix依赖的版本 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
</dependency>
<!-- sentinel限流熔断降级-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-base-core</artifactId>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,16 @@
package com.renkang.bank.constant;
/**
* @author feng
* @date 2026-02-05
*/
public interface HealthBankConstants {
/**
* 积分变化-增加积分
*/
Integer CHANGE_TYPE_ADD = 1;
/**
*积分变化-扣减积分
*/
Integer CHANGE_TYPE_DEDUCT = 2;
}
@@ -0,0 +1,44 @@
package com.renkang.bank.enums;
import lombok.Getter;
/**
* @author: feng
* @description: 积分来源枚举类
* @date 2026/02/05 09:38
*/
@Getter
public enum PointsSourceEnum {
// 积分变更类型
TYPE_1_SURVEY(1, "survey", "积分打卡"),
TYPE_2_USE(2, "use", "礼品兑换"),
TYPE_2_VIOLATION(2, "violation", "违规扣除"),
TYPE_2_EXPIRED(2, "expired", "过期清除");
/**
* 类型1-获得积分,2-扣减积分
*/
public final Integer changeType;
/**
* source 标识
*/
public final String source;
/**
* 来源描述
*/
public final String desc;
PointsSourceEnum(Integer changeType, String source, String desc) {
this.changeType = changeType;
this.source = source;
this.desc = desc;
}
public static PointsSourceEnum getBySource(String source) {
for (PointsSourceEnum value : PointsSourceEnum.values()) {
if (value.source.equals(source)) {
return value;
}
}
return null;
}
}
@@ -0,0 +1,25 @@
package com.renkang.bank.model.dto;
import com.renkang.bank.model.po.PointsRule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.base.PageInfo;
/**
* @author pj
* @version 1.0
* @description: 积分规则分页dto
* @date 2026/2/5 18:11
*/
@Data
public class PointsRulePageDTO extends PageInfo<PointsRule> {
@Schema(title = "健康分类")
private Integer ruleCategory;
@Schema(title = "健康行为")
private String ruleDesc;
@Schema(title = "状态1-启用,2停用")
private Integer status;
}
@@ -0,0 +1,40 @@
package com.renkang.bank.model.dto;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author pj
* @version 1.0
* @description: 更新用户积分DTO
* @date 2026/2/6 9:00
*/
@Data
public class UpdUserPointsDTO {
/**
* 积分标识(积分打卡:survey,礼品兑换:use,违规扣除:violation,过期清除:expired)
*/
private String sourceType;
/**
* 业务ID(积分任务id,兑换商品id等)
*/
private Long businessId;
/**
* 操作积分(扣减)
*/
private BigDecimal points;
/**
* 操作备注(扣减积分的说明等)
*/
private String remark;
/**
* 用户id(扣减的目标用户id)
*/
private String userId;
}
@@ -0,0 +1,42 @@
package com.renkang.bank.model.dto;
import com.renkang.bank.model.po.PointsRule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.base.PageInfo;
/**
* @author pj
* @version 1.0
* @description: 积分规则分页dto
* @date 2026/2/5 18:11
*/
@Data
public class UserPointsPageDTO extends PageInfo<PointsRule> {
@Schema(title = "姓名")
private String realName;
@Schema(title = "员工编号")
private String workNo;
@Schema(title = "所属单位")
private String orgCode;
@Schema(title = "所属部门")
private String deptCode;
@Schema(title = "1收入,2支出")
private Integer changeType;
@Schema(title = "健康行为")
private String ruleDesc;
@Schema(title = "打卡日期(开始)")
private String startDate;
@Schema(title = "打卡日期(结束)")
private String endDate;
private String userId;
}
@@ -0,0 +1,45 @@
package com.renkang.bank.model.dto;
import com.renkang.bank.model.po.PointsRule;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecg.base.PageInfo;
import java.util.Date;
import java.util.List;
/**
* @author pj
* @version 1.0
* @description: 积分规则分页dto
* @date 2026/2/5 18:11
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserPointsRankingDTO extends PageInfo<PointsRule> {
@Schema(title = "榜单类型,0日榜,1周榜,2月榜,3季榜,4年榜,5选时排名,10总榜")
private String rankType;
@Schema(title = "开始时间")
private Date startTime;
@Schema(title = "截止时间")
private Date endTime;
@Schema(title = "所属单位")
private String orgCode;
@Schema(title = "所属部门")
private String deptCode;
@Schema(title = "姓名")
private String realName;
@Schema(title = "员工编号")
private String workNo;
private List<String> userIds;
}
@@ -0,0 +1,74 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 礼品表 -礼品基本信息
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("bank_gift_info")
@Schema(title = "礼品基本信息", description = "礼品基本信息")
public class GiftInfo implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "礼品名称")
private String giftName;
@Schema(title = "礼品主图(多个使用逗号分隔)")
private String giftImage;
@Schema(title = "礼品库存")
private Integer inventory;
@Schema(title = "礼品分类(1-实物礼品,2-虚拟礼品)")
private Integer type;
@Schema(title = "礼品价格")
private BigDecimal price;
@Schema(title = "用户限购数量")
private Integer purchaseLimit;
@Schema(title = "礼品详情(富文本内容描述)")
private String giftInfo;
@Schema(title = "状态(0-启用,1-停用)")
private Integer status = 0;
@Schema(title = "删除标记")
@TableLogic
private Integer delFlag;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "修改时间")
private Date lastModifyDate;
}
@@ -0,0 +1,58 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
/**
* 礼品库存变更记录表 -礼品库存变更记录表
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("bank_gift_inventory_log")
@Schema(title = "礼品库存变更记录表", description = "礼品库存变更记录表")
public class GiftInventoryLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "礼品ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long giftId;
@Schema(title = "本次库存变化")
private Integer inventory;
@Schema(title = "1-加库存,2-减库存")
private Integer changeType;
@Schema(title = "本次变动前库存")
private Integer beforeInventory;
@Schema(title = "本次变动后库存")
private Integer afterInventory;
@Schema(title = "操作人ID")
private String operatorId;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
}
@@ -0,0 +1,56 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 礼品价格变更记录表 -礼品价格变更记录表
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("bank_gift_price_log")
@Schema(title = "礼品价格变更记录表", description = "礼品价格变更记录表")
public class GiftPriceLog implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "礼品ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long giftId;
@Schema(title = "本次设定的价格")
private BigDecimal price;
@Schema(title = "本次变动前价格")
private BigDecimal beforePrice;
@Schema(title = "本次变动后价格")
private BigDecimal afterPrice;
@Schema(title = "操作人ID")
private String operatorId;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
}
@@ -0,0 +1,57 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.github.yulichang.annotation.EntityMapping;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldNameConstants;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* 订单表 -积分兑换记录表
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@TableName("bank_order_form")
@Schema(title = "订单表(积分兑换记录表)", description = "订单表(积分兑换记录表)")
public class OrderForm implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "用户ID")
private String userId;
@Schema(title = "订单总额")
private BigDecimal orderPrice;
@Schema(title = "订单商品集合")
@TableField(exist = false)
@EntityMapping(thisField = OrderForm.Fields.id, joinField = OrderFormItem.Fields.orderId)
private List<OrderFormItem> orderFormItems;
@Schema(title = "删除标记")
@TableLogic
private Integer delFlag;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "下单时间")
private Date createTime;
}
@@ -0,0 +1,38 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldNameConstants;
import java.io.Serializable;
/**
* 订单子表 -订单明细表
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@FieldNameConstants
@TableName("bank_order_form_item")
@Schema(title = "订单明细表", description = "订单明细表")
public class OrderFormItem implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "订单ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long orderId;
}
@@ -0,0 +1,96 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.FieldNameConstants;
import org.jeecg.common.aspect.annotation.Dict;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* @Description: 积分规则表
* @Author: feng
* @Date: 2026-02-05
* @Version: V1.0
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@FieldNameConstants
@TableName("bank_points_rule")
@Schema(title = "积分规则表", description = "积分规则表")
public class PointsRule implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "ID")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Excel(name = "行为周期", width = 15, replace = {"次_1", "日_2", "周_3", "月_4", "季度_5", "年_6"}, orderNum = "3")
@Schema(title = "问答频次(1-单次,2-日/次,3-周/次,4-月/次,5-季度/次,6-年/次)")
private Integer frequency;
@Excel(name = "健康分类", width = 15, dicCode = "points_rule_category", orderNum = "1")
@Dict(dicCode = "points_rule_category")
@Schema(title = "规则类别")
private Integer ruleCategory;
@Excel(name = "健康行为", width = 15, orderNum = "2")
@Schema(title = "规则描述")
private String ruleDesc;
@Schema(title = "图标")
private String icon;
@Excel(name = "奖励积分", width = 15, orderNum = "4")
@Schema(title = "奖励积分")
private BigDecimal points;
@Schema(title = "状态1-启用,2停用")
private Integer status = 1;
@Schema(title = "删除标识0-正常,1已删除")
@TableLogic
private Integer delFlag;
@Schema(title = "创建人ID")
private String createUser;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "创建时间")
private Date createTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "最后修改时间")
private Date lastModifyDate;
@Schema(title = "周期内打卡次数上限")
private Integer checkInUpper;
@Schema(title = "年额外积分上限")
private BigDecimal yearExtraPointUpper;
@Schema(title = "是否支持打卡叠加得分,0不支持,1支持")
private Integer supportCum;
@Schema(title = "是否需要填写补充信息,0不需要,1需要")
private Integer needRemark;
}
@@ -0,0 +1,102 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 用户积分汇总表 -产生过积分的用户每人一条数据
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("bank_user_points")
@Schema(title = "用户积分汇总表", description = "用户积分汇总表")
public class UserPoints implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "用户ID")
private String userId;
@Schema(title = "用户部门")
private String orgCode;
@Excel(name = "当前可用积分", width = 15, orderNum = "7")
@Schema(title = "当前可用积分")
private BigDecimal points;
@Excel(name = "累计获得积分", width = 15, orderNum = "8")
@Schema(title = "历史获得的积分总和")
private BigDecimal addPointsSum;
/**
* 记录所有扣除积分的总和,包含兑换消耗、手动扣除的积分
*/
@Schema(title = "历史扣除积分总和")
private BigDecimal deductPointsSum;
/**
* 积分兑换记录总和,只记录积分兑换
*/
@Excel(name = "累计兑换积分", width = 15, orderNum = "9")
@Schema(title = "历史兑换积分总和")
private BigDecimal exchangePointsSum;
@Schema(title = "状态(1-正常,2-冻结)")
private Integer status = 1;
/**
* 注意:该字段记录最后一次获得积分当天收获的积分总和,该归档字段的更新会减少后期对积分明细表的查询次数
*/
@Schema(title = "最新一天获取积分的总和")
private BigDecimal latestAddDaySum;
/**
* 注意:该字段记录最后一次消费积分当天收获的积分总和,该归档字段的更新会减少后期对积分明细表的查询次数
*/
@Schema(title = "最新一天消耗积分的总和")
private BigDecimal latestDeductDaySum;
@Excel(name = "最近一次打卡时间", width = 25, format = "yyyy-MM-dd HH:mm:ss", orderNum = "10")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "最新获得积分的日期")
private Date lastAddDate;
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
@Schema(title = "最新消费积分的日期")
private Date lastDeductDate;
@Schema(title = "删除标记")
@TableLogic
private Integer delFlag;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "首次获得积分时间")
private Date createTime;
@JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Schema(title = "积分最后变更时间")
private Date lastVaryDate;
}
@@ -0,0 +1,80 @@
package com.renkang.bank.model.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableLogic;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.renkang.bank.enums.PointsSourceEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* 用户积分详情表 -记录每次的积分变更
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("bank_user_points_details")
@Schema(title = "用户积分详情表", description = "用户积分详情表")
public class UserPointsDetails implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.ASSIGN_ID)
@Schema(title = "id")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
@Schema(title = "用户ID")
private String userId;
@Schema(title = "本次积分变化")
private BigDecimal points;
@Schema(title = "本次变动前积分")
private BigDecimal beforePoints;
@Schema(title = "本次变动后积分")
private BigDecimal afterPoints;
@Schema(title = "1-获得积分,2-扣减积分")
private Integer changeType;
@Schema(title = "补充信息")
private String remark;
/**
* @see PointsSourceEnum 来源详见枚举类
*/
@Schema(title = "来源(survey-积分打卡,use-礼品兑换,violation-违规扣除,expired-过期清除)")
private String source;
@Schema(title = "关联业务ID(规则ID,礼品兑换订单Id等)")
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long businessId;
@Schema(title = "关联业务详情(可选择性的冗余业务详情:待定)")
private String businessInfo;
@Schema(title = "操作人ID")
private String operatorId;
@Schema(title = "删除标记")
@TableLogic
private Integer delFlag;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
}
@@ -0,0 +1,58 @@
package com.renkang.bank.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecgframework.poi.excel.annotation.Excel;
import java.math.BigDecimal;
/**
* @author pj
* @version 1.0
* @description: 用户积分榜单VO
* @date 2026/2/6 10:41
*/
@Data
public class UserPointRankingVO {
@Excel(name = "排名", width = 15, orderNum = "8")
@Schema(title = "排名")
private Integer ranking;
@Schema(title = "用户id")
private String userId;
@Excel(name = "姓名", width = 15, orderNum = "3")
@Schema(title = "姓名")
private String realName;
@Excel(name = "员工编号", width = 15, orderNum = "4")
@Schema(title = "员工编号")
private String workNo;
@Excel(name = "年龄", width = 15, orderNum = "6")
@Schema(title = "年龄")
private Integer age;
@Schema(title = "头像")
private String avatar;
@Excel(name = "所属单位", width = 15, orderNum = "1")
@Schema(title = "单位名称")
private String orgName;
@Excel(name = "所属部门", width = 15, orderNum = "2")
@Schema(title = "部门名称")
private String deptName;
@Schema(title = "性别")
private Integer sex;
@Excel(name = "性别", width = 15, orderNum = "5")
@Schema(title = "性别")
private String sexName;
@Excel(name = "日累计积分", width = 15, orderNum = "7")
@Schema(title = "积分")
private BigDecimal pointSum;
}
@@ -0,0 +1,44 @@
package com.renkang.bank.model.vo;
import cn.hutool.core.date.DateUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author pj
* @version 1.0
* @description: 用户积分明细VO
* @date 2026/2/6 10:41
*/
@Data
public class UserPointRecordVO {
@Schema(title = "记录id")
private Long id;
@Schema(title = "得分说明")
private String businessInfo;
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
@Schema(title = "本次积分变化")
private BigDecimal points;
@Schema(title = "补充信息")
private String remark;
public String getCreateMonthStr() {
if (createTime != null) {
return DateUtil.format(createTime, "yyyy年-MM月");
}
return null;
}
}
@@ -0,0 +1,48 @@
package com.renkang.bank.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* 用户积分详情VO
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(title = "用户积分详情VO", description = "用户积分详情VO")
public class UserPointsDeductDetailsVO {
@Schema(title = "id")
private Long id;
@Schema(title = "用户ID")
private String userId;
@Excel(name = "积分变化", width = 15, orderNum = "2")
@Schema(title = "本次积分变化")
private BigDecimal points;
@Excel(name = "支出信息", width = 25, orderNum = "1")
@Schema(title = "补充信息")
private String remark;
@Schema(title = "关联业务ID(规则ID,礼品兑换订单Id等)")
private Long businessId;
@Excel(name = "支出时间", width = 25, format = "yyyy-MM-dd HH:mm:ss", orderNum = "3")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
}
@@ -0,0 +1,56 @@
package com.renkang.bank.model.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jeecgframework.poi.excel.annotation.Excel;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.util.Date;
/**
* 用户积分详情VO
*
* @author feng
* @date 2026-02-05
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Schema(title = "用户积分详情VO", description = "用户积分详情VO")
public class UserPointsDetailsVO {
@Schema(title = "id")
private Long id;
@Schema(title = "用户ID")
private String userId;
@Excel(name = "积分变化", width = 15, orderNum = "4")
@Schema(title = "本次积分变化")
private BigDecimal points;
@Excel(name = "补充信息", width = 15, orderNum = "2")
@Schema(title = "补充信息")
private String remark;
@Schema(title = "关联业务ID(规则ID,礼品兑换订单Id等)")
private Long businessId;
@Excel(name = "健康行为", width = 25, orderNum = "1")
@Schema(title = "关联业务详情(可选择性的冗余业务详情:待定)")
private String businessInfo;
@Excel(name = "打卡时间", width = 25, format = "yyyy-MM-dd HH:mm:ss", orderNum = "5")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
@Schema(title = "创建时间")
private Date createTime;
@Excel(name = "行为周期", width = 15, replace = {"次_1", "日_2", "周_3", "月_4", "季度_5", "年_6"}, orderNum = "3")
@Schema(title = "问答频次(1-单次,2-日/次,3-周/次,4-月/次,5-季度/次,6-年/次)")
private Integer frequency;
}
@@ -0,0 +1,42 @@
package com.renkang.bank.model.vo;
import com.fasterxml.jackson.annotation.JsonSetter;
import com.fasterxml.jackson.annotation.Nulls;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
/**
* @author pj
* @version 1.0
* @description: 用户积分信息vo
* @date 2026/2/6 9:31
*/
@Data
public class UserPointsInfoVO {
@Schema(title = "可用积分")
@JsonSetter(nulls = Nulls.SKIP)
private BigDecimal points = BigDecimal.ZERO;
@Schema(title = "累计积分")
@JsonSetter(nulls = Nulls.SKIP)
private BigDecimal addPointsSum = BigDecimal.ZERO;
@Schema(title = "累计兑换")
@JsonSetter(nulls = Nulls.SKIP)
private BigDecimal exchangePointsSum = BigDecimal.ZERO;
public BigDecimal getPoints() {
return points == null ? BigDecimal.ZERO : points;
}
public BigDecimal getAddPointsSum() {
return addPointsSum == null ? BigDecimal.ZERO : addPointsSum;
}
public BigDecimal getExchangePointsSum() {
return exchangePointsSum == null ? BigDecimal.ZERO : exchangePointsSum;
}
}
@@ -0,0 +1,42 @@
package com.renkang.bank.model.vo;
import com.renkang.bank.model.po.UserPoints;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.jeecgframework.poi.excel.annotation.Excel;
/**
* @author pj
* @version 1.0
* @description: 用户积分信息列表VO
* @date 2026/2/5 18:51
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class UserPointsListVO extends UserPoints {
@Excel(name = "姓名", width = 15, orderNum = "3")
@Schema(title = "姓名")
private String realName;
@Excel(name = "员工编号", width = 15, orderNum = "4")
@Schema(title = "员工编号")
private String workNo;
@Excel(name = "所属单位", width = 15, orderNum = "1")
@Schema(title = "所属单位名称")
private String orgName;
@Excel(name = "所属部门", width = 15, orderNum = "2")
@Schema(title = "所属部门名称")
private String deptName;
@Excel(name = "性别", width = 15, orderNum = "5")
@Schema(title = "性别")
private String sexName;
@Excel(name = "年龄", width = 15, orderNum = "6")
@Schema(title = "年龄")
private Integer age;
}
@@ -0,0 +1,53 @@
package com.renkang.bank.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
/**
* @author pj
* @version 1.0
* @description: 员工打卡监测VO
* @date 2026/2/5 18:51
*/
@Data
public class UserPointsMonitorVO {
@Schema(title = "用户id")
private String userId;
@Schema(title = "姓名")
private String realName;
@Schema(title = "员工编号")
private String workNo;
@Schema(title = "所属单位名称")
private String orgName;
@Schema(title = "所属部门名称")
private String deptName;
@Schema(title = "性别")
private String sexName;
@Schema(title = "年龄")
private Integer age;
@Schema(title = "健康行为")
private String businessInfo;
@Schema(title = "补充信息")
private String remark;
@Schema(title = "周期")
private Integer frequency;
@Schema(title = "获取积分")
private BigDecimal points;
@Schema(title = "打卡时间")
private Date createTime;
}
@@ -0,0 +1,39 @@
package com.renkang.bank.model.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.jeecg.common.aspect.annotation.Dict;
import java.math.BigDecimal;
/**
* @author pj
* @version 1.0
* @description: 用户积分任务VO
* @date 2026/2/6 9:54
*/
@Data
public class UserPointsTaskVO {
@Schema(title = "规则id")
private Long ruleId;
@Schema(title = "问答频次(1-单次,2-日/次,3-周/次,4-月/次,5-季度/次,6-年/次)")
private Integer frequency;
@Dict(dicCode = "points_rule_category")
@Schema(title = "规则类别")
private Integer ruleCategory;
@Schema(title = "规则描述")
private String ruleDesc;
@Schema(title = "图标")
private String icon;
@Schema(title = "奖励积分")
private BigDecimal points;
@Schema(title = "打卡状态,0未打卡,1已打卡")
private Integer clockInStatus;
}
+65
View File
@@ -0,0 +1,65 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>health-bank</artifactId>
<groupId>com.renkang</groupId>
<version>2.0.0</version>
</parent>
<artifactId>health-bank-biz</artifactId>
<dependencies>
<dependency>
<groupId>com.renkang</groupId>
<artifactId>health-bank-api</artifactId>
</dependency>
<!-- 引入分布式锁依赖 -->
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter-lock</artifactId>
</dependency>
<!-- 中文拼音转换依赖 -->
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.1</version>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-boot-starter-job</artifactId>
</dependency>
<dependency>
<groupId>com.renkang</groupId>
<artifactId>renkang-starter-aspose-word</artifactId>
</dependency>
<dependency>
<groupId>com.renkang</groupId>
<artifactId>font-resources</artifactId>
</dependency>
<dependency>
<groupId>org.jeecgframework.boot</groupId>
<artifactId>jeecg-system-cloud-api</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,97 @@
package com.renkang.bank.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.renkang.bank.model.dto.PointsRulePageDTO;
import com.renkang.bank.model.po.PointsRule;
import com.renkang.bank.service.IPointsRuleService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.commons.lang.StringUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.system.base.controller.JeecgController;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
/**
* @author pj
* @version 1.0
* @description: 积分规则
* @date 2026/2/5 17:53
*/
@Tag(name = "积分规则")
@RestController
@RequestMapping("/bank/pointsRule")
public class PointsRuleController extends JeecgController<PointsRule, IPointsRuleService> {
@Autowired
private IPointsRuleService iPointsRuleService;
@Operation(summary = "积分规则-分页列表", description = "积分规则-分页列表")
// @RequiresPermissions("bank:points_rule:add")
@PostMapping(value = "/pageList")
public Result<IPage<PointsRule>> pageList(@RequestBody PointsRulePageDTO dto) {
return Result.OK(iPointsRuleService.pageList(dto));
}
@Operation(summary = "积分规则-添加", description = "积分规则-添加")
// @RequiresPermissions("bank:points_rule:add")
@PostMapping(value = "/add")
public Result<Integer> add(@RequestBody PointsRule pointsRule) {
return Result.OK("添加成功", iPointsRuleService.add(pointsRule));
}
@Operation(summary = "积分规则-修改", description = "积分规则-修改")
// @RequiresPermissions("bank:points_rule:edit")
@PostMapping(value = "/edit")
public Result<Integer> edit(@RequestBody PointsRule pointsRule) {
return Result.OK("修改成功", iPointsRuleService.edit(pointsRule));
}
@Operation(summary = "积分规则-修改状态", description = "积分规则-修改状态")
// @RequiresPermissions("bank:points_rule:edit")
@GetMapping(value = "/editStatus")
public Result<Integer> editStatus(@RequestParam(name = "id") Long id, @RequestParam(name = "status") Integer status) {
return Result.OK("修改成功", iPointsRuleService.editStatus(id, status));
}
@Operation(summary = "积分规则-详情", description = "积分规则-详情")
// @RequiresPermissions("bank:points_rule:byId")
@GetMapping(value = "/byId")
public Result<PointsRule> byId(@RequestParam(name = "id") String id) {
return Result.OK(iPointsRuleService.byId(id));
}
@Operation(summary = "积分规则-删除", description = "积分规则-删除")
// @RequiresPermissions("bank:points_rule:del")
@GetMapping(value = "/delete")
public Result<Integer> delete(@RequestParam(name = "id") String id) {
return Result.OK("删除成功", iPointsRuleService.delete(id));
}
@Operation(summary = "积分规则-批量删除", description = "积分规则-批量删除")
// @RequiresPermissions("bank:points_rule:del")
@GetMapping(value = "/deleteBatch")
public Result<Integer> deleteBatch(@RequestParam(name = "ids") String ids) {
if (StringUtils.isBlank(ids)) {
return Result.error("请选择需要删除的积分规则");
}
return Result.OK("删除成功", iPointsRuleService.deleteBatch(ids));
}
/**
* 导出excel
*
* @param request
* @param conHealthInfo
*/
@Operation(summary = "积分规则-导出", description = "积分规则-导出")
// @RequiresPermissions("bank:points_rule:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, PointsRule conHealthInfo) {
return super.exportXls(request, conHealthInfo, PointsRule.class, "积分规则");
}
}
@@ -0,0 +1,124 @@
package com.renkang.bank.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.renkang.bank.model.dto.UpdUserPointsDTO;
import com.renkang.bank.model.dto.UserPointsPageDTO;
import com.renkang.bank.model.dto.UserPointsRankingDTO;
import com.renkang.bank.model.vo.*;
import com.renkang.bank.service.IUserPointsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.jeecg.common.api.vo.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* @author pj
* @version 1.0
* @description: 用户积分汇总表
* @date 2026/2/5 16:42
*/
@Tag(name = "用户积分信息")
@RestController
@RequestMapping("/bank/userPoints")
public class UserPointsController {
@Autowired
private IUserPointsService iUserPointsService;
@Operation(summary = "测试积分变更", description = "测试积分变更")
@PostMapping(value = "/testUpdateUserPoints")
public Result<Boolean> testUpdateUserPoints(@RequestBody UpdUserPointsDTO dto) {
return Result.OK(iUserPointsService.testUpdateUserPoints(dto));
}
@Operation(summary = "用户积分信息-分页列表", description = "用户积分信息-分页列表")
@PostMapping(value = "/pageList")
public Result<IPage<UserPointsListVO>> pageList(@RequestBody UserPointsPageDTO dto) {
return Result.OK(iUserPointsService.pageList(dto));
}
@Operation(summary = "用户积分信息-分页列表(导出)", description = "用户积分信息-分页列表(导出)")
@PostMapping(value = "/exportUserPoints")
public Result<String> exportUserPoints(@RequestBody UserPointsPageDTO dto) {
iUserPointsService.exportUserPoints(dto);
return Result.OK("导出成功,请在导出记录查看");
}
@Operation(summary = "用户积分信息-积分明细", description = "用户积分信息-积分明细")
@PostMapping(value = "/detailPageList")
public Result<IPage<UserPointsDetailsVO>> detailPageList(@RequestBody UserPointsPageDTO dto) {
return Result.OK(iUserPointsService.detailPageList(dto));
}
@Operation(summary = "用户积分信息-积分明细(导出)", description = "用户积分信息-积分明细(导出)")
@PostMapping(value = "/exportPointsDetail")
public Result<String> exportPointsDetail(@RequestBody UserPointsPageDTO dto) {
iUserPointsService.exportPointsDetail(dto);
return Result.OK("导出成功,请在导出记录查看");
}
@Operation(summary = "用户积分信息-扣除积分", description = "用户积分信息-扣除积分")
@PostMapping(value = "/deductPoint")
public Result<Boolean> deductPoint(@RequestBody UpdUserPointsDTO dto) {
return Result.OK(iUserPointsService.deductPoint(dto));
}
@Operation(summary = "用户积分信息-员工打卡监测", description = "用户积分信息-员工打卡监测")
@PostMapping(value = "/clockInMonitor")
public Result<IPage<UserPointsMonitorVO>> clockInMonitor(@RequestBody UserPointsPageDTO dto) {
return Result.OK(iUserPointsService.clockInMonitor(dto));
}
@Operation(summary = "用户积分信息-员工打卡监测(导出)", description = "用户积分信息-员工打卡监测(导出)")
@PostMapping(value = "/exportClockInMonitor")
public Result<String> exportClockInMonitor(@RequestBody UserPointsPageDTO dto) {
iUserPointsService.exportClockInMonitor(dto);
return Result.OK("导出成功,请在导出记录查看");
}
@Operation(summary = "用户积分信息-员工积分排名", description = "用户积分信息-员工积分排名")
@PostMapping(value = "/userRanking")
public Result<IPage<UserPointRankingVO>> userRanking(@RequestBody UserPointsRankingDTO dto) {
return Result.OK(iUserPointsService.userRanking(dto));
}
@Operation(summary = "用户积分信息-员工积分排名(导出)", description = "用户积分信息-员工积分排名(导出)")
@PostMapping(value = "/exportUserRanking")
public Result<String> exportUserRanking(@RequestBody UserPointsRankingDTO dto) {
iUserPointsService.exportUserRanking(dto);
return Result.OK("导出成功,请在导出记录查看");
}
@Operation(summary = "用户积分信息-app我的积分", description = "用户积分信息-app我的积分")
@GetMapping(value = "/app/myPoints")
public Result<UserPointsInfoVO> myPoints() {
return Result.OK(iUserPointsService.myPoints());
}
@Operation(summary = "用户积分信息-app积分任务", description = "用户积分信息-app积分任务")
@GetMapping(value = "/app/pointsTaskList")
public Result<List<UserPointsTaskVO>> pointsTaskList() {
return Result.OK(iUserPointsService.pointsTaskList());
}
@Operation(summary = "用户积分信息-app积分打卡", description = "用户积分信息-app积分打卡")
@PostMapping(value = "/app/pointCheckIn")
public Result<Boolean> pointCheckIn(@RequestBody UpdUserPointsDTO dto) {
return Result.OK(iUserPointsService.pointCheckIn(dto));
}
@Operation(summary = "用户积分信息-app积分明细", description = "用户积分信息-app积分明细")
@PostMapping(value = "/app/pointRecord")
public Result<IPage<UserPointRecordVO>> pointRecord(@RequestBody UserPointsPageDTO dto) {
return Result.OK(iUserPointsService.pointRecord(dto));
}
@Operation(summary = "用户积分信息-app积分榜单", description = "用户积分信息-app积分榜单")
@PostMapping(value = "/app/pointRanking")
public Result<IPage<UserPointRankingVO>> pointRanking(@RequestBody UserPointsRankingDTO dto) {
return Result.OK(iUserPointsService.pointRanking(dto));
}
}
@@ -0,0 +1,16 @@
package com.renkang.bank.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.renkang.bank.model.po.PointsRule;
import com.renkang.bank.model.vo.UserPointsTaskVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface PointsRuleMapper extends MPJBaseMapper<PointsRule> {
List<UserPointsTaskVO> pointsTaskList(@Param("userId") String userId);
}
@@ -0,0 +1,19 @@
package com.renkang.bank.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.github.yulichang.base.MPJBaseMapper;
import com.renkang.bank.model.dto.UserPointsRankingDTO;
import com.renkang.bank.model.po.UserPointsDetails;
import com.renkang.bank.model.vo.UserPointRankingVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Date;
@Mapper
public interface UserPointsDetailsMapper extends MPJBaseMapper<UserPointsDetails> {
IPage<UserPointRankingVO> userRanking(IPage<UserPointRankingVO> page, Date startTime, Date endTime, UserPointsRankingDTO dto);
IPage<UserPointRankingVO> pointRanking(IPage<UserPointRankingVO> page, Date startTime, Date endTime, String secondOrgCode);
}
@@ -0,0 +1,14 @@
package com.renkang.bank.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.renkang.bank.model.po.UserPoints;
import org.apache.ibatis.annotations.Mapper;
/**
* @author: pj
* @description: 用户积分
* @date 2026/2/5 16:47
*/
@Mapper
public interface UserPointsMapper extends MPJBaseMapper<UserPoints> {
}
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renkang.bank.mapper.PointsRuleMapper">
<select id="pointsTaskList" resultType="com.renkang.bank.model.vo.UserPointsTaskVO">
SELECT
a.id as ruleId,
a.frequency,
a.rule_category,
a.rule_desc,
a.icon,
a.points,
CASE
WHEN a.frequency = 1 THEN
CASE WHEN COUNT(b.id) > 0 THEN 1 ELSE 0 END
WHEN a.frequency = 2 THEN
CASE WHEN COUNT(CASE WHEN TRUNC(b.create_time) = TRUNC(SYSDATE) THEN 1 END) > 0 THEN 1 ELSE 0 END
WHEN a.frequency = 3 THEN
CASE WHEN COUNT(CASE WHEN TO_CHAR(b.create_time, 'IYYY-IW') = TO_CHAR(SYSDATE, 'IYYY-IW') THEN 1 END) > 0 THEN 1 ELSE 0 END
WHEN a.frequency = 4 THEN
CASE WHEN COUNT(CASE WHEN TO_CHAR(b.create_time, 'YYYY-MM') = TO_CHAR(SYSDATE, 'YYYY-MM') THEN 1 END) > 0 THEN 1 ELSE 0 END
WHEN a.frequency = 5 THEN
CASE WHEN COUNT(CASE WHEN TO_CHAR(b.create_time, 'Q') = TO_CHAR(SYSDATE, 'Q') AND EXTRACT(YEAR FROM b.create_time) = EXTRACT(YEAR FROM SYSDATE) THEN 1 END) > 0 THEN 1 ELSE 0 END
WHEN a.frequency = 6 THEN
CASE WHEN COUNT(CASE WHEN EXTRACT(YEAR FROM b.create_time) = EXTRACT(YEAR FROM SYSDATE) THEN 1 END) > 0 THEN 1 ELSE 0 END
ELSE 0
END AS clockInStatus
FROM
bank_points_rule a
LEFT JOIN bank_user_points_details b ON b.user_id = #{userId}
AND b.change_type = 1
AND b.business_id = a.id
GROUP BY
a.id,a.frequency
order by a.frequency
</select>
</mapper>
@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.renkang.bank.mapper.UserPointsDetailsMapper">
<select id="userRanking" resultType="com.renkang.bank.model.vo.UserPointRankingVO">
<!--定义公共表表达式用于计算每个用户的总积分-->
WITH UserPointsSummary AS (
SELECT
a.user_id,
SUM(CASE WHEN a.change_type = 1 THEN a.points ELSE 0 END) -
SUM(CASE WHEN a.change_type = 2 THEN a.points ELSE 0 END) AS pointSum
FROM
bank_user_points_details a
INNER JOIN bank_user_points b ON a.user_id = b.user_id
<if test="null != dto.orgCode and '' != dto.orgCode">
AND b.org_code like concat(#{dto.orgCode},'%')
</if>
<if test="null != dto.deptCode and '' != dto.deptCode">
AND b.org_code like concat(#{dto.deptCode},'%')
</if>
GROUP BY
a.user_id
),
<!--计算所有用户的排名-->
RankedUsers AS (
SELECT
user_id,
pointSum,
ROW_NUMBER() OVER (ORDER BY pointSum DESC, user_id ASC) AS ranking
FROM
UserPointsSummary
)
<!--主查询:根据是否传入用户 ID 返回结果-->
SELECT
ranking,
user_id,
pointSum
FROM
RankedUsers
<where>
<if test="dto.userIds != null and dto.userIds.size() > 0">
AND user_id in
<foreach collection="dto.userIds" item="userId" open="(" separator="," close=")">
#{userId}
</foreach>
</if>
</where>
ORDER BY
pointSum DESC, user_id ASC
</select>
<select id="pointRanking" resultType="com.renkang.bank.model.vo.UserPointRankingVO">
WITH UserPointsSummary AS (
SELECT
a.user_id,
SUM( CASE WHEN a.change_type = 1 THEN a.points ELSE 0 END ) - SUM( CASE WHEN a.change_type = 2 THEN a.points
ELSE 0 END ) AS pointSum
FROM
bank_user_points_details a
INNER JOIN bank_user_points b ON a.user_id = b.user_id
<if test="null != secondOrgCode and '' != secondOrgCode">
AND b.org_code like concat(#{secondOrgCode},'%')
</if>
<where>
<if test="startTime != null and endTime != null">
AND a.create_time BETWEEN #{startTime} AND #{endTime}
</if>
</where>
GROUP BY
a.user_id
) SELECT
ROW_NUMBER() OVER ( ORDER BY pointSum DESC ) AS ranking,
user_id,
pointSum
FROM
UserPointsSummary
ORDER BY
pointSum DESC
</select>
</mapper>
@@ -0,0 +1,90 @@
package com.renkang.bank.redis.config;
import com.renkang.bank.redis.listener.BankAsyncListener;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.vo.SysUserModel;
import org.jeecg.enums.ServerEnum;
import org.jeecg.redis.RedisStreamKeyEnum;
import org.jeecg.util.IPUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.ObjectRecord;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.hash.Jackson2HashMapper;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
/**
* @author Shunzhi Jiang
* @since 2023/6/13
*/
@Configuration
@Slf4j
public class BankRedisStreamConfig {
private final Consumer consumer = Consumer.from(ServerEnum.BANK.getCustomGroup(), IPUtil.getCurrentServiceIp());
private final ReadOffset readOffset = ReadOffset.lastConsumed();
private BankAsyncListener bankAsyncListener;
@Autowired
public void setDepartAsyncListener(BankAsyncListener bankAsyncListener) {
this.bankAsyncListener = bankAsyncListener;
}
@Bean
public StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, SysUserModel>> sysUserModelInterveneStreamOptions(
@Qualifier("asyncTaskExecutor") TaskExecutor taskExecutor,
StringRedisSerializer stringSerializer,
GenericJackson2JsonRedisSerializer jsonSerializer
) {
return StreamMessageListenerContainer.StreamMessageListenerContainerOptions
.builder()
// 一次最多获取多少条消息
.batchSize(10)
// 运行 Stream 的 poll task
.executor(taskExecutor)
// 可以理解为 Stream Key 的序列化方式
.keySerializer(stringSerializer)
// 可以理解为 Stream 后方的字段的 key 的序列化方式
.hashKeySerializer(stringSerializer)
// 可以理解为 Stream 后方的字段的 value 的序列化方式
.hashValueSerializer(jsonSerializer)
// ObjectRecord 时,将 对象的 filed 和 value 转换成一个 Map 比如:将Book对象转换成map
.objectMapper(new Jackson2HashMapper(true))
// 将发送到Stream中的Record转换成ObjectRecord,转换成具体的类型是这个地方指定的类型
.targetType(SysUserModel.class)
.build();
}
@Bean(initMethod = "start", destroyMethod = "stop")
public StreamMessageListenerContainer<String, ObjectRecord<String, SysUserModel>> sysUserModelInterveneStreamMessageListenerContainer(
RedisConnectionFactory redisConnectionFactory,
StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, SysUserModel>> options
) {
StreamMessageListenerContainer<String, ObjectRecord<String, SysUserModel>> container =
StreamMessageListenerContainer.create(redisConnectionFactory, options);
// 修改用户
container.receive(
consumer,
createStreamOffset(RedisStreamKeyEnum.CHANGE_USER_DEPART),
bankAsyncListener
);
log.info("redisStream配置完成");
return container;
}
private StreamOffset<String> createStreamOffset(RedisStreamKeyEnum streamKeyEnum) {
return StreamOffset.create(streamKeyEnum.getKey(), readOffset);
}
}
@@ -0,0 +1,63 @@
package com.renkang.bank.redis.listener;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.renkang.bank.mapper.UserPointsMapper;
import com.renkang.bank.model.po.UserPoints;
import lombok.extern.slf4j.Slf4j;
import org.jeecg.common.system.vo.SysUserModel;
import org.jeecg.enums.ServerEnum;
import org.jeecg.redis.RedisStreamKeyEnum;
import org.jeecg.redis.RedisStreamUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.stream.ObjectRecord;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.stereotype.Component;
/**
* @author Shunzhi Jiang
* @since 2023/6/13
*/
@Component
@Slf4j
public class BankAsyncListener implements StreamListener<String, ObjectRecord<String, SysUserModel>> {
@Autowired
private RedisStreamUtil redisStreamUtil;
@Autowired
private UserPointsMapper userPointsMapper;
@Override
public void onMessage(ObjectRecord<String, SysUserModel> message) {
String streamKey = message.getStream();
SysUserModel model = message.getValue();
log.info("收到Redis Stream Key{}", streamKey);
RecordId recordId = message.getId();
log.info("ID{}, DATA: {}", recordId, JSON.toJSONString(model));
try {
if (RedisStreamKeyEnum.CHANGE_USER_DEPART.getKey().equals(streamKey)) {
if (StrUtil.isBlank(model.getId()) || StrUtil.isBlank(model.getOrgCode())) {
return;
}
// 修改用户积分主表单位信息
updateUserPointsInfo(model);
}
ack(message);
log.info("Redis Stream消费成功:Key: {}, ID: {}", streamKey, recordId);
} catch (Exception e) {
log.error("Redis Stream消费异常:Key: {}, ID: {}", streamKey, recordId, e);
}
}
private void updateUserPointsInfo(SysUserModel model) {
userPointsMapper.update(new LambdaUpdateWrapper<UserPoints>()
.eq(UserPoints::getUserId, model.getId())
.set(UserPoints::getOrgCode, model.getOrgCode())
);
}
private void ack(ObjectRecord<String, SysUserModel> message) {
redisStreamUtil.ack(ServerEnum.BANK.getCustomGroup(), message);
}
}
@@ -0,0 +1,23 @@
package com.renkang.bank.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.renkang.bank.model.dto.PointsRulePageDTO;
import com.renkang.bank.model.po.PointsRule;
public interface IPointsRuleService extends IService<PointsRule> {
IPage<PointsRule> pageList(PointsRulePageDTO dto);
int add(PointsRule pointsRule);
int edit(PointsRule pointsRule);
PointsRule byId(String id);
int delete(String id);
int editStatus(Long id, Integer status);
int deleteBatch(String ids);
}

Some files were not shown because too many files have changed in this diff Show More