精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

告別手工記錄!一文教你用 Spring Boot 玩轉自動化數(shù)據(jù)變更追蹤

開發(fā) 前端
通過 Javers + AOP + 注解 的結合,我們實現(xiàn)了一個零侵入、自動化、結構化的數(shù)據(jù)變更追蹤系統(tǒng)。? 它讓業(yè)務代碼保持純凈,讓審計邏輯統(tǒng)一、透明、可查詢。

在現(xiàn)代企業(yè)系統(tǒng)中,數(shù)據(jù)變更的可追溯性已不再是可選項,而是合規(guī)與審計的核心要求。本文將帶你一步步構建一個基于 Spring Boot + Javers 的自動化數(shù)據(jù)變更追蹤方案,真正告別手工記錄與混亂日志,讓系統(tǒng)能“自己講清楚”數(shù)據(jù)是何時、由誰、怎么變的。

為什么要自動化數(shù)據(jù)變更追蹤?

在金融、政務、電商、配置管理等系統(tǒng)中,運營人員最常問的幾個問題往往是:

  • 誰改了這條數(shù)據(jù)?
  • 什么時候改的?
  • 改了哪些字段?
  • 原始值是什么?能恢復嗎?

這些問題的背后,本質(zhì)上都是數(shù)據(jù)變更追蹤(Data Change Audit)。 很多團隊初期往往采用“人工記錄”的方式:

public void updatePrice(Long productId, BigDecimal newPrice) {
    Product old = productRepository.findById(productId).get();
    productRepository.updatePrice(productId, newPrice);
    auditService.save("價格從 " + old.getPrice() + " 改為 " + newPrice);
}

這種方式簡單直接,但當系統(tǒng)規(guī)模擴展后,問題接踵而至:

  • 代碼重復:幾乎每個業(yè)務方法都需要寫同樣的日志邏輯。
  • 維護困難:字段一變,日志邏輯也要改。
  • 風格混亂:不同開發(fā)者記錄格式不一致。
  • 難以查詢:字符串拼接日志沒法結構化檢索。
  • 邏輯耦合:業(yè)務代碼被審計邏輯污染。

典型痛點案例:

某產(chǎn)品價格被誤改,查了半天日志才定位到操作人; 某配置被誤刪,卻發(fā)現(xiàn)沒有字段級的變更記錄。

這些問題說明:手工審計已經(jīng)難以支撐復雜系統(tǒng)的可維護性。

目標與需求分析

要讓系統(tǒng)“自己追蹤變化”,我們需要一個自動化、可插拔的審計體系,它應滿足以下特性:

需求項

說明

零侵入性

業(yè)務邏輯不關心審計細節(jié)

自動化

配置或注解即可開啟

精確記錄

字段級別差異追蹤

結構化存儲

JSON 格式便于檢索

元數(shù)據(jù)完整

包含操作人、時間、動作類型等信息

技術方案選型

我們選用 Javers 作為核心比對組件,結合 Spring AOP 完成切面攔截與日志統(tǒng)一記錄。 Javers 的優(yōu)勢在于:

  • 提供 專業(yè)對象差異算法(支持復雜嵌套結構);
  • 與 Spring Boot 無縫集成;
  • 可輸出標準 JSON 差異;
  • 支持 MongoDB、SQL、文件等多種存儲方式。

系統(tǒng)設計思路

整體架構如下:

┌─────────────────┐
│   Controller    │
└─────────┬───────┘
          │ AOP攔截
┌─────────▼───────┐
│     Service     │ ← 業(yè)務邏輯保持純凈
└─────────┬───────┘
          │
┌─────────▼───────┐
│   AuditAspect   │ ← 審計切面統(tǒng)一處理
└─────────┬───────┘
          │
┌─────────▼───────┐
│   Javers Core   │ ← 對象差異比對引擎
└─────────┬───────┘
          │
┌─────────▼───────┐
│  Audit Storage  │ ← 結構化存儲與查詢
└─────────────────┘

