一、开发总结
1.1 数据库设计
在数据库设计上一些字段没有做到见名知意
表中自己的字段前加上表名的首字母和下划线比如菜品表中菜品的名称正确的字段名称应该是name但是我在命名的时候命名为v_name(这是没必要的)一个表自己的字段必须要再加前缀
在spme框架中,任何一个表中都必须有create_time(创建时间)、update_time(修改时间)、is_deleted(逻辑删除)、create_by(创建者id)、update_by(更新着id),注意这里所有时间的类型都是datetime
1.2 菜品分配
需求:在菜品分配详情页面显示已经分配给大宗用户的菜品和未分配给大宗用户的菜品,给具体某个大宗用分配菜品,以及查询每个大宗用户的创建时间更新时间和菜品数量
1.2.1 查询没各大宗用户的创建时间更新时间和菜品数量
这里的创建时间是大宗用户所在部门的创建时间,更新时间是菜品分配的最新时间,也就是最近一次的分配时间,如果没有给该机构分配菜品那么菜品分配数量就显示0,更新时间就是什么都不显示。
难点:
- 查询每个机构分配的菜品数量是没有办法进行联表查询的
- 菜品的更新时间有很多,但是只能返回一条时间最新的数据,而且也没有办法进行联表查询
解决方案:
-
对于上面两个没法进行联表查询的状况都可以通过子查询来解决,将查询到的数量和最新时间作为一条数据返回
-
关于时间的问题有两个解决方案
- 在子查询中对时间进行降序排序,然后通过limit 1,1 来选取最大的那个时间
- 是在每次添加菜品时(由于前端组件的需要,每次重新分配菜品时都需要将之前分配的菜品全部删除然后重新添加)将菜品的时间都设为同一个值——now()这样在进行子查询的时候就不需要排序,只需要limit 1,1 即可
子查询按位置可以分为四类:
- 标量子查询:结果集只有一行一列,一般可作为查询结果或者查询条件,一般在select后面(select后面仅仅支持标量子查询),也可以出现在where或having后面
- 列子查询:结果集只有一行多列,只能作为查询条件,位于where或having后面
- 行子查询:结果集只有一行多列,只能作为查询条件,位于where或having后面
- 表子查询:结果集一般为多行多列,位于exists或from后面
本需求中需要作为查询结果,所以使用的是标量子查询
@Select("SELECT dept.id,dept.`name`,dept.create_time,( \n" +
"SELECT COUNT(*) \n" +
"FROM allocation a \n" +
"WHERE a.dept_id = dept.id \n" +
"AND a.is_deleted = 0 \n" +
") as con,\n" +
"( \n" +
"SELECT DISTINCT(a.create_time) \n" +
"FROM allocation a \n" +
"WHERE a.dept_id = dept.id \n" +
"AND a.is_deleted = 0 \n" +
"LIMIT 1,1\n" +
") as uptime \n" +
"FROM sys_dept dept \n" +
"WHERE dept.pid = 8 \n" +
"AND dept.is_deleted = 0 ")
1.2.2 查询分配给大宗用户的菜品
这里查询的是分配给具体某个机构的菜品,这里需要进行联表查询,连接菜品表以及中间表分配表
@Select("SELECT v.* FROM allocation a,vegetable v \n" +
"WHERE a.v_id = v.id AND a.dept_id = #{id} AND v.is_deleted = 0 AND a.is_deleted = 0 ")
1.2.3 查询未分配给大宗用户的菜品
在查询过程中要塞掉已经分配给某一个机构的所有菜品
实现这个功能可以将分配给大宗用的菜品和所有菜品都查出了,然后在所有菜品中剔除分配个大宗用户的菜品,我个人感觉这样写太麻烦了,需要两次查询数据库,所以就没有使用户该方式
我使用的方式通过行子查询,现将已分配的菜品id全部查询出来,然后再通过NOT IN关键字进行筛选
@Select("SELECT v.* FROM vegetable v \n" +
"WHERE v.id NOT IN ( \n" +
" SELECT a.v_id FROM allocation a \n" +
" WHERE a.dept_id = #{deptId} \n" +
" AND a.is_deleted = 0 \n" +
") AND v.is_deleted = 0 ")
1.3 手机端
1.3.1 手机端查询机构所分配到的菜品
手机端查询机构所分配到的菜品,手机端的需求和PC端的需求不一样,所以分两个接口来写,区别在手机端不仅是机构的大宗用户来查询,机构下窗口的普通商户也许要查询,这个时候在条件查询时就需要用到OR来进行连接,在判断是部门id是对应要查询的部门id或者上级部门id是对应的部门id来进行查询。
@Select("SELECT v.* \n" +
"FROM allocation a,vegetable v,sys_dept dept\n" +
"WHERE a.v_id = v.id \n" +
"AND (a.dept_id = dept.id \n" +
"OR a.dept_id = dept.pid) \n" +
"AND dept.id = #{deptId} \n" +
"AND v.is_deleted = 0 \n" +
"AND a.is_deleted = 0 \n" +
"AND dept.is_deleted = 0 ")
二、框架收获
2.1 分页查询
在controller层中方法的返回值类型为Result<IPage<具体的实体类>> 传入的参数中必须得有一项PageVO类型的参数,而且在方法体中,调用service层中的方法中所传的参数也必须得传入PageVO类中的buildPage()方法
在service层中和mapper层的方法中的返回值类型为IPage<具体的实体类> 同样的在所传入的参数中也必须得有IPage<实体类>类型的参数。当前页和当前页的显示条数前端可以不传,如果不传的话默认是第一页,每页显示10条数据。
2.2 权限管理
需要在 Controller层的方法前加上@PreAuthorize注解,
该注解的使用方式
@PreAuthorize("@smpe.check('具体的权限标识符')")
2.3 @Slf4j
@Slf4j是用作日志输出的,一般会在项目每个类的开头加入该注解,如果不写下面这段代码,并且想用log
private final Logger logger = LoggerFactory.getLogger(当前类名.class);
添加了该注解之后,就可以直接在代码中引用log.info()打印日志
2.4 @RequiredArgsConstructor
这个注解是用来代替注解@Autowired的,该注解是用在类上的注解,使用该注解后引入新的类就必须用final来进行修饰
2.5 @Api和@ApiOperation
这两个注解是swagger注解
@Api是使用在类上,表明是swagger资源
@ApiOperation使用在方法上,表示一个HTTP请求的操作其中value属性用于方法描述,notes方法用于提示内容,tags属性表示可以重新分组
2.6 @ApiImplicitParams和ApiImplicitParam
@ApiImplicitParams:用在请求的方法上,表示一组参数说明
@ApiImplicitParam:用在@ApiImplicitParams注解中,指定一个请求参数的各个方面
name:参数名
value:参数的汉字说明、解释
required:参数是否必须传
paramType:参数放在哪个地方
header --> 请求参数的获取:@RequestHeader
query --> 请求参数的获取:@RequestParam
path(用于restful接口)–> 请求参数的获取:@PathVariable
body(不常用)
form(不常用)
dataType:参数类型,默认String,其它值dataType=“Integer”
defaultValue:参数的默认值
2.7 关于LocalDateTime的使用
LocalDateTime是smpe框架自己定义的一个类,该类的功能非常强大,他可以将前端传过来的时间戳转换为Date类型然后存入数据库,也可以将数据库中查询出来的datetime类型转换为时间戳类型
三、自我反思
本次接受项目比较仓促,在接受项目之前并没有做任何准备,对小组的框架SMPE也没有做过多的了解,导致刚开始开发的时候感觉非常的艰难,不知道如何去使用框架开发,通过一上午的探索,终于可以正常进行接口的开发了,知道如何去进行分页查询,如何给接口添加权限。
但是我在本次项目中有点急于求成,为了快速完成开发,框架中的很多东西没去探索,比如条件构造器,不过这也有点畏难的情绪在里面。以后要用于探索,勇于挑战难点。
通过本次项目我发现了我太多的不足,比如对于权限控制那一块我不知道该如何去实现就去问别人,有的人更我说到jwt等一些基础知识的时候我居然都不知道,这也体现了我平时的学习是有问题的。以后在没接项目的时候多找学长沟通关于技术上的问题,及时学习不会的知识,如果再项目中遇到不会的东西,要想办法尽快学会,哪怕是熬夜看视频
对于遇到问题,我一遇到问题就去找别人,这样很耽误工夫,而且我比较喜欢直接去找别人,而不是直接通过钉钉等线上的方式需求帮助,这样来回跑也比较耽误时间。在以后开发项目中,如果遇到自己解决不了的问题,如果不影响问题就先将问题放一放,如果阻碍开发可以尝试着去开发其他部分,找一个集中的时间去解决问题。
四、项目反思
在项目开发初期需求不明确,导致在开发过程还在讨论需求,修改已经写好了的接口甚至造成接口重写。
占用洽谈室时间较长,我们在联调的那几天基本上每天都在占用洽谈室,我们联调那几天是遇到一个功能上的问题马上就在现场改,但是这个问题并没有涉及到其他人的接口,这也就占用了别人的时间,而正确的方式应该是在联调的时候统一测出所有接口的问题,然后每个人回去自己在下面进行修改。这样既节省了其他人的时间,也不会导致洽谈室背长期占用。