# AdRequest.java

    package cloud.wconnect.frontoffice.entity.adjustment;

    import cloud.wconnect.commonlib.cons.BaseConstant;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    import lombok.EqualsAndHashCode;
    import lombok.ToString;
    import lombok.experimental.Accessors;
    import org.apache.commons.codec.digest.DigestUtils;
    import org.apache.commons.lang3.StringUtils;

    import java.io.Serializable;
    import java.math.BigDecimal;
    import java.math.RoundingMode;
    import java.util.Arrays;
    import java.util.Objects;
    import java.util.stream.Collectors;

    /**
     * <p>
     *
     * </p>
     *
     * @author lichen
     * @since 2023-03-30
     */
    @Data
    @EqualsAndHashCode(callSuper = false)
    @Accessors(chain = true)
    @TableName("ad_request_v2")
    @ToString
    public class AdRequest implements Serializable {

        private static final long serialVersionUID = 1L;

        /**
         * 审批单
         */
        @TableId(value = "id", type = IdType.AUTO)
        private Long id;

        /**
         * 资产id
         */
        private Integer propertyId;

        /**
         * 创建人
         */
        private Integer createBy;

        /**
         * 创建时间
         */
        private Long createTime;

        /**
         * 是否自动创建(0:否,1:是)
         */
        private Boolean autoCreated;

        /**
         * Conf. Num.
         */
        private String confNumber;

        /**
         * Adj. Num.
         */
        private String adjNumber;

        /**
         * 类型(1:Sales 2:Front Desk 3:Accounting 4:Misposting 5:chargeback)
         */
        private Integer requestType;

        /**
         * 责任选项(0:无,1:N/A,2:Employee`s Name)
         */
        private Integer mistakeType;

        /**
         * 责任描述/员工姓名（根据责任选项决定）
         */
        private String mistakeBy;

        /**
         * 来宾姓名或公司名
         */
        private String guestName;

        /**
         * 入住日期 (年月日)
         */
        private Long checkInDate;

        /**
         * 退房日期 (年月日)
         */
        private Long checkOutDate;

        /**
         * 房间号
         */
        private String roomNumber;

        /**
         * 需调整金额
         */
        private BigDecimal adjustedDollar;

        /**
         * 交易描述
         */
        private String trxDesc;

        /**
         * 选择原因(1:Incorrect Company rate,2:Guest Relation/Complaint,3:Over payment,4:CHANGE Credit Card OR other Method of Payment,5:Incorrect Posting Correction will be posted,6:Other,7:Chargeback,8:DB Write Off,9:Misposting类型使用,10:Fraud)
         */
        private Integer choosedReason;

        /**
         * 原因详情 {company name：公司名，rate code：rate code，correct rate：房价，explain：解释，specify：指明}
         */
        private String choosedReasonDetails;

        /**
         * 备注
         */
        private String comment;

        /**
         * 状态 0: 无效 1:有效 2:待定 3:作废
         */
        private Integer status;

        /**
         * 附件
         */
        private String attachments;

        /**
         * 关联主adj的id
         */
        private Long cbAdjId;

        /**
         * Case编号
         */
        private String caseNumber;

        /**
         * Revenue lost (yes / no)
         */
        private Boolean revenueLost;

        /**
         * 流程状态 1:draft-草稿 2:审批中 3:审批结束
         */
        private Integer flowStatus;

        /**
         * 当前审批层级索引
         */
        private Integer approveIndex;

        /**
         * 当前审批层级ID
         */
        private Long approveLevelId;

        /**
         * 当前审批批次id
         */
        private Long batchId;

        /**
         * 责任人
         */
        private Integer dutyUser;

        /**
         * 更新人
         */
        private Integer updateBy;

        /**
         * 更新时间
         */
        private Long updateTime;

        /**
         * PMS 系统账户
         */
        private String pmsAccount;

        /**
         * 是否归档
         */
        private Boolean isArchive;

        /**
         * 交易代码
         */
        private String trxCode;

        /**
         * 业务日期，格式yyyyMMdd。调账数据与PMS报告配对成功后，数据在PMS报告中的业务日期
         */
        private Integer businessDate;

        /**
         * 配对时间戳
         */
        private Long matchTime;

        /**
         * 账户编号
         */
        private String accountNumber;

        /**
         * 配对方式1：使用guestName、roomNumber、trxCode、adjustedDollar配对 2：使用trxCode、adjustedDollar、confNumber配对
         */
        private Integer matchType;

        /**
         * 是否作废
         */
        private Boolean isCancel;

        /**
         * 关联case的时间戳
         */
        private Long caseRelTime;

        /**
         * 使用guestName、roomNumber、trxCode、adjustedDollar配对
         */
        public static final int MATCH_BY_GUEST_NAME_ROOM_NUMBER_TRX_CODE_ADJUSTED_AMOUNT = 1;
        /**
         * 使用trxCode、adjustedDollar、confNumber配对
         */
        public static final int MATCH_BY_TRX_CODE_ADJUSTED_AMOUNT_CONF_NUMBER = 2;

        /**
         * 根据matchType生成签名字段
         *
         * @param matchType 匹配方式
         * @return java.lang.String
         * @author lichen
         * @date 2026/1/9
         */
        public String signByMatchType(int matchType) {
            String newRoomNumber = Objects.isNull(this.getRoomNumber()) ? "" : this.getRoomNumber();

            // 客人名称标准化，去除姓名中的前后空格、忽略大小写、忽略姓名的顺序
            String newGuestName = Objects.isNull(this.getGuestName()) ? "" : formatGuestName(this.getGuestName());

            String newTrxCode = Objects.isNull(this.getTrxCode()) ? "" : this.getTrxCode();

            // 金额字段标准化
            BigDecimal newAdjustedDollar = this.getAdjustedDollar().setScale(BaseConstant.DATA_PRECISION, RoundingMode.HALF_UP);

            String newConfNumber = Objects.isNull(this.getConfNumber()) ? "" : this.getConfNumber();
            if (matchType == MATCH_BY_GUEST_NAME_ROOM_NUMBER_TRX_CODE_ADJUSTED_AMOUNT) {
                return DigestUtils.sha256Hex(StringUtils.join(newRoomNumber, newGuestName, newTrxCode, newAdjustedDollar));
            } else {
                return DigestUtils.sha256Hex(StringUtils.join(newTrxCode, newAdjustedDollar, newConfNumber));
            }
        }

        /**
         * 使用GuestName、RoomNumber、TrxCode、AdjustedDollar、ConfNumber签名
         *
         * @return java.lang.String
         * @author lichen
         * @date 2026/2/5
         */
        public String signByCancel() {
            String newRoomNumber = Objects.isNull(this.getRoomNumber()) ? "" : this.getRoomNumber();

            // 客人名称标准化，去除姓名中的前后空格、忽略大小写、忽略姓名的顺序
            String newGuestName = Objects.isNull(this.getGuestName()) ? "" : formatGuestName(this.getGuestName());

            String newTrxCode = Objects.isNull(this.getTrxCode()) ? "" : this.getTrxCode();

            // 金额字段标准化
            BigDecimal newAdjustedDollar = this.getAdjustedDollar().setScale(BaseConstant.DATA_PRECISION, RoundingMode.HALF_UP);

            String newConfNumber = Objects.isNull(this.getConfNumber()) ? "" : this.getConfNumber();

            return DigestUtils.sha256Hex(StringUtils.join(this.pmsAccount, newRoomNumber, newGuestName, newTrxCode, newAdjustedDollar, newConfNumber));
        }

        /**
         * 格式化客人姓名
         * 1. 去除姓、名两端的空格
         * 2. 规范大小写
         * 3. 姓、名按顺序排列
         *
         * @param guestName 客人姓名
         * @return java.lang.String
         * @author lichen
         * @date 2026/1/16
         */
        public static String formatGuestName(String guestName) {
            if (StringUtils.isBlank(guestName)) return "";
            return Arrays.stream(guestName.toLowerCase().split(","))
                    .map(String::trim)
                    .sorted()
                    .collect(Collectors.joining(","));
        }

    }