核心設計理念:

  • 注解驅動:通過 @Audit 控制哪些方法被追蹤;
  • AOP 攔截:自動捕捉方法執(zhí)行;
  • Javers 比對:檢測對象變化;
  • 統(tǒng)一存儲:結構化記錄變更日志。

項目依賴配置

在 /src/main/resources/pom.xml 中加入以下依賴:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.javers</groupId>
        <artifactId>javers-core</artifactId>
        <version>7.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

核心模塊實現(xiàn)

審計注解 /src/main/java/com/icoderoad/audit/Audit.java

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Audit {


    String idField() default "id"; // 從實體中提取ID字段名


    String idParam() default "";   // 從方法參數(shù)中直接獲取ID


    ActionType action() default ActionType.AUTO; // 操作類型推斷


    String actorParam() default ""; // 操作人參數(shù)名


    int entityIndex() default 0; // 實體參數(shù)位置


    enum ActionType {
        CREATE, UPDATE, DELETE, AUTO
    }
}

審計切面 /src/main/java/com/icoderoad/audit/AuditAspect.java

@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class AuditAspect {


    private final Javers javers;
    private final List<AuditLog> auditTimeline = new CopyOnWriteArrayList<>();
    private final Map<String, Object> dataStore = new ConcurrentHashMap<>();
    private final AtomicLong auditSequence = new AtomicLong(0);


    @Around("@annotation(audit)")
    public Object around(ProceedingJoinPoint joinPoint, Audit audit) throws Throwable {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String[] paramNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();


        // 提取ID
        String entityId = extractEntityId(args, paramNames, audit);
        if (entityId == null) {
            log.warn("跳過審計:未找到實體ID");
            return joinPoint.proceed();
        }


        // 獲取執(zhí)行前快照
        Object before = dataStore.get(entityId);


        Object result = joinPoint.proceed();


        // 獲取執(zhí)行后快照
        Object after = args[audit.entityIndex()];


        Audit.ActionType actionType = audit.action();
        Diff diff = javers.compare(before, after);


        // 記錄審計日志
        recordAudit(after != null ? after.getClass().getSimpleName() : "Unknown",
                entityId,
                actionType.name(),
                extractActor(args, paramNames, audit),
                javers.getJsonConverter().toJson(diff));


        if (actionType != Audit.ActionType.DELETE) {
            dataStore.put(entityId, after);
        } else {
            dataStore.remove(entityId);
        }


        return result;
    }


    private String extractEntityId(Object[] args, String[] names, Audit audit) {
        if (!audit.idParam().isEmpty()) {
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(audit.idParam())) {
                    return args[i].toString();
                }
            }
        }
        return null;
    }


    private String extractActor(Object[] args, String[] names, Audit audit) {
        if (!audit.actorParam().isEmpty()) {
            for (int i = 0; i < names.length; i++) {
                if (names[i].equals(audit.actorParam())) {
                    return args[i].toString();
                }
            }
        }
        return "system";
    }


    private void recordAudit(String entity, String id, String action, String actor, String diffJson) {
        AuditLog logEntry = new AuditLog(
                String.valueOf(auditSequence.incrementAndGet()),
                entity,
                id,
                action,
                actor,
                Instant.now(),
                diffJson
        );
        auditTimeline.add(logEntry);
        log.info("審計記錄:{}", logEntry);
    }
}

業(yè)務服務 /src/main/java/com/icoderoad/service/ProductService.java

@Service
public class ProductService {


    private final Map<String, Product> products = new ConcurrentHashMap<>();


    @Audit(action = Audit.ActionType.CREATE, idParam = "id", actorParam = "actor", entityIndex = 1)
    public Product create(String id, ProductRequest request, String actor) {
        Product newProduct = new Product(id, request.name(), request.price(), request.description());
        return products.put(id, newProduct);
    }


    @Audit(action = Audit.ActionType.UPDATE, idParam = "id", actorParam = "actor", entityIndex = 1)
    public Product update(String id, ProductRequest request, String actor) {
        if (!products.containsKey(id)) throw new IllegalArgumentException("產(chǎn)品不存在: " + id);
        Product updated = new Product(id, request.name(), request.price(), request.description());
        return products.put(id, updated);
    }


