主要是给自己留个笔记。
1 需求分析
明确需求,先做基本的业务,整个业务没问题之后,再进行优化。按需求优先级分为四个级别,然后开始开发。
2 库表设计
用户表、应用表、题目表、评分结果表、用户答题记录表
2.1 题目表
其中题目表的题目内容用 JSON 格式保存,而不用多条数据。为什么呢?考虑到功能,当我们需要对题目进行排序时,如果不用 Json,就需要加一个 alter 字段进行排序,很多题目的题号就需要改变,是很不方便的。
2.2 评分结果表
评分结果表中,resultProp 的结果属性集合 JSON 对应题目类型为测评类的应用,
而 resultScoreRange 的结果得分范围对应题目类型为得分类的应用。>=某分数的值就对应这种结果。
并且,使用 appId:应用 id 作为应用的索引,加快查找速度,因为我们是根据应用 id 去计算评分结果的。
2.3 用户答题记录表
解答几个问题:
1. 为什么用户答题记录表中,明明有 resultId 可以从结果表里取到 result 的其他相关字段(以及 appId 也是类似),却还要在用户答题记录表中增加相关的字段呢?
这里我们增加冗余,因为每次的测试结果都是固定的,一般情况下不会发生更新,类似于一个日志,这样用户查答案,看结果的时候就不需要联表查询了,可以提高效率。
2. 为什么用户答题记录表中的 resultId 可以为空?
第一,我们的 resultId不一定存在,因为我们定义了一项名为 scoringStrategy (评分策略)的字段,其中值为 0 是自定义的那两种策略,此时因为需要从数据库表中读取结果,故有 resultId;而 1 则表示使用 AI 来评分,直接把 AI 生成的结果返回就可以了,不需要 resultId。
第二,因为 AI 生成答案速度比较慢,所以我们在一阶段,用户答前面的题目的时候,就可以直接提前填充 not null 的部分。而为 null 的部分等到二阶段,答案生成之后再去填充即可,所以在前一阶段就可以使 resultId 为空。
choices 是 JSON 结构,题目选项的 key 数组,也就是类似于 ["A","B","C"]。只存储选项的优点:节省空间。缺点:一旦题目发生修改,就对应不上了。那么这样设计合理吗?
是合理的。因为当时用户所做的题目 A 和选项一定是绑定的,就算后来修改成了题目 B,也不会影响原来题目为 A 的结果。所以无论是题目 A 还是题目 B,只需要保持同一个题目,用户看见的题和选的选项正确就可以了。
3 项目生成
用后端代码模板来生成项目后打开
启动项目排错:起初以为是 lombok 版本错了,结果 xml 里并没有写 lombok 的版本。后来发现jdk 版本要改成1.8,我的 jdk 初始化默认为21了。
然后运行创建数据库的 sql 文件,直接点文件左上角在 IDEA 里面运行。
打开接口网址测试一下注册和登录,成功。
接着就是重构项目,改一下名称,删一些不需要的功能之类的。(ctrl+shift+R全局替换)
3.1 用MyBatisX生成代码
按住 ctrl 选中要生成的表,右键点 MyBatisX Generator
生成到另外的包 generator 中去,然后再复制到相应的包中,这样不会意外导致原先的包内代码被覆盖。
然后把每个新生成的实体类中的 id 生成算法由 AUTO 递增改成 ASSIGN_ID雪花算法,防止被人利用递增规律获取数据。
并在每个实体类的 isDelete 上打上 @TableLogic 逻辑删除标签。
(补逻辑删除用法:@TableLogic注解是逻辑删除,并不会永久删除数据,实体类加上这个注解再执行删除方法的时候会变成修改。前台再根据所加注解字段进行显隐即可达到逻辑删除效果。)
运行这个 codegenerator ,改改生成参数就可以很快生成代码。然后同样粘贴到我们项目的包里
4 项目修改
4.1 数据模型
因为 Service、Controller 层都需要用到数据模型(model),我们先从数据模型修改起。
1. 修改枚举类:前面提到的用 @TableLogic 修饰的 isDelete 不用写枚举类,因为这个逻辑删除由框架帮你做了,在代码里我们根本用不到这个 isDelete,也就不用写它的枚举类了。
枚举类没有 set 方法。
2. 修改封装类:xxxRequest 对应增删改查操作。
为什么需要封装类?例如 App 实体类对应封装类 appRequest 因为实体类有时需要存入数据库,但前端用户返回的数据和数据库数据是不一样的,有的数据用户是接触不到的。
那么生成 appRequest 的思路就很清晰了,先把实体类中的条目都复制到 Request 封装类中,然后把那些不希望用户接触到的数据(例如审核、管理方面的数据)删掉,就可以了。
这里题目内容被写为 JSON 格式,对前端来说是字符串,这样对于前后端都不方便解析。所以我们创建一个 DTO 包装类,前后端交互时用包装类,进数据库的时候再转为 JSON。
可以直接根据示例数据来向 CodeGeex 提问,生成这个包装类
3. 视图类VO:就是后端想要在前端展示的信息封装。可能会涉及编写字符串与对象的转换方法。比如 QuestionContentDTO 与 String 的互化。
4.2 接口和 Service
service: