• No9.【spring-cloud-alibaba】添加字典业务,这一模块就是纯业务


    PigUserDetailsService  代码地址与接口文档看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客


    终于结束从零搭建springcloud的部分了,目前也仅仅是学习了最最基本的逻辑,同时包含了开发系统的一些基本的逻辑。接下来就按照 pig 文档将其余基本的内容再熟悉一下,看一遍和写一遍真的不一样呐~~~

    那接下来就一小模块一小模块的学习啦,加油吧少年!

    本文及以后的文章还是基于前面的No6系列文章开发的,可以看之前文章顶部的内容总结,简单了解详情~

    目录

    A1.添加字典功能

    B1.步骤

    B2.编码

    B3.测试

    遇到的问题:

    1.基础增删改查需要判断是否存在

    2.增删改查业务添加一个同意逻辑处理模块


    A1.添加字典功能

    字典,又分为字典和字典项,每个字典项属于某一个字典,每个字典有多个字典项。这样前端就可以根据字典拿到字典项。

    获取到的字典的属性,要尽可能适用于前端的组件,方便前端使用。

    B1.步骤

    首先,我们需要提供一个字典的增删改查功能,然后再提供一个字典项的增删改查功能,同时字典项必须要关联一个字典,并且字典项要有值和标签,值对应业务操作,标签对应用户展示。

     由于没有复杂业务,所以不需要关联表操作,不需要写 mapper 语句~

    B2.编码

    确定好数据结构之后,一定要先写接口文档,梳理编码逻辑!

    1.新建两个数据表,一个字典sys_dict,一个字典项sys_dict_item,设置 id 自增;

    2.新建表对应的实体类,要设置 mps 的id自增,和逻辑删除注解;

    3.新建 mapper 接口,继承 mps 的 BaseMapper ,不需要新增方法;

    4.新建 service 接口及实现类,继承 mps 的 IService、IServiceImpl ;

    5.新建 controller 类,编写接口端点方法,并确定好需要调用的 service 方法;

    【我习惯了解数据接口和接口文档后,就针对他们编写 controller 中的接口端点,然后再编写 service 业务方法】

    6.有些复杂的业务需要增加业务方法,所以修改 service 类,编写 controller 需要的方法;

    1. -- 1.新建两个数据表,一个字典sys_dict,一个字典项sys_dict_item,设置 id 自增;
    2. CREATE TABLE `sys_dict` (
    3. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    4. `dict_type` varchar(100) DEFAULT NULL COMMENT '标识',
    5. `description` varchar(100) DEFAULT NULL COMMENT '描述',
    6. `remark` varchar(255) DEFAULT NULL COMMENT '备注',
    7. `system_flag` char(1) DEFAULT '0' COMMENT '是否是系统内置',
    8. `del_flag` char(1) DEFAULT '0' COMMENT '删除标记',
    9. `create_time` datetime DEFAULT NULL COMMENT '创建时间',
    10. `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
    11. `update_by` varchar(64) DEFAULT NULL COMMENT '更新人',
    12. `update_time` datetime DEFAULT NULL COMMENT '更新时间',
    13. PRIMARY KEY (`id`) USING BTREE,
    14. KEY `sys_dict_del_flag` (`del_flag`) USING BTREE
    15. ) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8mb4 COMMENT='字典表'
    16. CREATE TABLE `sys_dict_item` (
    17. `id` bigint(20) NOT NULL AUTO_INCREMENT,
    18. `dict_id` bigint(20) NOT NULL COMMENT '字典ID',
    19. `dict_type` varchar(100) DEFAULT NULL COMMENT '字典标识',
    20. `value` varchar(100) DEFAULT NULL COMMENT '值',
    21. `label` varchar(100) DEFAULT NULL COMMENT '标签',
    22. `description` varchar(100) DEFAULT NULL COMMENT '描述',
    23. `sort_order` int(11) NOT NULL DEFAULT '0' COMMENT '排序(升序)',
    24. `remark` varchar(255) DEFAULT ' ' COMMENT '备注',
    25. `del_flag` char(1) DEFAULT '0' COMMENT '删除标记',
    26. `create_time` datetime DEFAULT NULL COMMENT '创建时间',
    27. `create_by` varchar(64) DEFAULT NULL COMMENT '创建人',
    28. `update_by` varchar(64) DEFAULT NULL COMMENT '修改人',
    29. `update_time` datetime DEFAULT NULL COMMENT '更新时间',
    30. PRIMARY KEY (`id`) USING BTREE,
    31. KEY `sys_dict_value` (`value`) USING BTREE,
    32. KEY `sys_dict_label` (`label`) USING BTREE,
    33. KEY `sys_dict_del_flag` (`del_flag`) USING BTREE
    34. ) ENGINE=InnoDB AUTO_INCREMENT=33 DEFAULT CHARSET=utf8mb4 COMMENT='字典项'
    1. //2.新建表对应的实体类,要设置 mps 的id自增,和逻辑删除注解;
    2. //新增
    3. com.pig4cloud.pig.admin.api.entity.SysDict extends BaseEntity
    4. com.pig4cloud.pig.admin.api.entity.SysDictItem extends BaseEntity
    1. //3.新建 mapper 接口,继承 mps 的 BaseMapper ,不需要新增方法;
    2. @Mapper
    3. public interface SysDictMapper extends BaseMapper {
    4. }
    5. @Mapper
    6. public interface SysDictItemMapper extends BaseMapper {
    7. }
    1. //4.新建 service 接口及实现类,继承 mps 的 IService、IServiceImpl ;
    2. public class SysDictServiceImpl extends ServiceImpl implements SysDictService {
    3. }
    4. public class SysDictItemServiceImpl extends ServiceImpl implements SysDictItemService {
    5. }
    1. //5.新建 controller 类,编写接口端点方法,并确定好需要调用的 service 方法;
    2. //其中只有两个更新和两个删除的需要重写对应的 service 方法,因为需要判断是否能删除,并且涉及到关联删除~
    3. //其余的使用 mps 提供的 service 方法就行
    4. @RestController
    5. @RequestMapping("/dict")
    6. @RequiredArgsConstructor
    7. public class DictController {
    8. private final SysDictService sysDictService;
    9. private final SysDictItemService sysDictItemService;
    10. /**
    11. * @Description: 添加
    12. * @param: [dict]
    13. * @return: com.pig4cloud.pig.common.core.util.R
    14. **/
    15. @SysLog("添加字典")
    16. @PostMapping
    17. @PreAuthorize("@pms.hasPermission('sys_dict_add')")
    18. public R save(@RequestBody SysDict dict) {
    19. //标签必须唯一,先判断是否已存在
    20. SysDict sysDict = sysDictService.getOne(Wrappers.lambdaQuery().eq(SysDict::getDictType, dict.getDictType()));
    21. if ( sysDict != null){
    22. return R.failed("标签已存在");
    23. }
    24. return R.ok(sysDictService.save(dict));
    25. }
    26. /**
    27. * @Description: 分页查询字典
    28. * @param: [page, dict]
    29. * @return: com.pig4cloud.pig.common.core.util.R>
    30. **/
    31. @SysLog("分页查询字典")
    32. @GetMapping("/page")
    33. @PreAuthorize("@pms.hasPermission('sys_dict_list')")
    34. public R> getPage(Page page, SysDict dict) {
    35. LambdaQueryWrapper wrapper = Wrappers.lambdaQuery();
    36. wrapper.eq(SysDict::getDelFlag, CommonConstants.NOT_DELETE);
    37. if(StrUtil.isNotBlank(dict.getDictType())){
    38. wrapper.like(SysDict::getDictType, dict.getDictType());
    39. }
    40. if(StrUtil.isNotBlank(dict.getDictType())){
    41. wrapper.like(SysDict::getSystemFlag, dict.getSystemFlag());
    42. }
    43. return R.ok(sysDictService.page(page, wrapper));
    44. }
    45. /**
    46. * @Description: 通过ID查询字典
    47. * @param: [id]
    48. * @return: com.pig4cloud.pig.common.core.util.R
    49. **/
    50. @SysLog("通过ID查询字典")
    51. @GetMapping("/{id}")
    52. public R getById(@PathVariable Long id) {
    53. return R.ok(sysDictService.getById(id));
    54. }
    55. /**
    56. * @Description: 添加
    57. * @param: [dictItem]
    58. * @return: com.pig4cloud.pig.common.core.util.R
    59. **/
    60. @SysLog("添加字典项")
    61. @PostMapping("/item")
    62. public R save(@RequestBody SysDictItem dictItem) {
    63. //value 必须唯一,先判断当前字典中是否已存在该 value
    64. SysDictItem sysDictItem = sysDictItemService.getOne(
    65. Wrappers.lambdaQuery().eq(SysDictItem::getDictType, dictItem.getDictType()).eq(SysDictItem::getValue, dictItem.getValue()));
    66. if ( sysDictItem != null){
    67. return R.failed("值已存在");
    68. }
    69. return R.ok(sysDictItemService.save(dictItem));
    70. }
    71. /**
    72. * @Description: 分页查询某个字典的字典项
    73. * @param: [page, dictItem]
    74. * @return: com.pig4cloud.pig.common.core.util.R>
    75. **/
    76. @SysLog("分页查询某个字典的字典项")
    77. @GetMapping("/item/page")
    78. @PreAuthorize("@pms.hasPermission('sys_dictItem_list')")
    79. public R> getDictItemPage(Page page, SysDictItem dictItem) {
    80. return R.ok(sysDictItemService.page(page,
    81. Wrappers.lambdaQuery().eq(SysDictItem::getDictType, dictItem.getDictType())
    82. .eq(SysDictItem::getDelFlag, CommonConstants.NOT_DELETE)));
    83. }
    84. /**
    85. * @Description: 通过ID查询字典项
    86. * @param: [id]
    87. * @return: com.pig4cloud.pig.common.core.util.R
    88. **/
    89. @SysLog("通过ID查询字典项")
    90. @GetMapping("/item/{id}")
    91. public R getDictItemById(@PathVariable Long id) {
    92. return R.ok(sysDictItemService.getById(id));
    93. }
    94. /**
    95. * @Description: 通过字典标识查找字典项
    96. * @param: [type]
    97. * @return: com.pig4cloud.pig.common.core.util.R>
    98. **/
    99. @GetMapping("/dictType/{dictType}")
    100. public R> getDictByType(@PathVariable String dictType) {
    101. return R.ok(sysDictItemService.list(
    102. Wrappers.query().lambda().eq(SysDictItem::getDictType, dictType).eq(SysDictItem::getDelFlag, CommonConstants.NOT_DELETE)));
    103. }
    104. }
    1. //6.有些复杂的业务需要增加业务方法,所以修改 service 类,编写 controller 需要的方法;
    2. @Service
    3. @RequiredArgsConstructor
    4. public class SysDictServiceImpl extends ServiceImpl implements SysDictService {
    5. private final SysDictItemMapper dictItemMapper;
    6. @Override
    7. public Boolean updateDict(SysDict sysDict) {
    8. //1.先拿到对应的存储的信息
    9. SysDict dict = this.getById(sysDict.getId());
    10. // 判断是不是系统内置的,如果是则抛异常不能修改
    11. if(!DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())){
    12. throw new RuntimeException("系统内置字典不能修改");
    13. }
    14. //不是系统内置,则进行修改
    15. this.updateById(sysDict);
    16. return Boolean.TRUE;
    17. }
    18. @Override
    19. public Boolean deleteDict(Long id) {
    20. //先拿到对应的存储的信息
    21. SysDict dict = this.getById(id);
    22. // 判断是不是系统内置的,如果是则抛异常不能删除
    23. if(!DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())){
    24. throw new RuntimeException("系统内置字典不允许删除");
    25. }
    26. //不是系统内置,则进行修改
    27. baseMapper.deleteById(id);
    28. //同时删除字典项
    29. dictItemMapper.delete(Wrappers.lambdaQuery().eq(SysDictItem::getDictId, id));
    30. return Boolean.TRUE;
    31. }
    32. }
    33. @Service
    34. @RequiredArgsConstructor
    35. public class SysDictItemServiceImpl extends ServiceImpl implements SysDictItemService {
    36. private final SysDictService dictService;
    37. @Override
    38. public Boolean updateDictItem(SysDictItem dictItem) {
    39. //先获取字典的系统内置信息
    40. SysDict dict = dictService.getById(dictItem.getDictId());
    41. // 判断是不是系统内置的,如果是则抛异常不能修改
    42. if(!DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())){
    43. throw new RuntimeException("系统内置字典不能修改");
    44. }
    45. //不是系统内置,则进行修改
    46. this.updateById(dictItem);
    47. return Boolean.TRUE;
    48. }
    49. @Override
    50. public Boolean deleteDictItem(Long id) {
    51. // 先根据ID查询字典ID
    52. SysDictItem dictItem = this.getById(id);
    53. //再获取字典的系统内置信息
    54. SysDict dict = dictService.getById(dictItem.getDictId());
    55. // 判断是不是系统内置的,如果是则抛异常不能修改
    56. if(!DictTypeEnum.SYSTEM.getType().equals(dict.getSystemFlag())){
    57. throw new RuntimeException("系统内置字典不允许删除");
    58. }
    59. this.removeById(id);
    60. return Boolean.TRUE;
    61. }
    62. }
    63. @RestController
    64. @RequestMapping("/dict")
    65. @RequiredArgsConstructor
    66. public class DictController {
    67. private final SysDictService sysDictService;
    68. private final SysDictItemService sysDictItemService;
    69. /**
    70. * @Description: 修改
    71. * @param: [dict]
    72. * @return: com.pig4cloud.pig.common.core.util.R
    73. **/
    74. @SysLog("修改字典")
    75. @PutMapping
    76. @PreAuthorize("@pms.hasPermission('sys_dict_update')")
    77. public R update(@RequestBody SysDict dict) {
    78. //标签必须唯一,先判断是否已存在未删除的、该标签
    79. SysDict sysDict = sysDictService.getOne(Wrappers.lambdaQuery().eq(SysDict::getDictType, dict.getDictType()).ne(SysDict::getId, dict.getId()));
    80. if ( sysDict != null){
    81. return R.failed("标签已存在");
    82. }
    83. return R.ok(sysDictService.updateDict(dict));
    84. }
    85. /**
    86. * @Description: 删除
    87. * @param: [dict]
    88. * @return: com.pig4cloud.pig.common.core.util.R
    89. **/
    90. @SysLog("删除字典")
    91. @DeleteMapping("/{id:\\d+}")
    92. @PreAuthorize("@pms.hasPermission('sys_dict_del')")
    93. public R deleteDictById(@PathVariable Long id) {
    94. return R.ok(sysDictService.deleteDict(id));
    95. }
    96. /**
    97. * @Description: 修改
    98. * @param: [dictItem]
    99. * @return: com.pig4cloud.pig.common.core.util.R
    100. **/
    101. @SysLog("修改字典项")
    102. @PutMapping("/item")
    103. public R update(@RequestBody SysDictItem dictItem) {
    104. //value 必须唯一,先判断当前字典中是否已存在该 value
    105. SysDictItem sysDictItem = sysDictItemService.getOne(
    106. Wrappers.lambdaQuery().eq(SysDictItem::getDictType, dictItem.getDictType()).eq(SysDictItem::getValue, dictItem.getValue()).ne(SysDictItem::getId, dictItem.getId()));
    107. if ( sysDictItem != null){
    108. return R.failed("值已存在");
    109. }
    110. return R.ok(sysDictItemService.updateDictItem(dictItem));
    111. }
    112. /**
    113. * @Description: 删除
    114. * @param: [id]
    115. * @return: com.pig4cloud.pig.common.core.util.R
    116. **/
    117. @SysLog("删除字典项")
    118. @DeleteMapping("/item/{id:\\d+}")
    119. public R deleteDictItemById(@PathVariable Long id) {
    120. return R.ok(sysDictItemService.deleteDictItem(id));
    121. }
    122. }

    B3.测试

    然后就按照 api 接口测试吧,后面要学一下 api 自动化测试~ 

    遇到的问题:

    1.基础增删改查需要判断是否存在

    对于普通的单表,增删改查的业务中,如果遇到非正常操作怎么办?

    例如,单表中有唯一数据时,改中要校验唯一数据是否存在(自己本身包含不算)。如果入参的 id 不存在或者已删除怎么办?还需要修改么?

    从单账号操作的设计上来说,不会出现这种情况,毕竟操作的数据一定是未删除的。但是如果是多账号使用时,就会多个帐号同时删除此条数据,总有一个人成功,而其余人会失败,失败的人也要收到反馈信息。

    此时的反馈信息不应该是 500 报错的。

    看下图,我用多个线程来调用删除字典的操作,就有一个报错了,虽然最终的数据是对的。

    原因是 controller 里面的判断,他通过,但是 service 里面再获取时就获取到了 null。所以还是要处理一下该异常。

    新建一个业务异常处理类,然后可以在此处用 try catch 异常并抛出新增的业务异常处理类。

    1. @Slf4j
    2. @Order(10000)
    3. @RestControllerAdvice
    4. @ConditionalOnExpression("!'${security.oauth2.client.clientId}'.isEmpty()")
    5. public class GlobalBizExceptionHandler {
    6. /**
    7. * 全局异常.
    8. * @param e the e
    9. * @return R
    10. */
    11. @ExceptionHandler(Exception.class)
    12. @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    13. public R handleGlobalException(Exception e) {
    14. log.error("全局异常信息 ex={}", e.getMessage(), e);
    15. // 业务异常交由 sentinel 记录
    16. Tracer.trace(e);
    17. return R.failed(e.getLocalizedMessage());
    18. }
    19. }

     这样就不会报错 500 了

     

    2.增删改查业务添加一个同意逻辑处理模块

    写这一块儿的时候,总是修修改改,写某个接口时总感觉逻辑缺少,所以,整理一个基础的单表增删改查逻辑,方便开发理解。

    开个新文章,到时粘个链接~

  • 相关阅读:
    CSS3自定义滚动条的颜色等样式 - hover显示切换 - 伪类::-webkit-scrollbar的样式控制
    Linux 网络虚拟化 Macvlan(基于物理网络接口虚拟网络接口) 认知
    如何依据环境试验标准开展试验?
    jQuery 选择器
    河北邯郸:拓展基层就业空间 助力高校毕业生就业
    java 处理树形结构数据
    Tauri 2.0.0 beta环境搭建
    【JavaEE初阶】多线程 _ 基础篇 _ 线程池(案例四)
    远距离双目视觉测量系统纵深方向测量精度较低原因分析
    LVS(Linux Virtual Server)集群,(1)NAT模式
  • 原文地址:https://blog.csdn.net/vaevaevae233/article/details/127789564