    @Audit(action = Audit.ActionType.DELETE, idParam = "id", actorParam = "actor")
    public boolean delete(String id, String actor) {
        return products.remove(id) != null;
    }
}

審計日志實體 /src/main/java/com/icoderoad/audit/AuditLog.java

public record AuditLog(
        String id,
        String entityType,
        String entityId,
        String action,
        String actor,
        Instant occurredAt,
        String diffJson
) {}

Javers 配置 /src/main/java/com/icoderoad/config/JaversConfig.java

@Configuration
public class JaversConfig {


    @Bean
    public Javers javers() {
        return JaversBuilder.javers()
                .withPrettyPrint(true)
                .build();
    }
}

典型使用場景

產(chǎn)品更新操作

PUT /api/products/prod-001
X-User: 張三

請求體:

{
  "name": "iPhone 15",
  "price": 99.99,
  "description": "最新款手機"
}

生成審計日志:

{
  "entityId": "prod-001",
  "action": "UPDATE",
  "actor": "張三",
  "diffJson": "{\"changes\":[{\"field\":\"price\",\"oldValue\":100.00,\"newValue\":99.99}]}"
}

刪除操作

DELETE /api/products/prod-001
X-User: 李四

對應審計:

{
  "entityId": "prod-001",
  "action": "DELETE",
  "actor": "李四",
  "diffJson": "{\"changes\":[]}"
}

結語:讓系統(tǒng)“自己說話”的力量

通過 Javers + AOP + 注解 的結合,我們實現(xiàn)了一個零侵入、自動化、結構化的數(shù)據(jù)變更追蹤系統(tǒng)。 它讓業(yè)務代碼保持純凈,讓審計邏輯統(tǒng)一、透明、可查詢。

這套方案帶來的收益包括:

  • 開發(fā)效率提升:無需手寫日志邏輯;
  • 維護成本降低:集中管理切面邏輯;
  • 數(shù)據(jù)分析友好:結構化 JSON 格式,便于后期審計與BI接入。

在合規(guī)時代,系統(tǒng)不僅要“能跑”,更要“能解釋”。 讓你的 Spring Boot 項目從今天起,真正具備可追溯的數(shù)據(jù)生命力

責任編輯:武曉燕 來源: 路條編程
相關推薦

2025-05-30 01:00:00

RAG大模型流程

2021-05-18 14:42:55

PythonMySQL

2022-02-15 08:07:17

測試軟件開發(fā)

2022-02-20 09:56:28

TCPIP網(wǎng)絡協(xié)議

2023-12-27 07:40:43

HTTP服務器負載均衡

2023-07-31 21:56:54

哨兵系統(tǒng)redis

2015-03-23 12:33:28

2024-02-29 14:27:37

人工智能機器學習物聯(lián)網(wǎng)

2022-04-28 06:05:10

無線中繼Mesh路由器

2022-09-05 07:32:46

mock數(shù)據(jù)Stream

2024-12-19 15:00:00

數(shù)據(jù)清洗Python

2021-12-07 06:02:15

Redis Docker運維

2021-01-15 13:18:39

數(shù)據(jù)模型領域模型代碼

2020-12-22 10:02:53

ZabbixMySQL數(shù)據(jù)庫

2024-11-18 17:16:18

Python性能優(yōu)化編程

2024-11-20 16:12:31

Python圖像處理計算機視覺

2024-11-20 16:42:03

Python科學計算

2020-12-11 10:20:33

Ansible運維軟件包

2025-06-20 08:00:00

硬路由軟路由網(wǎng)絡

2019-07-23 07:30:16

點贊
收藏

51CTO技術棧公眾號

