勤思实习日志(一)

勤思实习日志(一)

==========
  来到勤思实习也接近一个月了,虽然每天都有记录,但毕竟是第一次接触真正的项目开发,新手上路犯了很多蠢,本来是不想发出来的。转念一想虽然错误很低级,但菜鸟不都是这样嘛!还是发出来作为自己的成长记录。

2023-07-17~2023-07-23 第一周

==========

--刚入职,首先是下载开发相关的软件。我做的是Java后端开发,使用的软件除了最基本的IDEA外,还下载了Navicat(数据库可视化工具),SourceTreeSetup(Git客户端管理工具,控制版本),Postman(接口测试工具)。
  
--一开始看到那么多没见过的工具特别心慌,幸好带我的老师很亲切(后称之为枚姐),手把手教我如何测试接口,如何使用Git仓库把代码拉到本地,如何上传……最开始的两天基本是熟悉代码,以及研究新的开发工具。我在学校使用的比较多的是springMVC+Mybatis,但马上要参与的项目使用的是springBoot+BeetlSQL等。所以最开始的几天基本是一边熟悉代码风格,一边恶补基础知识和啃官方使用手册。
  
--大概到了第三天,我觉得我光看文档和代码有点迷茫了,感觉好像看懂了,但看到代码还是有点慌,不知道该从何下手,于是一个冲动就跑去找枚姐,问她能不能给我一个小需求,我一边做一边学习使用新工具。其实现在想想还是有点太急了,还没学会走就想跑了,但当时我心里没底,对自己的水平不太有把握,而且光是看文档也不确定会不会用,坐在工位上听着周围人忙总感觉很心虚。但冲动都冲动了,枚姐就给了我一个小需求做。

07-21 排班信息新增字段

==========

--总之,我接到的第一个需求是:省二医务系统的【排班信息】上新增一个字段,“是否有住院总”,如果选择是,则需要填写一个或多个人员(联想搜索),如果选择否,则无需填写。

--和产品沟通后,大致需要展示的信息如下:原本排班信息新增的时候只需要填写科室、时间、备注并上传排班文件等,现在还需要选择该次排班是否有住院总医师,如果有就需要选择医师。很基础的增删改查,原本排班信息的模块已经很完善了,我只需要仿照已有的结构新增两条字段,①有没有住院总②住院总的医师id。

科室时间住院总备注创建人排班文件
内科07.14-07.21111admin01.xlsx
内分泌科07.10-07.18张三222张三02.xlsx
妇产科07.24-07.31张三、李四333李四03.xlsx

--因为住院总医师可以有多个,即排班信息和住院总是一对多的关系,所以需要建一张关联表:住院总信息表,包括排班信息id和住院总医师id。原本的排班信息表加一个字段:是否有住院总(其实不加也行,可以由前端设置判断,如果没有住院总则住院总的信息直接不传数据就行,也不影响查)。

--增删改都很快做出来了,做到查的时候,因为是一个或多个住院总,且需要姓名和工号两个字段,如何把多个医师信息集合传给前端呢?字符串拼接或是列表形式。字符串拼接的方式很简单,可以直接在SQL语句里进行拼接,使用拼接语句wm_concat(‘医师表.name’||(||'医师表.work_no‘||)||), 输出的是医师名(医师工号)

select #{page()} --分页函数,因为函数内不能用groupby所以做了嵌套
from
(select 
	#{use("pageCols")},wm_concat(ph.NAME || ph.WORK_NO) chiefResidents
	--pageCols包括了科室、时间、备注等等字段,chiefResidents是拼接后的住院总字段
from INFO_SCHEDULE_MANAGE  sd
	left join CORE_DEPT cd on sd.DEPT_ID = cd.DEPT_ID --从科室表抓科室名
	left join CORE_OPERATOR op on sd.publish_by = op.oper_id --创建人信息
	left join INFO_SCHEDULE_CHIEF_RESIDENT cr on cr.SCHEDULE_ID = sd.ID --住院总
	left join INFO_PHYSICIAN ph on cr.PHYSICIAN_ID = ph.ID --医师信息
where  #{use("pageCondition")}
	group by #{use("groupCols")}
-- @pageIgnoreTag(){
	order by sd.create_time desc,sd.id
-- @}
)

--但拼接的字符串只能有医师名(医师工号)的格式,如果需求格式变了,要改就只能改后端代码,很不灵活。一般来说后端最好不要给前端一串格式定死的字符串,而是用列表传多个字段集合,让前端自己根据需求使用。接下来我试着用了两种方法去实现:

  1. 使用JsonMapping映射格式
    响应类中使用@JsonMapper注解,在md文件中定义响应格式,返回嵌套list
    @JsonMapper ( resource = “infoMoralEvaDetail . detailJsonMapping” )
    【使用json映射】 【md文件(sql)】 【文件中定义的映射格式】

    { --"参数名":"sql语句里的命名(不带表名,如重名则起别名)",
    	"deptName":"deptName",
    	"chiefResidents":
    	{
    		"residentName":"chiefResidentName",
    		"residentWorkNo":"chiefResidentWorkNo"
    	},
    

--JsonMapper可以很方便地自定义返回格式,但难点是每个字段都要对应上,因为合并list的规则是是主体完全一致的合并嵌套的list中的内容,如果主体参数传成了嵌套的参数就会被判定为不同的list,如果遇到字段很多很复杂的,一个一个对应就很麻烦。

  1. 使用循环插入嵌套的列表

--先查出主体信息再通过关联id查询其他表,将查询到的List插入到响应的字段里。这种方法相比前一种更常用,修改起来也更方便,比如之后需求说住院总不止要显示人名工号,还得显示职位,JsonMapper注解方法就要改响应类,改SQL语句和调整映射格式;而这种方法只需要更改嵌套List中相应的实体类就行,以下面的代码为例,在ScheduleSimpleResidentVo中加上职位字段,查询时加一个职位字段就行。

//查询排班信息(此时住院总List为空)
PageResult<SchedulePageVo> pageList = baseMapper.getPageList(dto, pageRequest);
if(ObjectUtil.isNotEmpty(pageList.getList())){ //先判断是否为空再进行后续操作
    //获取排班信息表ID列表
    List<Long> scheduleIdList = pageList.getList().stream().map(SchedulePageVo::getId).collect(Collectors.toList());
    //用排班信息表id对应医师信息,获取住院总信息
    List<ScheduleSimpleResidentVo> chiefResidentsList = infoScheduleChiefResidentDao.getResidentsByScheduleIdList(scheduleIdList);
    //map键值对应,键为Long类型的id,值为list类型的住院总信息,类似groupby
    Map<Long, List<ScheduleSimpleResidentVo>> map = chiefResidentsList.stream().collect(Collectors.groupingBy(ScheduleSimpleResidentVo::getScheduleId));
    //将住院总List依次插入排班信息
    pageList.getList().forEach(t -> {
        t.setChiefResidentsList(map.get(t.getId()));
    });
}

--第一周基本就做了这些工作,大部分时间还是在补基础,同时去详细了解了一下java8的stream流操作。其实做这些简单的增删改查,对于上手新技术还是挺有帮助的,至少我在做完这些之后对BeetlSQL的使用有了实际的体会,不再是光看文档时那种空中楼阁了。与此同时,因为写的过程中各种出错,感觉自己debug的能力也大大增强了……至少比在学校照着教程敲代码更能锻炼人。
--而且在由于这个项目集成了swagger接口文档,使前后端分离开发的对接过程更加方便,只要在代码中加上相应的接口注解,文档里就会自动生成相应的接口信息,前端直接就能看到接口的请求返回信息,甚至还能直接通过文档来测试接口,对于头一次接触的小菜鸡来说简直是太方便了,哈哈。

2023-07-24~2023-07-30 第二周

==========

--第二周开始参与龙岗医务系统的需求开发,系统的整体架构基本和省二是一样的。

07-24 资质授权汇总列表

==========

需求描述:

  1. 做一个汇总,列表就是展示手术、处方、麻醉等权限的状态。详情进去看到每个子类的详情,比如手术有多少个、处方是几级。
  2. 页面浏览权限设置,医务科、科主任可以看到所有人的详细信息,比如手术状态、处方状态

--接到需求后先大致构思一下怎么实现,像是这种在原有基础上新增的需求,可以看看原本的代码有哪些是可以复用的,哪些是需要重新写接口的,哪些地方需要和产品经理再确认一下具体的要求。

我的思考:

  1. 资质授权做汇总列表,展示的权限状态包括哪些?技术级别、审核状态和暂停/启用状态?
  2. 科主任只能看本科室还是可以跨科室?(产品答:科主任只能看本科室)

--询问产品后得到了页面原型,汇总页面可以展示医师基础信息以及相应的权限级别(通过字典表对应),同时可以通过医师基础信息(姓名、科室、工号)搜索。

姓名工号科室手术权限抗生素权限……处方资质
张三1001内科一级、二级、四级非限制使用……非限制级

--点击后显示的详情暂时可以先不做,所以不考虑。不过其实可以直接调用现有的接口,只是需要加几个字段。

--但是写的时候发现,存放授权信息的表结构是一次权限申请为一条数据,通过权限标识字段来区分是什么的权限(1:手术 2:处方 3:抗生素 4:麻醉 ……),也就是说如果一个医师申请了两次,第一次申请一级手术,第二次同时申请了一级和四级的手术,那么他在表里会有两条数据,权限字段分别为1和1,4(逗号分隔),因此直接使用字符拼接wm_concat方法会变成【1,1,4】,即返回【一级,一级,四级】,需要进行去重处理。

--又因为同一个医师的权限有许多种,也不方便使用行转列的方法直接在SQL里实现权限合并,所以最后是用了笨办法:多个查询嵌套,以申请人分组把权限合并,转换为一条个人权限信息的列,最后在代码里遍历进行去重。其实也可以通过在数据库里自定义函数来去重,但尝试了几次都报错,而且确实很麻烦,最后放弃了……

SELECT APPLY_USER, --申请人
(SELECT wm_concat(APPLY_LEVEL) from QUALIFICATIONS_APPLY where APPLY_OPERATION = 1 and APPLY_USER = QA.APPLY_USER)  op1, --手术权限合并
(SELECT wm_concat(APPLY_LEVEL) from QUALIFICATIONS_APPLY where APPLY_OPERATION = 2 and APPLY_USER = QA.APPLY_USER)  op2, --处方权限合并
(SELECT wm_concat(APPLY_LEVEL) from QUALIFICATIONS_APPLY where APPLY_OPERATION = 3 and APPLY_USER = QA.APPLY_USER)  op3, --抗生素权限合并,以此类推
(SELECT wm_concat(APPLY_LEVEL) from QUALIFICATIONS_APPLY where APPLY_OPERATION = 4 and APPLY_USER = QA.APPLY_USER)  op4,
(SELECT wm_concat(APPLY_LEVEL) from QUALIFICATIONS_APPLY where APPLY_OPERATION = 5 and APPLY_USER = QA.APPLY_USER)  op5
from QUALIFICATIONS_APPLY qa
GROUP BY APPLY_USER  --以申请人来分组合并

--代码里自定义去重函数,先从字符串取出以逗号分隔的权限等级放入列表,列表去重后再转回逗号分隔的字符串:

public String distinctString(String a){
        if (a == null) {return null;}
        String[] c = a.split(",");
        List<String> list = Arrays.asList(c);
        String b = list.stream().distinct().collect(Collectors.joining(","));
//        list = list.stream().distinct().collect(Collectors.toList());
//        String b = String.join(",",list);
        return b;
    }

--从数据库中查出数据后遍历,依次调用函数去重:

PageResult<QualificationsSummaryVo> pageList = baseMapper.getSummaryPageList(dto,pageRequest);
if(ObjectUtil.isNotEmpty(pageList)){
    pageList.getList().forEach(t->{
        //技术级别去重
        t.setAntibioticLevel(distinctString(t.getAntibioticLevel()));
        t.setInternetLevel(distinctString(t.getInternetLevel()));
        t.setOperationLevel(distinctString(t.getOperationLevel()));
        t.setPrescriptionLevel(distinctString(t.getPrescriptionLevel()));
        t.setPathologyLevel(distinctString(t.getPathologyLevel()));
        t.setRadiationLevel(distinctString(t.getRadiationLevel()));
        t.setAnaesthesiaLevel(distinctString(t.getAnaesthesiaLevel()));
    });
}

--然后在传到前端时进行字典转换,就能正常显示不重复的权限了。

浏览权限设置:

--查询当前用户持有的角色id,如果有相应的权限则展示相应的信息。管理员和医务科可以一起判断(都是看所有),科主任取他的科室号筛选出本科室的数据展示,如果是普通医师则取account传入查询他个人的权限信息。account即用户id,查询时若传入则只查询相应用户的字段,也就是控制浏览权限,普通用户只能看到自己相关的信息
--一开始我的处理是先赋值account,如果是管理员/医务科则account置空,但枚姐说这有点反直觉,应该默认为空判断后才赋值

--修改之后的处理逻辑: 默认account为null
    if 是管理员/医务科?是则不处理(null)默认看所有
    else if是科主任?是则传入科室ID(deptId)限制只能看相应科室的信息
    else 传入用户id(account)限制只能看当前用户的信息

--顺便一提,因为加了新的接口,一开始测试的时候明明用的是管理员账号却显示无操作权限,问了枚姐才知道新的接口要在权限管理模块添加接口并给相应角色授权,没有权限的用户是不能使用相关接口的。不过测试的时候可以先暂时避开权限控制,在不需要权限控制的资源表达式那里配置接口,这样就可以正常测试了。等到确定完成后再添加权限,方便前端进行调试开发。

关于字典转换的笔记:

--后端为了方便判断和存储,经常用数字指代特定状态,如0-否,1-是,而前端获取返回值的时候要做额外的转换,比较麻烦,所以后端传值给前端时可以做字典转换,将01转换为是否,方便前端直接调用。

		@Dict(type = "[code]") 
		private String Level;
		//type实际为自定义的,根据Dict接口类的设置写;[code]在字典类型表中

--字典类型表:存字典类型唯一id,字典对应字段code,字典类型名,是否启用等等
--字典表:存字典值(id, 值[code], 对应字段[name], 对应字典类型id)

List集合转逗号分隔字符串的方法

	// 如何把list集合拼接成以逗号分隔的字符串 a,b,c  
List<String> list = Arrays.asList("a", "b", "c"); 
	// 第一种方法,可以用stream流  
String join = list.stream().collect(Collectors.joining(","));  
System.out.println(join); // 输出 a,b,c  
	// 第二种方法,其实String也有join方法可以实现这个功能  
String join = String.join(",", list);  
System.out.println(join); // 输出 a,b,c  

07-26 轮科申请增加字段

==========

需求:
--轮科申请增加拟开始时间、拟结束时间;医务科填写实际开始时间、实际结束时间
思考:
--新增申请原本有开始结束时间字段,直接用于存拟开始拟结束时间,让前端把名字改改就行,申请表加两条字段:实际开始和实际结束时间,在医务科审核的接口的请求类增加字段。

--因为审核调用的是编辑接口,直接修改了编辑增加两个字段,主要是改编辑dto请求类和查询方法的vo响应类;审核界面的详情需要展示实际开始结束时间,即列表查询响应类增加实际时间字段。
--很简单的修改,基本只用改实体类和sql语句就行,不过枚姐跟我说,一般来说这种新增需求,最主要的就是不要大改原本的代码,特别是字段名(因为我原本想改拟开始结束时间的字段名来着),因为你不知道除了这里还会有哪里需要调用,所以单纯的加字段是最好的处理方法。虽然确实是这样啦,但怎么说呢,莫名想到了屎山代码……

--因为比较简单,很快就做完了,下午抽空研究了一下别的东西。

事务管理-回滚操作

@Transactional(rollbackFor = Exception.class)
--如果抛出异常就回滚操作,比如删到一半异常了,另一半删不掉,就把已经删除的一半复原。
--Spring Boot 默认的事务规则是遇到运行异常(RuntimeException)和程序错误(Error)才会回滚。比如上面我们的例子中抛出的 RuntimeException 就没有问题,但是抛出 SQLException 就无法回滚了。针对非运行时异常,如果要进行事务回滚的话,可以在 @Transactional 注解中使用 rollbackFor 属性来指定异常,比如 @Transactional(rollbackFor = Exception.class),这样就没有问题了,所以在实际项目中,一定要指定异常。

07-27 表单传发功能(已暂停)

--可参考https://juejin.cn/post/7104175160315346957#heading-1
--需求:
  1. 系统上传一份excel表单,各科室都可以在系统上填写。
  2. 填写后能回收填写内容
思考:
  1. 填写的页面在哪里?单开一个页面吗?权限怎么设置?
  2. 回收内容,页面上浏览,有没有导出?(导出应该是前端调用方法,不知道现有导出方法能不能直接用)
  新增,即建表单,新增一条表单数据,表单id唯一标识
  填写表单调用edit方法,(设置权限后面再考虑,先实现功能)
  前端请求字段:表单id(唯一标识),科室?日期?
  表单列表展示,返回表单id,名称,新建/修改日期,创建人?科室?
  注意表单包含多个sheet(参考
  获取表单详情/进入编辑:请求:表单id,用户信息?(工号之类的,确定是谁在填)
  响应:表单id,表单内容(json格式,后端解压)
  后端逻辑:需要字段:主键ID,报表名,报表描述,报表内容(压缩),工作表标识(当前sheet?),创建人,创建时间,更新时间,delete_flag(?)
  
--因为接下来要做一个比较急的项目需求所以这个暂时搁置了,本来其实就是不着急做的功能,枚姐看我闲着让我练练来着(。)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值