package com.ruoyi.system.domain.fieldchangerecord.annotation;

import com.ruoyi.system.domain.changerecord.enums.OperationType;
import com.ruoyi.system.domain.changerecord.enums.SubjectType;
import com.ruoyi.system.domain.fieldchangerecord.enums.FieldCode;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 字段级变更日志注解。
 *
 * <p>标注在 Mapper 方法上，用于声明该写操作需要对指定 {@link #fields()} 做前后值 diff，
 * 并将真变更写入 {@code data_field_change_record} 表。和主体级 {@code @DataChangeLog}
 * 并行存在，互不影响。</p>
 *
 * <h3>idExpr 表达式语法</h3>
 * <p>与 {@code @DataChangeLog} 完全一致：</p>
 * <ul>
 *     <li>{@code "fieldName"} — 从参数按属性名取值；</li>
 *     <li>{@code "list[*].fieldName"} — 批量操作场景；</li>
 *     <li>{@code "@preload[*].fieldName"} — 从 {@link #preLoadMapper()} 查询结果中取值。</li>
 * </ul>
 *
 * <h3>preLoadMapper</h3>
 * <p><strong>UPDATE 场景必需</strong>：diff 需要知道旧值，拦截器会在执行业务 SQL 前
 * 先调用 preLoadMapper 查出旧记录。INSERT 场景 preLoadMapper 留空即可。</p>
 *
 * <h3>Partial update 语义</h3>
 * <p>UPDATE 时，如果 new value（从参数里取）为 {@code null}，拦截器会跳过该字段 diff
 * 不产生日志（视为"未修改"），兼容 MyBatis {@code <set>} 标签只拼非 null 字段的写法。</p>
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(DataFieldChangeLogs.class)
public @interface DataFieldChangeLog {

    /** 主体类型。必须和 {@link #fields()} 里每个 FieldCode 的 subjectType 一致。 */
    SubjectType subjectType();

    /** 操作类型。仅支持 INSERT 和 UPDATE；DELETE 不进本表。 */
    OperationType operation();

    /** subject_id 取值表达式。 */
    String idExpr();

    /** 本次需要 diff 的字段清单。 */
    FieldCode[] fields();

    /**
     * UPDATE 场景下，用于捞旧记录的查询 Mapper 方法全限定名。
     * INSERT 场景留空。
     *
     * <p>预加载的返回值应该是与主体同构的对象列表（每个元素是 HouseResource /
     * BusinessEntityInfo / BusinessEntitySell 等），拦截器会按 id 匹配找到对应旧值。</p>
     */
    String preLoadMapper() default "";
}
