V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
taloricag
V2EX  ›  程序员

操作记录精细到字段级别,有比较干净的方案么?

  •  
  •   taloricag · 2021-11-27 02:19:44 +08:00 · 2946 次点击
    这是一个创建于 1120 天前的主题,其中的信息可能已经有所发展或是发生改变。
    就是 post 请求,需要知道修改到数据库改了什么字段做操作记录
    实际上现在操作记录的实现已经是 AOP ,在路由层丢给一个单独的控制层去解决操作记录的问题
    但是在路由层不可能记录到字段级别,毕竟也不可能在这个地方去查数据
    所以有没啥干净一点的方案,实在是不想在业务逻辑的 controller 里再去逐个字段做对比...

    或许最理想的方案是在 ORM 去做,但是目前对 ORM 的控制力实在有点弱...(go / gorm
    12 条回复    2021-11-29 17:00:00 +08:00
    wd
        1
    wd  
       2021-11-27 06:45:02 +08:00 via iPhone
    数据库 trigger
    totoro52
        2
    totoro52  
       2021-11-27 08:01:12 +08:00 via iPhone   ❤️ 1
    我的做法是 binlog ,用 canal 解析 binlog 在写入日志数据库,同步到 es , 完全和业务层解偶,缺点是无法记录用户的 ip 等,不过业务日志只要记录业务行为就行了,剩下的交给系统日志
    taloricag
        3
    taloricag  
    OP
       2021-11-27 09:35:16 +08:00
    @totoro52 谢谢大佬,但是场景不太符合我,因为我的场景不是完全跟业务解耦,还有条件触发,比如修改了 A 字段需要发个邮件,修改了 B 字段发个通知这种,给我整蒙了
    rrfeng
        4
    rrfeng  
       2021-11-27 09:54:20 +08:00 via Android
    gorm 有 hooks 啊
    cs419
        5
    cs419  
       2021-11-27 09:58:52 +08:00
    client 驱动层
    修改 github.com/go-sql-driver/mysql 进行 aop
    fgwmlhdkkkw
        6
    fgwmlhdkkkw  
       2021-11-27 10:12:30 +08:00 via Android
    只记请求和 sql ,然后再用 sql 解析器,取出来字段。
    totoro52
        7
    totoro52  
       2021-11-27 11:24:58 +08:00   ❤️ 1
    @taloricag 一样可以 只要你的表设计有更新人这类似的字段,可以取出来在回表去查通知,如果你的表设计没有更新人和创建人那就不行了
    totoro52
        8
    totoro52  
       2021-11-27 11:27:08 +08:00
    @taloricag canal 本身只是负责解析记录,剩下的逻辑还是在自己手上
    Fule
        9
    Fule  
       2021-11-29 09:58:47 +08:00
    SQL Server 2016+ 有 Temporal Table ,自动维护整行历史记录,一定程度上有助于这类问题,不知道其它数据库是否有类似特性:
    https://docs.microsoft.com/en-us/sql/relational-databases/tables/temporal-tables?view=sql-server-2016
    shigella
        10
    shigella  
       2021-11-29 10:00:03 +08:00
    canal
    cnit
        11
    cnit  
       2021-11-29 16:28:51 +08:00
    ``` java


    @Component
    @CanalTable("table_name")
    public class OrderHeaderHandler implements EntryHandler {

    @Autowired
    private XxxService xxxService;

    @Override
    public void update(Map<String, String> before, Map<String, String> after, Set<String> updateColumns) {
    //获取需要记录日志的列
    Map<String, String> filedMap = FieldUtils.objectToMap(TableName.class, updateColumns);
    String tableId= before.get("tableId");
    String records = "";
    if (filedMap != null && filedMap.keySet().size() > 0) {
    for (String update : filedMap.keySet()) {
    String comment = filedMap.get(update);
    String beforeValue = before.get(update);
    String afterValue = after.get(update);
    records = records.concat(String.format("%s 由 %s 改为 %s", comment, StringUtils.isEmpty(beforeValue) ? "空" : beforeValue
    , StringUtils.isEmpty(afterValue) ? "空" : afterValue) + ";");
    }
    //记录日志
    OperationRecords operationRecords = new OperationRecords();
    operationRecords.setLinkedId(Long.parseLong(orderId));
    operationRecords.setRecords(records);
    operationRecords.setTableName("container_transport_order_header");
    operationRecords.setCreateTime(new Date());
    operationRecords.setCreateUserId(parseLongUserId(after.get("updateUserId")));
    operationRecordsService.save(operationRecords);
    }
    }



    ```
    onhao
        12
    onhao  
       2021-11-29 17:00:00 +08:00
    @taloricag 我的方案,可能应用场景有限 用触发器
    https://wuhao.pw/archives/268/

    免去了在应用端敲代码了,仅供参考。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   935 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 21:56 · PVG 05:56 · LAX 13:56 · JFK 16:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.