精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

88国产精品欧美一区二区三区| 欧美撒尿777hd撒尿| 久久国产精品99久久久久久丝袜| 丰满少妇乱子伦精品看片| 国产精品日韩精品中文字幕| 欧美剧情电影在线观看完整版免费励志电影 | 青青草手机视频在线观看| 红杏视频成人| 欧美日韩国产精选| 国产午夜福利100集发布| h视频在线免费| 粉嫩一区二区三区在线看| 国产999精品久久久| 玖玖爱这里只有精品| 免费看成人哺乳视频网站| 欧美日韩国产在线观看| 久久黄色片视频| а天堂中文在线官网| 久久久久亚洲蜜桃| 成人18视频| 中文字幕有码视频| 在线亚洲欧美| 久久久久久成人精品| 久久精品三级视频| 日韩成人一级| 欧美videofree性高清杂交| 免费涩涩18网站入口| 国产中文在线播放| 夜色激情一区二区| 正在播放91九色| 成全电影播放在线观看国语| 97精品久久久午夜一区二区三区| 亚洲一区二区在线播放| 亚洲一线在线观看| 久久最新视频| 清纯唯美亚洲综合| 99热只有这里有精品| 国内精品亚洲| 视频直播国产精品| 中文字幕黄色网址| 国产真实有声精品录音| 日韩第一页在线| 国产精品手机在线观看| 涩涩屋成人免费视频软件| 最新日韩欧美| 亚洲永久精品国产| 美女黄色片网站| 日本网站在线免费观看视频| 久久精品亚洲乱码伦伦中文| 久久久com| 亚洲av毛片成人精品| 成人精品鲁一区一区二区| 99视频在线播放| 亚洲第一成人av| 国产99久久精品| 国产高清精品一区二区| wwwav在线播放| 国产成人高清视频| 国产精品国产精品国产专区不卡| 国产小视频一区| 99久久久免费精品国产一区二区| 激情伦成人综合小说| 五月婷婷六月丁香综合| 97成人超碰视| 日本一区免费观看| 成人动漫在线播放| 亚洲欧美日韩在线不卡| 成人污网站在线观看| 欧美videossex| 五月婷婷色综合| 日本在线观看a| 欧美在线va视频| 欧美老女人第四色| 国内av免费观看| 懂色av一区二区| 亚洲欧美日韩另类| 男人的午夜天堂| 国产一区久久| 欧美一级淫片videoshd| 中文字幕欧美人妻精品一区蜜臀| 激情偷乱视频一区二区三区| 91黄色国产视频| 欧美亚洲精品在线观看| 国产校园另类小说区| 宅男一区二区三区| 男人天堂视频在线观看| 欧美熟乱第一页| 无码人妻一区二区三区精品视频 | 88在线观看91蜜桃国自产| 色哟哟在线观看视频| 玖玖玖免费嫩草在线影院一区| 亚洲美女性生活视频| 精品国产大片大片大片| 亚洲精品1区| 国产精品欧美激情| 亚洲免费不卡视频| 久久久国际精品| 无码人妻精品一区二区三区99v| 2021中文字幕在线| 精品视频1区2区| 国产性猛交96| 成人激情开心网| 久久噜噜噜精品国产亚洲综合| 免费的毛片视频| 国产黑丝在线一区二区三区| 欧美中日韩免费视频| 亚洲第一图区| 欧美日韩国产一级| 人妻少妇精品视频一区二区三区| 久久精品国产www456c0m| 97视频免费在线看| 国产日韩精品suv| 久久日韩精品一区二区五区| 蜜桃网站在线观看| 97精品国产综合久久久动漫日韩| 精品久久国产老人久久综合| 国产探花在线视频| 蜜桃久久av| 国产精品久久久久久久天堂第1集 国产精品久久久久久久免费大片 国产精品久久久久久久久婷婷 | 成人av在线天堂| 男同在线观看| 亚洲不卡一区二区三区| 日韩成人av免费| 精品国产一区二区三区噜噜噜 | 久久成人av网站| 波多野结衣在线观看视频| 北条麻妃国产九九精品视频| 大桥未久一区二区三区| 久久亚洲精品人成综合网| 国产视频亚洲精品| 成人午夜视频精品一区| 福利一区二区在线观看| 99中文字幕在线观看| 日日夜夜亚洲精品| 中文字幕日韩在线播放| 黄色av网站免费观看| 91小视频免费看| 欧美二区在线视频| 久久精品亚洲成在人线av网址| 九九九热精品免费视频观看网站| 91av久久久| 国产精品麻豆网站| 久热精品在线播放| 人人狠狠综合久久亚洲婷| 国产成人福利视频| 黄色片视频在线观看| 色欧美片视频在线观看| 国产 欧美 在线| 日本亚洲免费观看| 亚洲激情一区二区| 香蕉久久一区| 久久久999国产| av中文字幕观看| 亚洲精品第一国产综合野| 日韩精品xxx| 亚洲午夜伦理| 农村寡妇一区二区三区| 丝袜美腿一区| 久久精品成人动漫| av免费在线观看不卡| 一区二区三区在线观看动漫| 亚洲国产精品狼友在线观看| 制服诱惑一区二区| 日韩欧美在线电影| 人人玩人人添人人澡欧美| 久久视频在线看| 亚洲av无码乱码国产精品| 亚洲电影第三页| 免费a级黄色片| 男男视频亚洲欧美| 最近中文字幕免费mv| 亚洲国产中文在线| 91精品国产色综合| 国产精品视频一区二区久久| 在线播放中文一区| 日本少妇做爰全过程毛片| 99久久影视| 2014亚洲片线观看视频免费| 亚洲欧美日韩图片| 你懂的国产在线| 国产亚洲综合性久久久影院| 国内自拍视频网| 天天射成人网| 国产精品免费一区二区三区在线观看| √8天堂资源地址中文在线| 日韩的一区二区| 一区二区小视频| 一二三四社区欧美黄| av无码一区二区三区| 老鸭窝毛片一区二区三区| 制服诱惑一区| 欧美理论电影在线精品| 国产精品美女www爽爽爽视频| 成人黄色网址| 精品调教chinesegay| 911美女片黄在线观看游戏| 亚洲国产一区二区在线播放| 韩国三级hd中文字幕| 国产福利一区二区三区在线视频| 妺妺窝人体色www在线小说| 欧美hentaied在线观看| 国产精品有限公司| 欧产日产国产v| 欧美日韩国产综合网| 久久久久久久久久码影片| 四虎国产精品成人免费影视| 97在线视频免费播放| 夜级特黄日本大片_在线 | 国产aⅴ爽av久久久久成人| 午夜影院久久久| 日韩在线一卡二卡| 久久综合国产精品| www.四虎精品| 精品制服美女丁香| 熟女少妇精品一区二区| 一区在线播放| 私拍精品福利视频在线一区| 国产午夜精品全部视频在线播放| 国产色综合视频| 色狠狠综合天天综合综合| 国产精品 欧美激情| 欧美国产欧美综合| 亚洲精品成人无码熟妇在线| 丁香天五香天堂综合| 97国产成人无码精品久久久| 久久精品99国产国产精| 免费观看美女裸体网站| 一区二区三区在线| 亚洲电影免费| 国产一区二区三区四区五区传媒 | 久激情内射婷内射蜜桃| 伊人情人综合网| 欧美特级限制片免费在线观看| 久久人妻无码aⅴ毛片a片app| 99精品视频播放| jizzjizz亚洲| 色悠悠久久久久| 国产精品一区二区婷婷| 亚洲欧美日韩国产精品| 日韩精品一二| 日韩av影视综合网| 日本精品999| 亚洲精品一线二线三线无人区| 99精品人妻无码专区在线视频区| 欧美片网站yy| 一区二区三区日| 欧美夫妻性生活| 国产视频手机在线观看| 91精品国产91久久综合桃花| 国产免费不卡av| 欧美一区二区私人影院日本| 一区二区三区www污污污网站| 欧美日韩在线播放三区| 中文字幕一区二区久久人妻| 欧美日韩国产另类不卡| 国产毛片毛片毛片毛片| 欧美电影免费提供在线观看| www香蕉视频| 亚洲成人精品视频在线观看| а√中文在线资源库| 亚洲精品在线观看视频| 亚洲人午夜射精精品日韩| 亚洲欧美国产一区二区三区| 国产高清视频在线播放| 日韩中文在线观看| 影音先锋中文在线视频| 高清欧美性猛交xxxx黑人猛交| www.九色在线| 国产精品96久久久久久| 日韩午夜视频在线| 国产精品大全| 国产欧美一区二区三区精品观看 | 亚洲精品成人| 妞干网在线观看视频| 久久精品日产第一区二区| jizz欧美性11| 豆国产96在线|亚洲| 草草地址线路①屁屁影院成人| 国产精品午夜电影| 免费在线观看av网址| 一本色道亚洲精品aⅴ| 亚洲一区二区三区高清视频| 日韩一级黄色片| 免费在线超碰| 欧美成在线视频| 国产精品一区二区av影院萌芽| 91精品免费视频| 亚洲电影一级片| 国产又粗又硬又长| 国产一区二区三区的电影 | 亚洲 欧美 视频| 欧美久久一区二区| 污视频软件在线观看| 日韩在线免费视频| 操人在线观看| 成人日韩av在线| 日本中文字幕在线一区| 异国色恋浪漫潭| 米奇777在线欧美播放| 国产又粗又猛又爽又黄| 国产欧美日本一区二区三区| 久久久一二三区| 欧美精品日日鲁夜夜添| 青青操视频在线| 久久久久国产精品一区| 亚洲福利影视| 日韩中文一区二区三区| 亚洲精品欧洲| 亚洲一级片免费观看| 亚洲国产精品传媒在线观看| 日本一区二区不卡在线| 91精品国产aⅴ一区二区| 成年人视频在线看| 日本最新高清不卡中文字幕| 岛国精品一区| 经典三级在线视频| 麻豆一区二区三区| 自拍偷拍中文字幕| 精品国产1区2区| 亚洲精品成av人片天堂无码| www.精品av.com| 写真福利精品福利在线观看| 精品一区在线播放| 亚洲乱码久久| 亚洲免费观看在线| 亚洲精品免费在线播放| 国产一区二区三区三州| 国产亚洲欧美日韩一区二区| 蜜桃视频m3u8在线观看| 国产精品日韩一区二区免费视频| 伊人青青综合网| 性鲍视频在线观看| 亚洲欧洲色图综合| 中文字幕男人天堂| 一个色综合导航| 日韩在线免费| 日本一区二区三区免费看| 性高湖久久久久久久久| 最近中文字幕无免费| 午夜激情一区二区三区| 好吊色在线观看| 久久久久久网站| 操欧美女人视频| 国产精品一色哟哟| 成年人国产精品| 全部毛片永久免费看| 亚洲精品动漫久久久久| 亚洲v.com| 欧洲亚洲一区二区三区四区五区| 日韩精品电影一区亚洲| 亚洲av成人无码久久精品 | 91福利视频网| 色爱综合av| 大香煮伊手机一区| 国产精品麻豆欧美日韩ww| 一级片视频播放| 九九久久国产精品| 免费日韩一区二区三区| 少妇性饥渴无码a区免费| 国产亚洲欧美色| 91久久久久国产一区二区| 欧美成人午夜免费视在线看片| www.亚洲一二| 国产综合免费视频| 国产精品久久久久桃色tv| 国产欧美日韩成人| 国内精品小视频| 国模精品一区| 日韩av自拍偷拍| 亚洲6080在线| 国产乱视频在线观看| 成人免费看黄网站| 亚洲国产高清一区二区三区| 男女黄床上色视频| 欧美日韩高清不卡| 久久不射影院| 亚洲精品8mav| 粉嫩高潮美女一区二区三区| 欧美日韩一级黄色片| 久久国产精品免费视频| 人人精品视频| 中文字幕线观看| 欧美视频裸体精品| 亚洲按摩av| 水蜜桃亚洲精品| 北条麻妃一区二区三区| 91青青草视频| 久久99亚洲精品| 波多野结衣在线观看一区二区| 杨幂一区二区国产精品| 欧美日韩国产综合新一区| 求av网址在线观看| 精品一区在线播放| 国产一区三区三区| 天堂网中文字幕| 欧美激情在线狂野欧美精品| 不卡中文字幕| 色天使在线视频| 欧美一区二区成人| 免费在线观看一区|