最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Springboot結(jié)合Mybatis-Plus實(shí)現(xiàn)業(yè)務(wù)撤銷回滾功能

 更新時(shí)間:2024年12月17日 11:29:32   作者:_院長大人_  
本文介紹了如何在Springboot結(jié)合Mybatis-Plus實(shí)現(xiàn)業(yè)務(wù)撤銷回滾功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

操作記錄表

CREATE TABLE `operation_record` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  `check_id` bigint(20) NOT NULL COMMENT '檢測id',
  `status` varchar(64) DEFAULT NULL COMMENT '檢測old狀態(tài)-檢測new狀態(tài)',
  `operation` varchar(255) NOT NULL COMMENT '操作事件',
  `deleted` tinyint(1) DEFAULT '0' COMMENT '是否刪除',
  `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '創(chuàng)建時(shí)間',
  `created_by_id` bigint(20) DEFAULT NULL COMMENT '創(chuàng)建者id',
  `created_by_name` varchar(255) DEFAULT NULL COMMENT '創(chuàng)建者名稱',
  `old_data` json DEFAULT NULL COMMENT '存儲(chǔ)操作前的舊數(shù)據(jù),JSON格式',
  `remark` varchar(255) DEFAULT NULL COMMENT '備注信息,用于記錄額外的操作說明',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC COMMENT='操作記錄';

操作記錄實(shí)體類

package com.applets.manager.core.model.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Builder;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

/**
 * <p>
 * 操作記錄實(shí)體類
 * </p>
 *
 * @author MybatisGenerator
 * @since 2024-05-15
 */
@Data
@Builder
@TableName("check_status_record")
public class CheckStatusRecord implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 自增id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 檢測id(業(yè)務(wù)主鍵,可以自行修改)
     */
    @TableField("check_id")
    private Long checkId;

    /**
     * 檢測狀態(tài)
     */
    @TableField("status")
    private String status;

    /**
     * 操作事件
     */
    @TableField("operation")
    private String operation;

    /**
     * 是否刪除
     */
    @TableField("deleted")
    private Boolean deleted;

    /**
     * 創(chuàng)建時(shí)間
     */
    @TableField("created_time")
    private LocalDateTime createdTime;

    /**
     * 創(chuàng)建者id
     */
    @TableField("created_by_id")
    private Long createdById;

    /**
     * 創(chuàng)建者名稱
     */
    @TableField("created_by_name")
    private String createdByName;

    /**
     * 老數(shù)據(jù)
     */
    @TableField("old_data")
    private String oldData;


    /**
     * 備注
     */
    @TableField("remark")
    private String remark;

}

操作記錄表數(shù)據(jù)存儲(chǔ)實(shí)例

執(zhí)行業(yè)務(wù),存入操作記錄并且存入快照數(shù)據(jù)

此處的我存在oldData的字段類型為map,這樣oldData可以一次性存入多個(gè)修改記錄,就比如說如下方法enterEvaluationPrice修改了ylcheck和saleConditions

  • key為YlCheck.class.getName() + “-” + “ylCheckMapper”+"-update"即(存入的對(duì)象全類名-容器中操作對(duì)應(yīng)類的mapper名稱-數(shù)據(jù)庫操作類型)
  • value 為歷史快照數(shù)據(jù)
  • 主要使用方法就是在做數(shù)據(jù)庫操作之前,把要操作的歷史數(shù)據(jù)查詢出來,json的方法序列化,存入操作記錄表中的oldData的字段,因?yàn)槲疫@里是操作了多個(gè)表,所有我用map的方式存儲(chǔ)多個(gè)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Result enterEvaluationPrice(EnterPriceReqVo req) {
        Integer deliveryDays = req.getDeliveryDays();

        Long id = req.getId();
        BigDecimal acquisitionPrice = req.getAcquisitionPrice();
        BigDecimal multiply = acquisitionPrice.multiply(new BigDecimal(10000));

        try {

            HashMap<String, Object> oldDataMap   = new HashMap<>();

            YlCheck ylCheck = ylCheckMapper.selectById(id);

            if (ylCheck == null) {

                return Result.failure(CommonResultStatus.VEHICLE_REPORT_NOT_FIND);
            }

            if (ObjectUtils.isNotEmpty(deliveryDays)) {
                LambdaUpdateWrapper<SaleConditions> saleConditionsLambdaUpdateWrapper = new LambdaUpdateWrapper<>();
                LambdaUpdateWrapper<SaleConditions> qw = saleConditionsLambdaUpdateWrapper
                        .eq(SaleConditions::getCarId, ylCheck.getCheckCarId());
                saleConditionsLambdaUpdateWrapper.set(SaleConditions::getDeliveryDays, deliveryDays);
                List<SaleConditions> saleConditions = saleConditionsMapper.selectList(qw);
                oldDataMap.put(SaleConditions.class.getName() + "-" + "saleConditionsMapper"+"-update", saleConditions); //存入歷史數(shù)據(jù)
                saleConditionsMapper.update(null, saleConditionsLambdaUpdateWrapper);
            }


            oldDataMap.put(YlCheck.class.getName() + "-" + "ylCheckMapper"+"-update", ylCheck);//存入歷史數(shù)據(jù)
            //其他業(yè)務(wù)代碼
            ylCheckMapper.update(null, new LambdaUpdateWrapper<YlCheck>()
                    .eq(YlCheck::getId, ylCheck.getId())
                    .set(YlCheck::getStatus, to.name())//變更后的狀態(tài)
            );

            // 存入操作記錄
            CheckStatusRecord checkStatusRecord = CheckStatusRecord.builder()
                    .checkId(ylCheck.getId())
                    .status(from.name() + "-" + to.name())
                    .operation(event.name())
                    .createdByName(localUserInfo.getUserName())
                    .createdTime(LocalDateTime.now())
                    .deleted(false)
                    .oldData(JSON.toJSONString(oldDataMap))
                    .build();
            checkStatusRecordMapper.insert(checkStatusRecord);
            //其他業(yè)務(wù)代碼
    }

評(píng)估回滾請(qǐng)求對(duì)象

  • 此處就傳了一個(gè)checkId即我這里的業(yè)務(wù)主鍵,可以自定義修改
package com.applets.manager.core.model.vo.req;

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.List;

@Data
@Accessors(chain = true)
@ApiModel("評(píng)估回滾請(qǐng)求對(duì)象")
public class YlCheckRollbackReqVo extends BaseReqVo implements Serializable {
    private static final long serialVersionUID = 718144265818263634L;
    /**
     * 檢測id
     */
    @ApiModelProperty("檢測id")
    private Long checkId;

}

根據(jù)操作記錄回滾業(yè)務(wù)數(shù)據(jù)

rollback業(yè)務(wù)方法

  • checkId 為某業(yè)務(wù)主鍵,可以自行改動(dòng)
  • 通過checkid查詢出對(duì)應(yīng)的操作記錄(我這里是查詢最新的,讀者可以自定義查詢條件)
  • 查到操作記錄后將historyRecord.getOldData() JSON 字符串反序列化為 Map
  • 遍歷 Map,反射獲取類和 Mapper
  • 通過反射獲取類,提供給反序列化時(shí)使用
  • 進(jìn)行反序列化
    • 檢查數(shù)據(jù)是否是數(shù)組類型還是單個(gè)對(duì)象(兩種反序列化的方式不同)
  • 根據(jù) type 判斷操作類型并進(jìn)行相應(yīng)的回滾(insert,update,delete)
    • 如果是邏輯刪除的化就單獨(dú)使用update回滾即可,把對(duì)應(yīng)的刪除標(biāo)志修改一下即可
    • 如果不是邏輯刪除的化insert回滾就需要反向執(zhí)行刪除操作;delete回滾就需要反向執(zhí)行插入操作.
    • 內(nèi)部也需要檢查數(shù)據(jù)是否是數(shù)組類型還是單個(gè)對(duì)象(baseMapper處理數(shù)組類型和單個(gè)對(duì)象的方法不同)
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollback(YlCheckRollbackReqVo req) {
        Long checkId = req.getCheckId();
        if (ObjectUtils.isEmpty(checkId)){
            throw new BusinessException("參數(shù)錯(cuò)誤");
        }
        // 查詢保存的歷史數(shù)據(jù)最新的一條 (JSON 字符串)
        LambdaQueryWrapper<CheckStatusRecord> qw = new LambdaQueryWrapper<>();
        qw.eq(CheckStatusRecord::getCheckId, checkId)
                .orderByDesc(CheckStatusRecord::getCreatedTime) 
                .last("LIMIT 1");  // 只查詢一條
        CheckStatusRecord historyRecord = checkStatusRecordMapper.selectOne(qw);
        if (historyRecord == null || historyRecord.getOldData() == null) {
            throw new BusinessException("沒有找到可以回滾的歷史數(shù)據(jù)");
        }

        // 1. 將 JSON 字符串反序列化為 Map
        Map<String, Object> oldDataMap = JSON.parseObject(historyRecord.getOldData(), Map.class);

        // 2. 遍歷 Map,反射獲取類和 Mapper
        for (Map.Entry<String, Object> entry : oldDataMap.entrySet()) {
            // 解析 key,獲取類名和 Mapper 名,和操作類型
            String[] keyParts = entry.getKey().split("-");
            String className = keyParts[0];
            String mapperName = keyParts[1];
            String type = keyParts[2];  // 獲取操作類型

            try {
                // 通過反射獲取類
                Class<?> clazz = Class.forName(className);

                Object oldData;

                // 3. 檢查數(shù)據(jù)是否是數(shù)組類型
                if (entry.getValue() instanceof JSONArray) {
                    // 如果是數(shù)組類型,使用 parseArray 進(jìn)行反序列化
                    oldData = JSON.parseArray(JSON.toJSONString(entry.getValue()), clazz);
                } else {
                    // 如果是單個(gè)對(duì)象,使用 parseObject 進(jìn)行反序列化
                    oldData = JSON.parseObject(JSON.toJSONString(entry.getValue()), clazz);
                }

                // 通過 Spring 應(yīng)用上下文獲取 Mapper 實(shí)例
                Object mapper = applicationContext.getBean(mapperName);

                // 4. 根據(jù) type 判斷操作類型并進(jìn)行相應(yīng)的回滾
                if ("insert".equals(type)) {
                    // 如果是新增操作,回滾時(shí)執(zhí)行刪除操作
                    if (oldData instanceof List) {
                        for (Object item : (List<?>) oldData) {
                            if (mapper instanceof BaseMapper) {
                                String jsonItem = JSON.toJSONString(item);
                                JSONObject jsonObjectItem = JSON.parseObject(jsonItem);
                                BaseMapper baseMapper = (BaseMapper) mapper;
                                baseMapper.deleteById(jsonObjectItem.getLong("id"));  // 刪除每個(gè)元素
                            }
                        }
                    } else {
                        if (mapper instanceof BaseMapper) {
                            String jsonOldData = JSON.toJSONString(oldData);
                            JSONObject jsonObjectOldData = JSON.parseObject(jsonOldData);
                            BaseMapper baseMapper = (BaseMapper) mapper;
                            baseMapper.deleteById(jsonObjectOldData.getLong("id"));  // 刪除單個(gè)對(duì)象
                        }
                    }
                } else if ("update".equals(type)) {
                    // 如果是更新操作,回滾時(shí)恢復(fù)原始數(shù)據(jù)
                    if (oldData instanceof List) {
                        for (Object item : (List<?>) oldData) {
                            if (mapper instanceof BaseMapper) {
                                BaseMapper baseMapper = (BaseMapper) mapper;
                                baseMapper.updateById(item);  // 恢復(fù)每個(gè)元素
                            }
                        }
                    } else {
                        if (mapper instanceof BaseMapper) {
                            BaseMapper baseMapper = (BaseMapper) mapper;
                            baseMapper.updateById(oldData);  // 恢復(fù)單個(gè)對(duì)象
                        }
                    }
                } else if ("delete".equals(type)) {
                    // 如果是刪除操作,回滾時(shí)重新插入數(shù)據(jù)
                    if (oldData instanceof List) {
                        for (Object item : (List<?>) oldData) {
                            if (mapper instanceof BaseMapper) {
                                BaseMapper baseMapper = (BaseMapper) mapper;
                                baseMapper.insert(item);  // 插入每個(gè)元素
                            }
                        }
                    } else {
                        if (mapper instanceof BaseMapper) {
                            BaseMapper baseMapper = (BaseMapper) mapper;
                            baseMapper.insert(oldData);  // 插入單個(gè)對(duì)象
                        }
                    }
                }

            } catch (ClassNotFoundException e) {
                log.error("回滾操作失敗: {}", e);
                throw new BusinessException("回滾操作失敗: " + className);
            }
        }
    }

業(yè)務(wù)回滾測試測試

到此這篇關(guān)于Springboot結(jié)合Mybatis-Plus實(shí)現(xiàn)業(yè)務(wù)撤銷回滾功能的文章就介紹到這了,更多相關(guān)Springboot Mybatis-Plus業(yè)務(wù)撤銷回滾內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java控制臺(tái)實(shí)現(xiàn)拼圖游戲

    java控制臺(tái)實(shí)現(xiàn)拼圖游戲

    這篇文章主要為大家詳細(xì)介紹了java控制臺(tái)實(shí)現(xiàn)拼圖游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • spring學(xué)習(xí)JdbcTemplate數(shù)據(jù)庫事務(wù)管理

    spring學(xué)習(xí)JdbcTemplate數(shù)據(jù)庫事務(wù)管理

    這篇文章主要為大家介紹了spring學(xué)習(xí)JdbcTemplate數(shù)據(jù)庫事務(wù)管理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • SpringBoot攔截器的配置使用介紹

    SpringBoot攔截器的配置使用介紹

    攔截器可以用來實(shí)現(xiàn)未滿足某些條件,不容許訪問某些資源。SpringBoot 支持?jǐn)r截器,本文主要介紹攔截器的使用與原理
    2022-10-10
  • 詳解如何使用Java流API構(gòu)建樹形結(jié)構(gòu)數(shù)據(jù)

    詳解如何使用Java流API構(gòu)建樹形結(jié)構(gòu)數(shù)據(jù)

    在實(shí)際開發(fā)中,構(gòu)建樹狀層次結(jié)構(gòu)是常見需求,本文主要為大家詳細(xì)介紹了如何使用Java 8 Stream API將扁平化的菜單數(shù)據(jù)轉(zhuǎn)換為具有層級(jí)關(guān)系的樹形結(jié)構(gòu),需要的可以參考下
    2024-04-04
  • SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法

    SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法

    這篇文章主要給大家介紹了關(guān)于SpringBoot實(shí)現(xiàn)類似鉤子函數(shù)的方法,文中通過代碼示例介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2024-04-04
  • 一篇文章帶你學(xué)習(xí)Mybatis-Plus(新手入門)

    一篇文章帶你學(xué)習(xí)Mybatis-Plus(新手入門)

    這篇文章主要介紹了MyBatis-Plus 快速入門案例(小白教程),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-08-08
  • Feign調(diào)用傳輸文件異常的解決

    Feign調(diào)用傳輸文件異常的解決

    這篇文章主要介紹了Feign調(diào)用傳輸文件異常的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • Mybatis使用IN語句查詢的實(shí)現(xiàn)

    Mybatis使用IN語句查詢的實(shí)現(xiàn)

    這篇文章主要介紹了Mybatis使用IN語句查詢的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java面向?qū)ο笾橄箢?接口的那些事

    Java面向?qū)ο笾橄箢?接口的那些事

    這篇文章主要介紹了Java面向?qū)ο蠡A(chǔ):抽象類和接口,文中代碼可以幫助各位更好的理解學(xué)習(xí),有需求的小伙伴可以參考下,希望能夠給你帶來幫助
    2021-08-08
  • JVM:晚期(運(yùn)行期)優(yōu)化的深入理解

    JVM:晚期(運(yùn)行期)優(yōu)化的深入理解

    今天小編就為大家分享一篇關(guān)于JVM:晚期(運(yùn)行期)優(yōu)化的深入理解,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧
    2019-02-02

最新評(píng)論

会宁县| 仲巴县| 昆明市| 嘉黎县| 淅川县| 都江堰市| 濉溪县| 湘潭市| 汉寿县| 仙桃市| 青海省| 法库县| 顺平县| 临汾市| 句容市| 宁波市| 房产| 临城县| 长垣县| 新津县| 绥宁县| 崇阳县| 普兰店市| 赞皇县| 彝良县| 新龙县| 衡水市| 西畴县| 中牟县| 宜昌市| 五河县| 兴文县| 常德市| 丰原市| 综艺| 凤阳县| 永德县| 兴和县| 珠海市| 石屏县| 手机|