亚洲天堂电影网| 欧美精品福利视频| 向日葵污视频在线观看| 日本高清中文字幕在线| 国产一区二区剧情av在线| 欧美高清视频在线| 久久国产精品影院| 国产专区精品| 懂色av影视一区二区三区| 亚洲不卡中文字幕| av一区二区三| 日韩和欧美一区二区三区| 久久国产精品久久久久久久久久| 黄色av网址在线观看| 久久日本片精品aaaaa国产| 亚洲国产成人91porn| 亚洲精品国产一区| 五月天婷婷视频| 久久国产精品免费| 4k岛国日韩精品**专区| 日韩视频中文字幕在线观看| 岳的好大精品一区二区三区| 日韩视频一区二区在线观看| 一路向西2在线观看| 爱搞国产精品| 亚洲视频1区2区| 日韩久久久久久久| 欧美 日韩 国产 精品| 美女网站在线免费欧美精品| 668精品在线视频| 一区二区视频免费看| 国产精品亚洲人成在99www| 日韩一级片在线播放| 亚洲一级免费观看| 手机在线理论片| 亚洲电影在线播放| 亚洲小视频在线播放| 东凛在线观看| 久久久久高清精品| 久久国产精品久久| 欧美在线 | 亚洲| 国产精选一区二区三区| 欧美成人一区二区三区片免费 | 日韩av电影网| av免费在线免费观看| 中文av一区特黄| 日本在线观看一区| 免费播放片a高清在线观看| www.欧美.com| 国产青春久久久国产毛片| www.97av| 国产成人综合在线播放| 51精品国产人成在线观看| 国产乱子伦精品无码码专区| 麻豆精品国产传媒mv男同| 国产精品视频999| 最新中文字幕免费| 日本欧洲一区二区| 国产在线观看不卡| 91中文字幕在线播放| 九九久久精品视频| 91最新在线免费观看| 99久久99久久久精品棕色圆| 国产乱码精品一区二区三区五月婷| 成人精品网站在线观看| av官网在线观看| 国产69精品久久99不卡| 国产高清一区视频| 午夜视频福利在线观看| www激情久久| 欧美尤物一区| 色的视频在线免费看| 18成人在线视频| 成人短视频在线观看免费| 日韩经典av| 欧美日韩激情小视频| 日本三级免费观看| 成人黄页网站视频| 日韩免费视频一区二区| 国产精品一区二区人妻喷水| 最新亚洲精品| 日韩在线观看网址| 永久看片925tv| 99视频精品| 国产精品狼人色视频一区| 国产精品久久久久久久久毛片 | 一区二区三区四区免费| 日韩高清欧美| 欧美黑人性猛交| 精产国品一区二区| 精品制服美女丁香| 国产亚洲欧美另类一区二区三区| 九九热视频在线观看| 国产精品美女久久久久久久| 久久久久久久久久久久久国产| av福利导福航大全在线| 色婷婷久久综合| 91视频福利网| 亚洲精品一级二级三级| 久久夜精品香蕉| 综合激情网五月| 国产在线视频一区二区三区| 狠狠色噜噜狠狠狠狠色吗综合| www在线播放| 亚洲午夜精品在线| 国产wwwxx| 国产精品极品国产中出| 三级精品视频久久久久| 日韩男人的天堂| 麻豆高清免费国产一区| 久久久久久国产精品mv| 久草免费在线观看| 色老综合老女人久久久| 精品人妻二区中文字幕| 日韩在线视频精品| 日本aⅴ大伊香蕉精品视频| av免费观看网址| 国产精品蜜臀在线观看| 97成人在线观看视频| 精品视频在线播放一区二区三区| 亚洲深夜福利在线| 香蕉视频一区二区| 国产麻豆成人传媒免费观看| 日韩av一区二区三区美女毛片| 黑人精品视频| 欧美一区午夜精品| 国产一二三四视频| 久久这里有精品15一区二区三区| 国产精品对白刺激久久久| 在线日本视频| 欧美伊人久久大香线蕉综合69| 成人性生活免费看| 精品福利av| 成人av男人的天堂| 影音先锋在线播放| 日韩一区二区电影网| 黄色一级大片在线免费观看| 六月丁香婷婷久久| 亚洲高清精品中出| 美脚恋feet久草欧美| 欧美一级二级在线观看| 情侣偷拍对白清晰饥渴难耐| 麻豆精品新av中文字幕| 天天综合色天天综合色hd| 粉嫩一区二区三区| 亚洲欧美三级伦理| 一级片视频在线观看| 97se亚洲国产综合在线| 亚洲美免无码中文字幕在线| 91成人噜噜噜在线播放| 欧美高清videos高潮hd| 精品黑人一区二区三区国语馆| 亚洲日本一区二区| 国产亚洲色婷婷久久| 欧美精品观看| 国产伦精品一区二区三区| 123区在线| 日韩精品欧美激情| 青青草免费观看视频| 久久久国产综合精品女国产盗摄| 国产精品丝袜久久久久久消防器材| 欧美三级自拍| 日本欧美爱爱爱| av中文字幕一区二区三区| 欧美三级电影一区| 国产性生活大片| 国产成人久久精品77777最新版本| 在线观看三级网站| 国内毛片久久| 国产91免费看片| 色综合久久影院| 日韩一区二区三区视频| 久久综合综合久久| 久久综合久久久久88| 免费看污黄网站| 亚洲最大黄网| 国产一区二区黄色| 3d欧美精品动漫xxxx无尽| 日日狠狠久久偷偷四色综合免费 | 亚洲人成网站在线观看播放| 日韩电影免费观看高清完整版在线观看| 日韩中文字幕在线观看| 欧美 日韩 国产 精品| 欧美综合视频在线观看| 丝袜美腿小色网| 99久久精品情趣| 色噜噜狠狠永久免费| 国产精品www994| 日韩精品一区二区三区外面 | 国产黄色小视频网站| 国产精品一区二区你懂的| 凹凸国产熟女精品视频| 久久高清免费| 久久国产精品-国产精品| 国产91亚洲精品久久久| 欧美激情免费在线| √新版天堂资源在线资源| 欧美va亚洲va| 最近中文字幕免费在线观看| 亚洲国产精品久久久男人的天堂| 免费在线观看a视频| 国产成人无遮挡在线视频| 国产三区在线视频| 国产精品theporn| 亚洲欧美精品| 亚洲综合图色| 99视频免费观看| 日韩色淫视频| 91国内揄拍国内精品对白| 韩国中文字幕在线| 伊人一区二区三区久久精品| 黄色www视频| 7777精品伊人久久久大香线蕉完整版| 中文字幕亚洲精品在线| 亚洲欧美激情插| 欧美丰满老妇熟乱xxxxyyy| 91在线视频官网| 91性高潮久久久久久久| 免费在线观看不卡| 日韩精品视频一区二区在线观看| 欧美1区视频| 一本久久a久久精品vr综合| 亚洲视频分类| 精品国产乱码久久久久久丨区2区 精品国产乱码久久久久久蜜柚 | 中文字幕中文在线| 日韩激情一二三区| 成年人黄色片视频| 在线亚洲自拍| 国产精品视频网站在线观看 | 欧美视频在线观看视频| 欧美一区不卡| 影音先锋男人的网站| 99久久.com| 亚洲一卡二卡三卡| 色综合久久网| 中文字幕乱码一区二区三区| 成人免费a**址| 日韩欧美亚洲v片| 精品久久久久久久| 欧美在线3区| 国产成人av| 秋霞毛片久久久久久久久| 亚洲成a人片77777在线播放 | 欧美zozo| 亚洲欧美精品在线| 免费资源在线观看| 亚洲视频视频在线| porn亚洲| 日韩综合中文字幕| 在线免费观看的av| 欧美国产极速在线| heyzo在线播放| 午夜免费在线观看精品视频| a级大胆欧美人体大胆666| 国内精品久久久| 偷拍自拍在线看| 国产不卡av在线| 欧美亚洲福利| 91在线播放国产| 九九热hot精品视频在线播放 | 亚洲aⅴ在线观看| 日韩国产欧美精品一区二区三区| 国产精品国产高清国产| 亚洲人成在线观看| 天堂地址在线www| 久色乳综合思思在线视频| 日本aa在线| 91干在线观看| 国产激情久久| 亚洲free性xxxx护士白浆| 日韩中文字幕| 九色综合日本| 日韩久久综合| 第九区2中文字幕| 国产精品入口66mio| 国产性生交xxxxx免费| 国内精品自线一区二区三区视频| 韩国黄色一级片| 久久久久久久久久电影| 麻豆一区在线观看| 亚洲一区二区成人在线观看| 国产午夜免费福利| 91麻豆精品国产综合久久久久久 | av在线观看地址| 久久午夜精品| 国产伦精品一区二区三区妓女下载| www.av亚洲| 国产视频不卡在线| 亚洲一区自拍偷拍| 波多野结衣视频免费观看| 91精品国产欧美一区二区成人 | 久久久午夜精品理论片中文字幕| 日本视频在线免费| 亚洲午夜久久久久| 亚洲熟妇av乱码在线观看| 日韩女优制服丝袜电影| 岛国视频免费在线观看| 久久国产天堂福利天堂| 日韩电影大全网站| 99久久无色码| 久久国产电影| 丰满少妇被猛烈进入高清播放| 国内精品伊人久久久久av影院| 中国极品少妇videossexhd| 亚洲视频 欧洲视频| 亚洲av无码精品一区二区| 日韩欧美123| 午夜不卡视频| 日本91av在线播放| 国产精品对白| 波多野结衣 作品| 免费欧美在线视频| 免费看黄色aaaaaa 片| 一区二区三区久久| 一级日韩一级欧美| 亚洲天堂网站在线观看视频| 55av亚洲| 成人免费看片网址| 一区二区三区国产精华| 中文字幕天天干| 国产日韩欧美电影| 黄色在线视频网址| 亚洲国产日韩欧美在线99| 国产成人午夜| 国产一区香蕉久久| 欧美影院三区| 激情综合网俺也去| 久久久久国产精品厨房| 日本中文字幕在线| 日韩精品有码在线观看| 精品众筹模特私拍视频| 翡翠波斯猫1977年美国| 亚洲欧美色图| 日本少妇一区二区三区| 亚洲视频精选在线| 99热这里只有精品在线观看| 日韩专区在线播放| 青娱乐极品盛宴一区二区| 午夜老司机精品| 另类中文字幕网| 免费精品在线视频| 欧美高清精品3d| 黄av在线播放| 亚洲综合国产精品| 国产精品mv在线观看| 天天躁日日躁狠狠躁av| 亚洲国产sm捆绑调教视频| 欧美一区,二区| 91国内揄拍国内精品对白| 精品精品国产毛片在线看| 人人干视频在线| www.亚洲精品| 国产麻豆xxxvideo实拍| 一级精品视频在线观看宜春院| 国模私拍一区二区| 中文字幕在线看视频国产欧美在线看完整| 中文字幕一区久| 欧美激情论坛| 蜜臀久久久久久久| 日本黄色小说视频| 精品国产髙清在线看国产毛片| а√在线中文在线新版| 欧美日韩一区二| 免费成人在线观看| 欧美性x x x| 亚洲黄页视频免费观看| 日本国产欧美| 国内外成人激情免费视频| av色综合久久天堂av综合| 蜜臀精品一区二区三区| 色老头一区二区三区| 视频一区国产| 国产成人无码a区在线观看视频| 久久午夜国产精品| 国产一区二区三区视频免费观看 | 奇米影视首页 狠狠色丁香婷婷久久综合| 日韩精品亚洲一区二区三区免费| 顶级黑人搡bbw搡bbbb搡| 亚洲精品在线三区| 国产精品无码久久久久| www.日本在线视频| 久久精品欧美一区二区三区麻豆| 亚洲天堂手机在线| 国内久久久精品| 日韩中文在线电影| 屁屁影院国产第一页| 欧美日韩国产大片| 女海盗2成人h版中文字幕| 伊人狠狠色丁香综合尤物| 成人免费看的视频| 一级片在线观看视频| 欧美一区在线直播| 欧美激情偷拍| 久久久久久久毛片| 亚洲成人三级在线| 日韩午夜视频在线| 国产极品在线视频| 亚洲欧洲国产专区| 黄色片在线播放| 激情视频在线观看一区二区三区|