首页 > 数据库 >Redis + CAS,配置自增业务单号

Redis + CAS,配置自增业务单号

时间:2023-03-08 16:13:45浏览次数:33  
标签:自增 get CAS List Redis null config dataMap append

DB 脚本

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for log_czrz
-- ----------------------------
CREATE TABLE `log_czrz`
(
    `id`             int(11)                                                  NOT NULL AUTO_INCREMENT COMMENT '主键',
    `SQBM`           varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT NULL COMMENT '社区编码',
    `table_name`     varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT NULL COMMENT '表名',
    `table_id`       varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT NULL COMMENT '表主键',
    `table_column`   varchar(1000) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL COMMENT '医保目录编码',
    `xgysbm`         varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT NULL COMMENT '修改医生编码',
    `xgysmc`         varchar(32) CHARACTER SET utf8 COLLATE utf8_bin          NULL     DEFAULT NULL COMMENT '更改医生名称',
    `xgxx`           longtext CHARACTER SET utf8 COLLATE utf8_general_ci      NULL COMMENT '修改信息',
    `xgrq`           int(11)                                                  NULL     DEFAULT NULL COMMENT '修改日期',
    `xgsj`           int(11)                                                  NULL     DEFAULT NULL COMMENT '修改时间',
    `machine_ip`     varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT NULL COMMENT 'ip',
    `machine_name`   varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci   NULL     DEFAULT NULL COMMENT 'ip名称',
    `System_version` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL     DEFAULT NULL COMMENT '电脑系统版本',
    `created_at`     timestamp                                                NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `updated_at`     timestamp                                                NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB
    -- AUTO_INCREMENT = 5135
  CHARACTER SET = utf8
  COLLATE = utf8_general_ci COMMENT = '药房库存修改药品有效期日志表'
  ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;

-- ----------------------------
-- Table structure for ywdh_config
-- ----------------------------
CREATE TABLE `ywdh_config`
(
    `PKYWDHCONFIG` int(11)                                                NOT NULL AUTO_INCREMENT COMMENT '业务单号设置自增主键',
    `SQBM`         varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '社区编码',
    `LX`           int(2)                                                 NULL     DEFAULT NULL COMMENT '单号类型(1药库采购、4药库验收、2药库入库、3药库出库...)',
    `KSLX`         int(5)                                                 NULL     DEFAULT NULL COMMENT '科室类型(对应zd_ks科室的主键id)',
    `JTLX`         int(5)                                                 NULL     DEFAULT NULL COMMENT '具体类型(将单号类型进一步细分的具体类型)',
    `CZRYID`       int(11)                                                NULL     DEFAULT NULL COMMENT '操作人员id',
    `DHQZ`         varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL COMMENT '单号前缀',
    `DHCD`         int(5)                                                 NULL     DEFAULT NULL COMMENT '单号长度(不含前缀)',
    `DHQSH`        int(5)                                                 NULL     DEFAULT NULL COMMENT '单号起始号',
    `DHBC`         int(5)                                                 NULL     DEFAULT NULL COMMENT '单号步长',
    `ISOPEN`       int(2)                                                 NULL     DEFAULT 1 COMMENT '是否开启(1开启为递增序列,0不开启为时间戳随机序列)',
    `CURRENTTIME`  varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL     DEFAULT NULL COMMENT '时间戳',
    `CREATEDAT`    timestamp                                              NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建日期',
    `UPDATEDAT`    timestamp                                              NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新日期',
    PRIMARY KEY (`PKYWDHCONFIG`) USING BTREE
) ENGINE = InnoDB
    -- AUTO_INCREMENT = 180
  CHARACTER SET = utf8
  COLLATE = utf8_general_ci COMMENT = '业务单号生成规则配置'
  ROW_FORMAT = Compact;

-- ----------------------------
-- Table structure for ywdh_redis_pair
-- ----------------------------
CREATE TABLE `ywdh_redis_pair`
(
    `PKPAIR`      int(11)                                                 NOT NULL AUTO_INCREMENT COMMENT '键值对主键pk',
    `SQBM`        varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci  NOT NULL COMMENT '社区编码',
    `CURRENTTIME` varchar(15) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '时间戳',
    `KEYNAME`     varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '键',
    `VALUE`       varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci  NULL DEFAULT NULL COMMENT '值',
    PRIMARY KEY (`PKPAIR`) USING BTREE
) ENGINE = InnoDB
    -- AUTO_INCREMENT = 214
  CHARACTER SET = utf8
  COLLATE = utf8_general_ci COMMENT = '业务单号键值对'
  ROW_FORMAT = Compact;

SET FOREIGN_KEY_CHECKS = 1;


Entity

YwdhConfig(业务单号配置)

import lombok.Data;

import javax.persistence.*;
import java.util.Date;

/**
 * @Author: DengJia
 * @Date: 2022/2/14 10:44
 * @Description: 业务单号配置
 */

@Data
public class YwdhConfig {
    /**
     * 药库单号设置自增主键
     */
    private Integer pkywdhconfig;
    /**
     * 社区编码
     */
    private String sqbm;
    /**
     * 单号类型(1药库采购、2药库入库、3药库出库...)
     */
    private Integer lx;
    /**
     * 科室类型(对应zd_ks科室的主键id)
     */
    private Integer kslx;
    /**
     * 具体类型(将单号类型进一步细分的具体类型)
     */
    private Integer jtlx;
    /**
     * 操作人员id
     */
    private Integer czryid;
    /**
     * 单号前缀
     */
    private String dhqz;
    /**
     * 单号长度(不含前缀)
     */
    private Integer dhcd;
    /**
     * 单号起始号
     */
    private Integer dhqsh;
    /**
     * 单号步长
     */
    private Integer dhbc;
    /**
     * 是否开启(1开启为递增序列,0不开启为时间戳随机序列)
     */
    private Integer isopen;
    /**
     * 时间戳
     */
    private String currenttime;

    private Date createdAt;
    private Date updatedAt;
}

YwdhRedisPair(Redis键值对)

import lombok.Data;

import javax.persistence.*;

/**
 * @Author: DengJia
 * @Date: 2022/2/17 9:25
 * @Description: redis键值对
 */

@Data
public class YwdhRedisPair {
    private Integer pkpair;
    private String currenttime;
    private String sqbm;
    private String keyname;
    private String value;

    public YwdhRedisPair() {
    }

    public YwdhRedisPair(String sqbm, String currenttime) {
        this.currenttime = currenttime;
        this.sqbm = sqbm;
    }

    public YwdhRedisPair(Integer pkpair, String currenttime, String sqbm, String keyname, String value) {
        this.pkpair = pkpair;
        this.currenttime = currenttime;
        this.sqbm = sqbm;
        this.keyname = keyname;
        this.value = value;
    }
}

Mapping

YwdhConfig.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.yuanbo.chss.domain.business.YwdhConfig" table="ywdh_config">
        <id name="pkywdhconfig" type="java.lang.Integer">
            <column name="PKYWDHCONFIG" precision="11" scale="0"/>
            <generator class="identity"/>
        </id>
        <property name="sqbm">
            <column name="SQBM" sql-type="varchar(20)" length="20"/>
        </property>
        <property name="lx" type="java.lang.Integer">
            <column name="LX" precision="2" scale="0"/>
        </property>
        <property name="kslx" type="java.lang.Integer">
            <column name="KSLX" precision="5" scale="0"/>
        </property>
        <property name="jtlx" type="java.lang.Integer">
            <column name="JTLX" precision="5" scale="0"/>
        </property>
        <property name="czryid" type="java.lang.Integer">
            <column name="CZRYID" precision="11" scale="0"/>
        </property>
        <property name="dhqz" type="java.lang.String">
            <column name="DHQZ" sql-type="varchar(10)" length="10"/>
        </property>
        <property name="dhcd" type="java.lang.Integer">
            <column name="DHCD" precision="5" scale="0"/>
        </property>
        <property name="dhqsh" type="java.lang.Integer">
            <column name="DHQSH" precision="5" scale="0"/>
        </property>
        <property name="dhbc" type="java.lang.Integer">
            <column name="DHBC" precision="2" scale="0"/>
        </property>
        <property name="isopen" type="java.lang.Integer">
            <column name="ISOPEN" precision="2" scale="0"/>
        </property>
        <property name="currenttime" type="java.lang.String">
            <column name="CURRENTTIME" sql-type="varchar(50)" length="50"/>
        </property>
    </class>
</hibernate-mapping>

YwdhRedisPair.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>
    <class name="com.yuanbo.chss.domain.business.YwdhRedisPair" table="ywdh_redis_pair">
        <id name="pkpair" type="java.lang.Integer">
            <column name="PKPAIR" precision="11" scale="0"/>
            <generator class="identity"/>
        </id>
        <property name="sqbm">
            <column name="SQBM" sql-type="varchar(20)" length="20"/>
        </property>
        <property name="currenttime" type="java.lang.String">
            <column name="CURRENTTIME" sql-type="varchar(15)" length="15"/>
        </property>
        <property name="keyname" type="java.lang.String">
            <column name="KEYNAME" sql-type="varchar(100)" length="100"/>
        </property>
        <property name="value" type="java.lang.String">
            <column name="VALUE" sql-type="varchar(20)" length="20"/>
        </property>
    </class>
</hibernate-mapping>

Enum

YwdhOpsEnum(业务单号操作,枚举类)

import com.yuanbo.common.util.G3GlobalNames;

import java.util.Objects;
import java.util.stream.Stream;

/**
 * @Author: DengJia
 * @Date: 2022/2/10 11:31
 * @Description: 业务单号操作,枚举类
 */

public enum YwdhOpsEnum {
    // 药库采购
    PURCHASE(G3GlobalNames.YK_CGD0, 1),
    // 药库验收
    ACCEPTANCE(G3GlobalNames.YK_YSD0, 4),
    // 药库入库
    INBOUND(G3GlobalNames.YK_RKD0, 2),
    // 药库出库
    OUTBOUND(G3GlobalNames.YK_CKD0, 3);
    // ...

    private Long opcode;
    private Integer lx;

    /**
     * 获取对应类型的操作码
     *
     * @param lx 类型值
     * @return 操作码
     */
    public static Long getOpcode(Integer lx) {
        return Stream.of(YwdhOpsEnum.values())
                .filter(e -> Objects.equals(e.getLx(), lx))
                .findFirst().map(e -> e.opcode)
                // 如果lx不存在,opcode返回无效值:-1L
                .orElse(-1L);
    }

    /**
     * 获取操作码的类型
     *
     * @param opcode 药库操作的编码
     * @return 类型值
     */
    public static Integer getLx(long opcode) {
        return Stream.of(YwdhOpsEnum.values())
                .filter(e -> Objects.equals(e.getOpcode(), opcode))
                .findFirst().map(e -> e.lx)
                // 如果opcode不存在,lx返回无效值:-1
                .orElse(-1);
    }

    YwdhOpsEnum(Long opcode, Integer lx) {
        this.opcode = opcode;
        this.lx = lx;
    }

    public Long getOpcode() {
        return opcode;
    }

    public void setOpcode(Long opcode) {
        this.opcode = opcode;
    }

    public Integer getLx() {
        return lx;
    }

    public void setLx(Integer lx) {
        this.lx = lx;
    }
}

Repository

YwdhConfigRepository(业务单号配置 Repository)

import com.yuanbo.chss.domain.business.YwdhConfig;

import java.util.List;
import java.util.Map;

/**
 * @Author: DengJia
 * @Date: 2022/2/14 14:30
 * @Description: 药库单号配置 Repository
 */
public interface YwdhConfigRepository {

    /**
     * 查询业务单号配置,入参含主键则通过主键查询,否则按条件查询。
     *
     * @param dataMap 过滤条件
     * @return 配置信息
     */
    List<Map<String, Object>> selectYwdhConfig(Map<String, Object> dataMap);

    /**
     * 用户自定义配置业务单号格式
     *
     * @param dbBean    原值
     * @param paramBean 修改值
     */
    void updateYwdhConfig(YwdhConfig dbBean, YwdhConfig paramBean);

    /**
     * 删除单号配置
     * @param ids 待删除单号主键ids
     */
    void deleteBusinessNumConfigByIds(List<Integer> ids);

}

YwdhConfigRepositoryImpl(业务单号配置 RepositoryImpl)

import com.yuanbo.chss.domain.business.YwdhConfig;
import com.yuanbo.common.util.LoginHelper;
import com.yuanbo.hap.util.DBAgent;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Repository;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.yuanbo.hap.util.ParamUtil.beanToMap;
import static com.yuanbo.hap.util.ParamUtil.mapToBean;

/**
 * @Author: DengJia
 * @Date: 2022/2/14 14:39
 * @Description: 业务单号配置 RepositoryImpl
 */

@Repository("YwdhConfigRepository")
public class YwdhConfigRepositoryImpl implements YwdhConfigRepository {
    private static final DBAgent DBA = DBAgent.getInstance();

    @Override
    public List<Map<String, Object>> selectYwdhConfig(Map<String, Object> dataMap) {
        StringBuilder querySql = new StringBuilder("select * from ywdh_config where sqbm = ::SQBM ");
        if (dataMap.get("PKYWDHCONFIG") != null) {
            querySql.append("and pkywdhconfig = ::PKYWDHCONFIG ");
        } else {
            if (dataMap.get("LX") != null) {
                querySql.append("and lx = ::LX ");
            }
            if (dataMap.get("KSLX") != null) {
                querySql.append("and kslx = ::KSLX ");
            }
            if (dataMap.get("JTLX") != null) {
                querySql.append("and jtlx = ::JTLX ");
            }
//            if (dataMap.get("CZRYID") != null) {
//                querySql.append("and czryid = ::CZRYID ");
//            }
            if (dataMap.get("DHQZ") != null) {
                querySql.append("and dhqz = :DHQZ ");
            }
            if (dataMap.get("DHCD") != null) {
                querySql.append("and dhcd = ::DHCD ");
            }
            if (dataMap.get("DHQSH") != null) {
                querySql.append("and dhqsh = ::DHQSH ");
            }
            if (dataMap.get("DHBC") != null) {
                querySql.append("and dhbc = ::DHBC ");
            }
            if (dataMap.get("ISOPEN") != null) {
                querySql.append("and isopen = ::ISOPEN ");
            }
            if (dataMap.get("CURRENTTIME") != null) {
                querySql.append("and currenttime = ::CURRENTTIME ");
            }
        }
        return DBA.querySQL(querySql.toString(), dataMap, null);
    }

    @Override
    public void updateYwdhConfig(YwdhConfig dbBean, YwdhConfig paramBean) {
        YwdhConfig config = new YwdhConfig();
        // 浅拷贝
        BeanUtils.copyProperties(paramBean, config);
        if (config.getLx() == null) {
            config.setLx(dbBean.getLx());
        }
        if (config.getKslx() == null) {
            config.setKslx(dbBean.getKslx());
        }
        if (config.getJtlx() == null) {
            config.setJtlx(dbBean.getJtlx());
        }
        if (config.getCzryid() == null) {
            config.setCzryid(dbBean.getCzryid());
        }
        if (config.getDhqz() == null) {
            config.setDhqz(dbBean.getDhqz());
        }
        if (config.getDhcd() == null) {
            config.setDhcd(dbBean.getDhcd());
        }
        if (config.getDhqsh() == null) {
            config.setDhqsh(dbBean.getDhqsh());
        }
        if (config.getDhbc() == null) {
            config.setDhbc(dbBean.getDhbc());
        }
        if (config.getIsopen() == null) {
            config.setIsopen(dbBean.getIsopen());
        }
        if (config.getCurrenttime() == null) {
            config.setCurrenttime(dbBean.getCurrenttime());
        }
        config.setCreatedAt(dbBean.getCreatedAt());
        DBA.update(null, config);
    }

    @Override
    public void deleteBusinessNumConfigByIds(List<Integer> ids) {
        // 去掉字符串前后中括号
        String strIds = org.apache.commons.lang3.StringUtils.strip(ids.toString(), "[]");
        DBA.executeProcSql("delete from ywdh_config where pkywdhconfig in ( " + strIds + " )", null);
    }
}

YwdhRedisPairRepository(Redis键值对配置 Repository)

import com.yuanbo.chss.domain.business.YwdhRedisPair;

import java.util.List;

public interface YwdhRedisPairRepository {

    List<YwdhRedisPair> selectYwdhKeyConfig(YwdhRedisPair ywdhRedisPair);

    void insertYwdhRedisPair(YwdhRedisPair ywdhRedisPair);

    void updateYwdhKeyConfig(YwdhRedisPair ywdhRedisPair);

    void deleteYwdhKeyConfig(YwdhRedisPair ywdhRedisPair);
}

YwdhRedisPairRepositoryImpl(Redis键值对配置 RepositoryImpl)

import com.yuanbo.chss.domain.business.YwdhRedisPair;
import com.yuanbo.hap.util.DBAgent;
import com.yuanbo.hap.util.ParamUtil;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Author: DengJia
 * @Date: 2022/2/17 11:28
 * @Description:
 */

@Repository("YwdhRedisPairRepository")
public class YwdhRedisPairRepositoryImpl implements YwdhRedisPairRepository {
    private static final DBAgent DBA = DBAgent.getInstance();

    @Override
    public List<YwdhRedisPair> selectYwdhKeyConfig(YwdhRedisPair ywdhRedisPair) {
        HashMap<String, Object> dataMap = new HashMap<>(4);
        ParamUtil.beanToMap(ywdhRedisPair, dataMap, true);
        StringBuilder querySql = new StringBuilder("select * from ywdh_redis_pair where sqbm = :sqbm ");
        if (dataMap.get("pkpair") != null) {
            querySql.append("and pkpair = :pkpair ");
        } else {
            if (dataMap.get("fkywdhconfig") != null) {
                querySql.append("and fkywdhconfig = :fkywdhconfig ");
            }
            if (dataMap.get("currenttime") != null) {
                querySql.append("and currenttime = :currenttime ");
            }
            if (dataMap.get("keyname") != null) {
                querySql.append("and keyname = :keyname ");
            }
            if (dataMap.get("value") != null) {
                querySql.append("and value = :value ");
            }
        }

        List<Map<String, Object>> list = DBA.querySQL(querySql.toString(), dataMap, null);
        return ParamUtil.mapsToBeans(list, YwdhRedisPair.class, true);
    }

    @Override
    public void insertYwdhRedisPair(YwdhRedisPair ywdhRedisPair) {
        DBA.save(null, ywdhRedisPair);
    }

    @Override
    public void updateYwdhKeyConfig(YwdhRedisPair ywdhRedisPair) {
//        Map<String, Object> map = new HashMap<>();
//        map.put("SQBM", ywdhRedisPair.getSqbm());
//        map.put("KEYNAME", ywdhRedisPair.getKeyname());
//        if (ywdhRedisPair.getValue() != null) {
//            map.put("VALUE", ywdhRedisPair.getValue());
//        }
//
//        StringBuilder sb = new StringBuilder("update ywdh_redis_pair set value = 3 ");
//
//        sb.append("where sqbm = ::SQBM and keyname = ::KEYNAME");
//
//        DBA.executeSQL(sb.toString(), );
        DBA.update(null, ywdhRedisPair);
    }

    @Override
    public void deleteYwdhKeyConfig(YwdhRedisPair ywdhRedisPair) {
        DBA.delete(null, ywdhRedisPair);
    }
}

Service

YwdhConfigBS(业务单号配置 业务接口)

import com.yuanbo.chss.domain.business.YwdhConfig;
import com.yuanbo.hap.webservice.function.FuncResult;

import java.util.Map;

/**
 * his业务单号配置 业务接口
 */
public interface YwdhConfigBS {

    /**
     * 创建单号生成规则的时候,也生成redis键,由于redis未进行持久化配置,同时需要将key记录进mysql。
     * 客户端传入的JTLX,代表多条具体类型,以 ',' 逗号分割,分别存储。
     *
     * @param dataMap 配置信息
     * @return 添加成功
     */
    FuncResult createBusinessNumConfig(Map<String, Object> dataMap);

    /**
     * 条件查询业务单号配置
     *
     * @param dataMap 查询条件
     * @return 按条件过滤后的配置信息
     */
    FuncResult queryBusinessNumConfig(Map<String, Object> dataMap);


    /**
     * 按入参TYPE分别进行修改、新增、删除
     * 通过时间戳字段(CURRENTTIME)实现乐观锁机制(修改之前查出来的时间戳与数据库的时间戳进行对比,一致方可进行本次修改)
     *
     * @param dataMap 入参
     * @return 修改成功
     */
    FuncResult updateBusinessNumConfigByType(Map<String, Object> dataMap);

    /**
     * 删除业务单号配置
     *
     * @param dataMap 待删除的配置主键
     * @return 删除成功
     */
    FuncResult deleteBusinessNumConfigByType(Map<String, Object> dataMap);
}

YwdhConfigBSImpl(业务单号配置 业务类)

import com.yuanbo.chss.domain.business.YwdhConfig;
import com.yuanbo.chss.domain.business.YwdhRedisPair;
import com.yuanbo.chss.domain.log.LogCzrz;
import com.yuanbo.chss.domain.yk.YwdhOpsEnum;
import com.yuanbo.chss.repository.YwdhConfigRepository;
import com.yuanbo.chss.repository.YwdhRedisPairRepository;
import com.yuanbo.common.util.DateTransUtil;
import com.yuanbo.common.util.LoginHelper;
import com.yuanbo.hap.core.exception.AppException;
import com.yuanbo.hap.util.DBAgent;
import com.yuanbo.hap.util.WebServiceUtils;
import com.yuanbo.hap.webservice.function.FuncResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;
import java.util.stream.Collectors;

import static com.yuanbo.hap.util.ParamUtil.*;
import static com.yuanbo.hap.util.ParamUtil.mapToBean;

/**
 * @Author: DengJia
 * @Date: 2022/2/14 14:30
 * @Description: 业务单号配置 业务类
 */
@Service("YwdhConfigBS")
@Transactional
public class YwdhConfigBSImpl implements YwdhConfigBS {
    @Autowired
    private YwdhConfigRepository ywdhConfigRepository;
    @Autowired
    private YwdhRedisPairRepository ywdhRedisPairRepository;
    @Resource
    private RedisTemplate<Object, Object> redisTemplate;

    private static final String PREFIX = "INCR";
    private static final DBAgent DBA = DBAgent.getInstance();

    /**
     * 乐观锁机制下执行数据的改动(UPDATE、DELETE)
     */
    public static final String OPTIMISTIC_LOCK_UPDATE = "UPDATE";
    public static final String OPTIMISTIC_LOCK_DELETE = "DELETE";

    @Override
    public FuncResult createBusinessNumConfig(Map<String, Object> dataMap) {
        // 入参校验
        if (dataMap.get("SQBM") == null) {
            dataMap.put("SQBM", LoginHelper.getLoginerSQBM());
        }
        if (dataMap.get("CZRYID") == null) {
            dataMap.put("CZRYID", LoginHelper.getLoginerId());
        }
        if (dataMap.get("LX") == null) {
            throw new AppException("LX不可为空!");
        }
        if (dataMap.get("KSLX") == null) {
            throw new AppException("KSLX不可为空!");
        }
        List<YwdhConfig> configs = new ArrayList<>();
        StringBuilder rklxString = new StringBuilder();
        List<Integer> rklxList = new ArrayList<>();

        if (dataMap.get("JTLX") != null) {
            try {
                String jtlxString = dataMap.get("JTLX").toString();
                if (StringUtils.isNotBlank(jtlxString)) {
                    String[] jtlxs = jtlxString.split(",");
                    configs = new ArrayList<>();
                    for (String jtlx : jtlxs) {
                        int i = Integer.parseInt(jtlx.trim());
                        dataMap.put("JTLX", i);
                        configs.add(getBean(dataMap));
                        rklxList.add(i);
                    }
                }
            } catch (Exception e) {
                throw new AppException("请将JTLX以逗号分割传入!");
            }
        } else {
            configs.add(getBean(dataMap));
        }

        // 检查可行性,及后续操作
        configs.forEach(this::checkAvailability);

        // 为多个具体类型设置同一套规则(一次添加一套规则)
        String nowTimeString = String.valueOf(System.currentTimeMillis());
        System.currentTimeMillis();
        for (YwdhConfig config : configs) {
            config.setCurrenttime(nowTimeString);
            // mysql保存自定义的规则
            DBA.save(null, config);
        }

        // 生成key
        StringBuilder keyName = new StringBuilder(PREFIX + "_" + dataMap.get("SQBM"));
        if (dataMap.get("LX") != null) {
            Long dhlx = YwdhOpsEnum.getOpcode(Integer.parseInt(dataMap.get("LX").toString()));
            keyName.append("_").append(dhlx);
        }
        if (dataMap.get("KSLX") != null) {
            Integer kslx = Integer.parseInt(dataMap.get("KSLX").toString());
            keyName.append("_").append(kslx);
        }
        if (!rklxList.isEmpty()) {
            Collections.sort(rklxList);
            rklxList.forEach(rklxString::append);
            keyName.append("_").append(rklxString);
        }

        // mysql保存键
        YwdhRedisPair pair = new YwdhRedisPair();
        pair.setCurrenttime(nowTimeString);
        pair.setSqbm(dataMap.get("SQBM").toString());
        pair.setKeyname(keyName.toString());
        pair.setValue(dataMap.get("DHQSH").toString());
        ywdhRedisPairRepository.insertYwdhRedisPair(pair);

        // redis设置键
        callRedisToSetKeyAndExpiration(keyName.toString(), Integer.parseInt(dataMap.get("DHQSH").toString()));

        return new FuncResult().setMsg("添加成功");
    }

    @Override
    public FuncResult queryBusinessNumConfig(Map<String, Object> dataMap) {
        if (dataMap.get("SQBM") == null) {
            dataMap.put("SQBM", LoginHelper.getLoginerSQBM());
        }
        List<Map<String, Object>> list = ywdhConfigRepository.selectYwdhConfig(dataMap);
        return new FuncResult().addDataSet("YWDHCONFIG", list);
    }

    @Override
    @Transactional
    public FuncResult updateBusinessNumConfigByType(Map<String, Object> dataMap) {
        final String currentTimeMillis = String.valueOf(System.currentTimeMillis());
        String sqbm = LoginHelper.getLoginerSQBM();
        Integer czryid = Integer.parseInt(LoginHelper.getLoginerId().toString());
        List[] listArray = (List[]) dataMap.get("YWDHCONFIG");
        List<Map<String, Object>> configMapList = listArray[0];
        List<String> flagList = listArray[1];
        List<YwdhConfig> configBeanList = mapsToBeans(configMapList, YwdhConfig.class, true);
        final int dataSize = configMapList.size();

        // 设置操作人员
        configBeanList.forEach(b -> b.setCzryid(czryid));

        List<Integer>
                // 之前的具体类型列表
                jtlxList0 = new ArrayList<>(),
                // 现在的具体类型列表
                jtlxList1 = new ArrayList<>();
        StringBuilder
                // 之前的redis后缀
                jtlxStr0 = new StringBuilder(),
                // 现在的redis后缀
                jtlxStr1 = new StringBuilder();

        int addSize = 0, updateSize = 0, deleteSize = 0, unChangedSize = 0;

        final String rowOp_unchange = "UnChanged";
        for (String flag : flagList) {
            switch (flag) {
                case WebServiceUtils.rowOp_add:
                    addSize++;
                    break;
                case WebServiceUtils.rowOp_delete:
                    deleteSize++;
                    break;
                case WebServiceUtils.rowOp_update:
                    updateSize++;
                    break;
                case rowOp_unchange:
                    unChangedSize++;
                    break;
                default:
                    break;
            }
        }

        boolean
                onlyAdd = addSize == dataSize,
                onlyDelete = deleteSize == dataSize,
                onlyUpdate = updateSize == dataSize,
                unChange = unChangedSize == dataSize;

        if (unChange) {
            return new FuncResult().setMsg("修改成功");
        }
        if (onlyAdd) {
            doOnlyAddAction(configBeanList, jtlxList0, jtlxList1, currentTimeMillis);
        } else if (onlyDelete) {
            doOnlyDeleteAction(configBeanList, jtlxList0, jtlxList1, currentTimeMillis);
        } else {
            doElseAction(listArray, jtlxList0, jtlxList1, onlyUpdate, currentTimeMillis);
        }

        Collections.sort(jtlxList0);
        Collections.sort(jtlxList1);

        jtlxList0.forEach(jtlxStr0::append);
        jtlxList1.forEach(jtlxStr1::append);

        // 做好Redis的同步以及MySQL的数据备份
        syncAndBackup(configBeanList, jtlxStr0, jtlxStr1, currentTimeMillis);

        return new FuncResult().setMsg("修改成功");
    }

    @Override
    public FuncResult deleteBusinessNumConfigByType(Map<String, Object> dataMap) {
        if (dataMap.get("YWDHCONFIG") == null) {
            throw new AppException("YWDHCONFIG不可为空!");
        }
        List[] lists = (List[]) dataMap.get("YWDHCONFIG");
        List<Map<String, Object>> ywdhConfigs = lists[0];
        for (Map<String, Object> ywdhConfigMap : ywdhConfigs) {
            // 条件校验
            if (ywdhConfigMap.get("SQBM") == null) {
                ywdhConfigMap.put("SQBM", LoginHelper.getLoginerSQBM());
            }
            if (ywdhConfigMap.get("CZRYID") == null) {
                ywdhConfigMap.put("CZRYID", LoginHelper.getLoginerId());
            }
            if (ywdhConfigMap.get("PKYWDHCONFIG") == null) {
                throw new AppException("PKYWDHCONFIG不可为空!");
            }
        }

        StringBuilder rklxString = new StringBuilder();
        List<Integer> rklxList = new ArrayList<>();
        for (Map<String, Object> ywdhConfigMap : ywdhConfigs) {
            Map<String, Object> dbYwdhConfigMap = ywdhConfigRepository.selectYwdhConfig(ywdhConfigMap).get(0);
            YwdhConfig dbYwdhConfig = getBean(dbYwdhConfigMap);
            // 删除配置规则
            DBA.delete(null, dbYwdhConfig);
            // 记录日志
            doLogAction(dbYwdhConfig, null);

            if (dbYwdhConfigMap.get("JTLX") != null) {
                int jtlx = Integer.parseInt(dbYwdhConfigMap.get("JTLX").toString());
                rklxList.add(jtlx);
            }
        }

        // 获取key
        Map<String, Object> ywdhConfig = ywdhConfigs.get(0);
        StringBuilder keyName = new StringBuilder(PREFIX + "_" + ywdhConfig.get("SQBM"));
        if (ywdhConfig.get("LX") != null) {
            Long dhlx = YwdhOpsEnum.getOpcode(Integer.parseInt(ywdhConfig.get("LX").toString()));
            keyName.append("_").append(dhlx);
        }
        if (ywdhConfig.get("KSLX") != null) {
            Integer kslx = Integer.parseInt(ywdhConfig.get("KSLX").toString());
            keyName.append("_").append(kslx);
        }

        if (!rklxList.isEmpty()) {
            List<Integer> sorted = rklxList.stream().sorted().collect(Collectors.toList());
            sorted.forEach(rklxString::append);
            keyName.append("_").append(rklxString);
        }

        // 删除redis
        redisTemplate.delete(keyName.toString());
        // 删除mysql键值
        YwdhRedisPair pairCondition = new YwdhRedisPair();
        pairCondition.setSqbm(ywdhConfig.get("SQBM").toString());
        pairCondition.setKeyname(keyName.toString());
        List<YwdhRedisPair> queryResult = ywdhRedisPairRepository.selectYwdhKeyConfig(pairCondition);
        if (!queryResult.isEmpty()) {
            ywdhRedisPairRepository.deleteYwdhKeyConfig(queryResult.get(0));
        }

        return new FuncResult().setMsg("删除成功");
    }

    /**
     * 获取Map类型数据的实体类对象
     *
     * @param dataMap 入参配置信息
     * @return 类对象
     */
    private YwdhConfig getBean(Map<String, Object> dataMap) {
        YwdhConfig bean = new YwdhConfig();
        mapToBean(dataMap, bean, true);
        return bean;
    }

    /**
     * 针对一次UPDATE里面仅有新增的操作
     *
     * @param configBeanList    入参配置
     * @param jtlxList0         改之前的具体类型列表
     * @param jtlxList1         现在的具体类型列表
     * @param currentTimeMillis 系统当前时间戳
     */
    private void doOnlyAddAction(List<YwdhConfig> configBeanList, List<Integer> jtlxList0, List<Integer> jtlxList1, String currentTimeMillis) {
        /*
            currenttimeVersion(乐观锁标识),
            以时间戳实现乐观锁,只有每次修改前从数据库查出来的时间戳和修改时数据库的数据仍然为此值时,才允许修改数据,
            否则已经发生了并行操作,不允许修改。
         */
        String currenttimeVersion = "";

        YwdhConfig param = configBeanList.get(0);
        Map<String, Object> condi = new HashMap<>(4);
        condi.put("sqbm", param.getSqbm());
        condi.put("dhqz", param.getDhqz());
        condi.put("lx", param.getLx());
        condi.put("kslx", param.getKslx());

        // 记录具体类型id值并且修改时间戳
        List<YwdhConfig> queryConfigBeanList = DBA.find(YwdhConfig.class, condi, null);
        for (YwdhConfig config : queryConfigBeanList) {
            currenttimeVersion = config.getCurrenttime();
            jtlxList0.add(config.getJtlx());
            config.setCurrenttime(currentTimeMillis);
        }
        for (YwdhConfig config : configBeanList) {
            jtlxList1.add(config.getJtlx());
            config.setCurrenttime(currentTimeMillis);
        }
        jtlxList1.addAll(jtlxList0);

        // 乐观锁机制下执行数据修改保存操作
        optimisticLockingDataChange(currenttimeVersion, queryConfigBeanList, OPTIMISTIC_LOCK_UPDATE);
        DBA.save(null, configBeanList);
    }

    /**
     * 针对一次UPDATE里面仅有删除的操作
     *
     * @param configBeanList    入参配置
     * @param jtlxList0         改之前的具体类型列表
     * @param jtlxList1         现在的具体类型列表
     * @param currentTimeMillis 系统当前时间戳
     */
    private void doOnlyDeleteAction(List<YwdhConfig> configBeanList, List<Integer> jtlxList0, List<Integer> jtlxList1, String currentTimeMillis) {
        /*
            currenttimeVersion(乐观锁标识),
            以时间戳实现乐观锁,只有每次修改前从数据库查出来的时间戳和修改时数据库的数据仍然为此值时,才允许修改数据,
            否则已经发生了并行操作,不允许修改。
         */
        String currenttimeVersion = "";
        YwdhConfig param = configBeanList.get(0);
        Map<String, Object> condi = new HashMap<>(4);
        condi.put("sqbm", param.getSqbm());
        condi.put("dhqz", param.getDhqz());
        condi.put("lx", param.getLx());
        condi.put("kslx", param.getKslx());

        List<YwdhConfig> queryConfigBeanList = DBA.find(YwdhConfig.class, condi, null);
        currenttimeVersion = queryConfigBeanList.get(0).getCurrenttime();

        // 记录具体类型id值并且修改时间戳
        for (YwdhConfig config : queryConfigBeanList) {
            jtlxList0.add(config.getJtlx());
        }

        List<Integer> needDel = configBeanList.stream().map(YwdhConfig::getJtlx).collect(Collectors.toList());
        List<YwdhConfig> retainConfigList = queryConfigBeanList.stream().filter(jtlx -> !needDel.contains(jtlx.getJtlx())).collect(Collectors.toList());
        List<Integer> retain = retainConfigList.stream().map(YwdhConfig::getJtlx).collect(Collectors.toList());
        jtlxList1.addAll(retain);

        for (YwdhConfig config : retainConfigList) {
            config.setCurrenttime(currentTimeMillis);
        }

        List<Integer> pkids = configBeanList.stream().map(YwdhConfig::getPkywdhconfig).collect(Collectors.toList());
        ywdhConfigRepository.deleteBusinessNumConfigByIds(pkids);

        // 乐观锁执行修改
        optimisticLockingDataChange(currenttimeVersion, retainConfigList, OPTIMISTIC_LOCK_UPDATE);

        // 记录批量删除日志
        List<YwdhConfig> deleteSet = queryConfigBeanList.stream().filter(b -> pkids.contains(b.getPkywdhconfig())).collect(Collectors.toList());
        deleteSet.forEach(config -> doLogAction(config, null));
    }

    /**
     * 针对一次UPDATE里面不是仅有增加操作或不是仅有删除操作的其他操作
     *
     * @param listArray         原生列表数组
     * @param jtlxList0         改之前的具体类型列表
     * @param jtlxList1         现在的具体类型列表
     * @param onlyUpdate        仅有修改的操作
     * @param currentTimeMillis 系统当前时间戳
     */
    private void doElseAction(List[] listArray, List<Integer> jtlxList0, List<Integer> jtlxList1, boolean onlyUpdate, String currentTimeMillis) {
        List<Map<String, Object>> configMapList = listArray[0];
        List<String> flagList = listArray[1];
        List<YwdhConfig> configBeanList = mapsToBeans(configMapList, YwdhConfig.class, true);
        final int dataSize = configMapList.size();
        Integer czryid = Integer.parseInt(LoginHelper.getLoginerId().toString());
        List<YwdhConfig> allDbConfigBeans = new ArrayList<>();

        if (!onlyUpdate) {
            // 获取所涉及到的所有具体类型的数据库记录
            allDbConfigBeans = getYwdhConfigs(configMapList, flagList);
        }

        // 通过flag类型来进行相应的操作,日志记录等
        for (int i = 0; i < dataSize; i++) {
            String flag = flagList.get(i);
            Map<String, Object> map = configMapList.get(i);
            YwdhConfig bean = configBeanList.get(i);

            if (WebServiceUtils.rowOp_add.equalsIgnoreCase(flag)) {
                bean.setCurrenttime(currentTimeMillis);
                bean.setCzryid(czryid);
                // 新增
                DBA.save(null, bean);
                // 记录具体类型id值
                jtlxList1.add(bean.getJtlx());

            } else if (WebServiceUtils.rowOp_delete.equalsIgnoreCase(flag)) {
                YwdhConfig
                        paramConfig = getBean(map),
                        dbConfig = getBean(ywdhConfigRepository.selectYwdhConfig(map).get(0));
                // 乐观锁机制执行删除
                List<YwdhConfig> deleteConfigBeanList = new ArrayList<>();
                deleteConfigBeanList.add(paramConfig);
                optimisticLockingDataChange(dbConfig.getCurrenttime(), deleteConfigBeanList, OPTIMISTIC_LOCK_DELETE);

                // 记录删除日志
                doLogAction(dbConfig, null);
                // 记录具体类型id值
                jtlxList0.add(dbConfig.getJtlx());

            } else if (WebServiceUtils.rowOp_update.equalsIgnoreCase(flag)) {
                YwdhConfig
                        paramConfig = getBean(map),
                        dbConfig = getBean(ywdhConfigRepository.selectYwdhConfig(map).get(0));
                paramConfig.setCurrenttime(currentTimeMillis);
                paramConfig.setCzryid(czryid);
                // 乐观锁机制执行修改
                List<YwdhConfig> updateConfigBeanList = new ArrayList<>();
                updateConfigBeanList.add(paramConfig);
                optimisticLockingDataChange(dbConfig.getCurrenttime(), updateConfigBeanList, OPTIMISTIC_LOCK_UPDATE);
                // 记录修改日志
                doLogAction(dbConfig, paramConfig);
                // 记录具体类型id值
                jtlxList0.add(dbConfig.getJtlx());
                jtlxList1.add(dbConfig.getJtlx());
            }
        }

        if (!onlyUpdate) {
            // 可能有增删和未修改的数据,需要对未修改的数据执行相关操作
            doMoreAction(allDbConfigBeans, jtlxList0, jtlxList1, currentTimeMillis);
        }
    }

    /**
     * 乐观锁机制执行数据改动(UPDATE、DELETE)
     *
     * @param currenttimeVersion 乐观锁标识
     * @param changeDataList     需要进行改动的数据列表
     * @param flag               UPDATE or DELETE
     */
    private void optimisticLockingDataChange(String currenttimeVersion, List<YwdhConfig> changeDataList, String flag) {
        if (OPTIMISTIC_LOCK_UPDATE.equalsIgnoreCase(flag)) {
            for (YwdhConfig config : changeDataList) {
                StringBuilder spliceSQL = new StringBuilder(" ");
                Class<YwdhConfig> c = YwdhConfig.class;
                Field[] fields = c.getDeclaredFields();
                // 反射拼接更新语句的SET项
                for (Field field : fields) {
                    field.setAccessible(true);
                    Class<?> type = field.getType();
                    String name = field.getName();
                    if ("createdAt".equalsIgnoreCase(name) || "updatedAt".equalsIgnoreCase(name)) {
                        continue;
                    }
                    Object value = null;
                    try {
                        value = field.get(config);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }

                    if (type == String.class) {
                        spliceSQL.append(name).append("='").append(value).append("',");
                    } else {
                        spliceSQL.append(name).append("=").append(value).append(",");
                    }
                }
                String sqlToString = spliceSQL.substring(0, spliceSQL.length() - 1);
                String updateSQL = "update ywdh_config set " + sqlToString + " where PKYWDHCONFIG=" + config.getPkywdhconfig() + " and CURRENTTIME=" + currenttimeVersion;
                if (DBA.executeSQL(updateSQL, null) != 1) {
                    throw new AppException("当前页面数据已被更改,请刷新页面后再试!");
                }
            }
        } else if (OPTIMISTIC_LOCK_DELETE.equalsIgnoreCase(flag)) {
            for (YwdhConfig config : changeDataList) {
                String deleteSQL = "delete from ywdh_config where PKYWDHCONFIG=" + config.getPkywdhconfig() + " and CURRENTTIME=" + currenttimeVersion;
                if (DBA.executeSQL(deleteSQL, null) != 1) {
                    throw new AppException("当前页面数据已被更改,请刷新页面后再试!");
                }
            }
        }
    }

    /**
     * 获取所涉及到的所有具体类型的数据库记录
     *
     * @param configMapList 入参配置集合
     * @param flagList      入参标志集合
     * @return 所有相关的配置项(即每一个具体类型的数据记录)
     */
    private List<YwdhConfig> getYwdhConfigs(List<Map<String, Object>> configMapList, List<String> flagList) {
        int dataSize = configMapList.size();
        List<YwdhConfig> allDbConfigBeans;
        Map<String, Object> oneMap = null;
        for (int i = 0; i < dataSize; i++) {
            // 随便选择一个修改的或者删除的(为了获取时间戳),通过时间戳查询数据库里的所有config
            boolean deleteOrUpdate = WebServiceUtils.rowOp_delete.equalsIgnoreCase(flagList.get(i))
                    || WebServiceUtils.rowOp_update.equalsIgnoreCase(flagList.get(i));
            if (deleteOrUpdate) {
                oneMap = configMapList.get(i);
                break;
            }
        }
        String dbTime =
                ywdhConfigRepository.selectYwdhConfig(oneMap)
                        .get(0).get("CURRENTTIME").toString();

        Map<String, Object> condi = new HashMap<>(2);
        condi.put("SQBM", LoginHelper.getLoginerSQBM());
        condi.put("CURRENTTIME", dbTime);
        allDbConfigBeans = mapsToBeans(ywdhConfigRepository.selectYwdhConfig(condi),
                YwdhConfig.class, true);
        return allDbConfigBeans;
    }

    /**
     * 可能有增删和未修改的数据,需要对未修改的数据执行相关操作
     *
     * @param allDbConfigBeans  涉及到的所有具体类型的业务配置
     * @param jtlxList0         改之前的具体类型列表
     * @param jtlxList1         现在的具体类型列表
     * @param currentTimeMillis 系统当前时间戳
     */
    private void doMoreAction(List<YwdhConfig> allDbConfigBeans, List<Integer> jtlxList0, List<Integer> jtlxList1, String currentTimeMillis) {
        List<YwdhConfig> unchangedList =
                allDbConfigBeans.stream()
                        .filter(b -> !jtlxList0.contains(b.getJtlx()) && !jtlxList1.contains(b.getJtlx()))
                        .collect(Collectors.toList());

        List<Integer> unchangedJtlxs =
                unchangedList.stream()
                        .map(YwdhConfig::getJtlx).collect(Collectors.toList());

        jtlxList0.addAll(unchangedJtlxs);
        jtlxList1.addAll(unchangedJtlxs);

        unchangedList.forEach(b -> b.setCurrenttime(currentTimeMillis));
        DBA.update(null, unchangedList);
    }

    /**
     * 执行Redis的单号起始号改动以及在MySQL做好备份(用作在Redis数据丢失之后通过MySQL找回数据)
     *
     * @param configBeanList    入参配置
     * @param jtlxStr0          改之前的具体类型
     * @param jtlxStr1          现在的具体类型
     * @param currentTimeMillis 系统当前时间戳
     */
    private void syncAndBackup(List<YwdhConfig> configBeanList, StringBuilder jtlxStr0, StringBuilder jtlxStr1, String currentTimeMillis) {
        String sqbm = LoginHelper.getLoginerSQBM();
        YwdhConfig config = configBeanList.get(0);

        // 获取keyName
        StringBuilder
                keyName0 = new StringBuilder(PREFIX + "_" + sqbm),
                keyName1 = new StringBuilder(PREFIX + "_" + sqbm);
        if (config.getLx() != null) {
            Long dhlx = YwdhOpsEnum.getOpcode(config.getLx());
            keyName0.append("_").append(dhlx);
            keyName1.append("_").append(dhlx);
        }
        if (config.getKslx() != null) {
            keyName0.append("_").append(config.getKslx());
            keyName1.append("_").append(config.getKslx());
        }
        if (StringUtils.isNotBlank(jtlxStr0) && !Objects.equals("null", jtlxStr0.toString())) {
            keyName0.append("_").append(jtlxStr0);
        }
        if (StringUtils.isNotBlank(jtlxStr1) && !Objects.equals("null", jtlxStr1.toString())) {
            keyName1.append("_").append(jtlxStr1);
        }

        // 删除mysql旧键值
        YwdhRedisPair pair0 = new YwdhRedisPair(null, null, sqbm, keyName0.toString(), null);
        List<YwdhRedisPair> pairQueryResult = ywdhRedisPairRepository.selectYwdhKeyConfig(pair0);
        DBA.delete(null, pairQueryResult);

        // 添加mysql新键值
        YwdhRedisPair pair1 = new YwdhRedisPair(null, currentTimeMillis, sqbm, keyName1.toString(), config.getDhqsh().toString());
        DBA.save(null, pair1);

        // 删除旧redis
        redisTemplate.delete(keyName0.toString());
        // 添加新redis
        callRedisToSetKeyAndExpiration(keyName1.toString(), Integer.parseInt(config.getDhqsh().toString()));
    }

    /**
     * 通过反射记录日志
     *
     * @param from 从什么
     * @param to   变成了什么
     */
    private void doLogAction(YwdhConfig from, YwdhConfig to) {
        boolean
                del = from != null && to == null,
                mod = from != null && to != null;

        DBAgent dba = DBAgent.getInstance();
        // 获取所有字段
        Field[] fields = YwdhConfig.class.getDeclaredFields();
        LogCzrz rz = new LogCzrz();
        StringBuilder tableColumn = new StringBuilder();
        StringBuilder xgxx = new StringBuilder();
        for (Field field : fields) {
            // 获取属性名
            String attrName = field.getName();
            // 将属性名的首字母变为大写,为set/get方法做准备
            String methodName = attrName.substring(0, 1).toUpperCase() + attrName.substring(1);
            Object result = null;
            Object dbObj = null;
            try {
                // 获取类当前属性的set/getXXX方法(只获取公有方法)
                Method method = YwdhConfig.class.getMethod("get" + methodName);
                // 执行该方法
                if (mod) {
                    result = method.invoke(to);
                }
                dbObj = method.invoke(from);
            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                try {
                    result = field.get(to);
                    dbObj = field.get(from);
                } catch (IllegalAccessException illegalAccessException) {
                    illegalAccessException.printStackTrace();
                }
                e.printStackTrace();
            }

            // 修改又是字段给的null,这种情况不记录日志,其他都记录
            if (!mod || result != null) {
                // 判断字段并进行日志记录
                if (!Objects.equals(dbObj, result)) {
                    tableColumn.append(attrName).append(", ");
                    xgxx.append(attrName).append(": ").append(dbObj).append(" -> ").append(result).append(" | ");
                }
            }
        }
        if (tableColumn.length() > 0) {
            rz.setTableName("ywdh_config");
            rz.setTableId(from.getPkywdhconfig().toString());
            rz.setSqbm(LoginHelper.getLoginerSQBM());
            rz.setXgysbm(LoginHelper.getLoginerBm());
            rz.setXgysmc(LoginHelper.getLoginerName());
            rz.setTableColumn(tableColumn.toString());
            rz.setXgxx(xgxx.toString());
            Map<String, Integer> m = DateTransUtil.getOLEDateFromDate(new Date());
            rz.setXgrq(m.get("days"));
            rz.setXgsj(m.get("second"));
            try {
                InetAddress host = InetAddress.getLocalHost();
                rz.setMachineIp(host.getHostAddress());
                rz.setMachineName(host.getHostName());
            } catch (UnknownHostException e) {
                rz.setMachineName(null);
                rz.setMachineIp(null);
            }
            dba.save("LogCzrz", rz);
        }
    }

    /**
     * 已经进行了业务单号配置的科室将不允许再次设置
     * 一个用户不可对同一社区,同一单号类型,同一药库,同一具体操作设置两套不同的规则
     *
     * @param config 业务单号配置
     */
    private void checkAvailability(YwdhConfig config) {
        String sqbm = config.getSqbm() == null ? LoginHelper.getLoginerSQBM() : config.getSqbm();
        HashMap<String, Object> condition = new HashMap<>(4);
        condition.put("SQBM", sqbm);
        if (config.getLx() != null) {
            condition.put("LX", config.getLx());
        }
        if (config.getKslx() != null) {
            condition.put("KSLX", config.getKslx());
        }
        if (config.getJtlx() != null) {
            condition.put("JTLX", config.getJtlx());
        }

        List<Map<String, Object>> queryResult = ywdhConfigRepository.selectYwdhConfig(condition);

        if (!queryResult.isEmpty()) {
            throw new AppException("配置 lx:" + config.getLx() + " kslx:" + config.getKslx() + " jtlx:" + config.getJtlx() + " 已存在!");
        }
    }

    /**
     * 设置redis键的同时也设置他的时效性(默认采用当天有效)
     *
     * @param key   键
     * @param value 值
     */
    private void callRedisToSetKeyAndExpiration(String key, Object value) {
        Calendar calendar = new GregorianCalendar();
        // 时
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        // 分
        calendar.set(Calendar.MINUTE, 50);
        // 秒
        calendar.set(Calendar.SECOND, 0);
        // 毫秒
        calendar.set(Calendar.MILLISECOND, 0);

        redisTemplate.opsForValue().set(key, value);
        redisTemplate.expireAt(key, calendar.getTime());
    }
}

Util

NumberHelper

public interface NumberHelper {

    /**
     * 生成药库增序单号
     *
     * @param ywdhConfig 业务单号生成规则
     * @param keyName    Redis唯一键:前缀(INCR) + opcode + "_" + sqbm
     * @return 业务单号
     */
    String generateIncrementSequenceNumber(YwdhConfig ywdhConfig, String keyName);

}

NumberHelperImpl


@Service("NumberHelper")
public class NumberHelperImpl implements NumberHelper {

    @Override
    public String generateIncrementSequenceNumber(YwdhConfig config, String keyName) {
        DBAgent dbAgent = DBAgent.getInstance();

        // 单号起始号
        long dhqsh = Long.parseLong(config.getDhqsh().toString());
        if (redisTemplate.hasKey(keyName)) {
            // 单号步长
            long dhbc = Long.parseLong(config.getDhbc().toString());
            dhqsh = redisTemplate.opsForValue().increment(keyName, dhbc);
        } else {
            redisTemplate.opsForValue().set(keyName, dhqsh);
        }

        // 单号长度
        Integer dhcd = config.getDhcd();
        // 前补零操作
        String str = IntStream.range(0, dhcd).mapToObj(i -> "0").collect(Collectors.joining());
        DecimalFormat df = new DecimalFormat(str);
        String format = df.format(dhqsh);

        String s = "select BM from zd_ks where sqbm = ::SQBM and sysid = ::SYSID";
        Map<String, Object> map = new HashMap<>();
        map.put("SQBM", config.getSqbm());
        map.put("SYSID", config.getKslx());
        List<Map<String, Object>> result = dbAgent.querySQL(s, map, null);
        Object bm = result.get(0).get("BM");
        // 拼接单号,比如首单入库将可拼接为:RK0380000000000001
        return config.getDhqz() + bm + format;
    }
}

具体业务(药库入库)

    public FuncResult businessYkrk(Map dataMap) {
        DBAgent dba = DBAgent.getInstance();
        List[] ykrkd0 = (List[]) dataMap.get("YK_RKD0");
        List[] ykrkd1 = (List[]) dataMap.get("YK_RKD1");
        Map<String, Object> ykrkd = (Map<String, Object>) ykrkd0[0].get(0);
        String sqbm = LoginHelper.getLoginerSQBM();
        int czryid = Integer.parseInt(LoginHelper.getLoginerId().toString());

        // 通过配置来判断是否递增生成入库单号
        YwdhConfig config = null;
        boolean isIncrement = false;

        // 按客户端页面给的类型来筛查,寻找相应的配置。
        Map<String, Object> condi = new HashMap<>(8);
        condi.put("SQBM", sqbm);
        condi.put("CZRYID", czryid);
        condi.put("LX", YwdhOpsEnum.getLx(G3GlobalNames.YK_RKD0));
        // 药库类型
        if (ykrkd.get("KFID") != null) {
            condi.put("KSLX", ykrkd.get("KFID"));
        } else if (ykrkd.get("kfid") != null) {
            condi.put("KSLX", ykrkd.get("kfid"));
        }

        // 入库类型
        if (ykrkd.get("RKLX") != null) {
            condi.put("JTLX", ykrkd.get("RKLX"));
        } else if (ykrkd.get("rklx") != null) {
            condi.put("JTLX", ykrkd.get("rklx"));
        }

        // 获取单号的配置规则
        List<YwdhConfig> queryBeanResult =
                mapsToBeans(ywdhConfigRepository.selectYwdhConfig(condi), YwdhConfig.class, true);

        if (!queryBeanResult.isEmpty()) {
            config = queryBeanResult.get(0);
            isIncrement = Objects.equals(1, config.getIsopen());
        }

        // redis键
        String keyName = null;
        // redis键的原值
        Object originalObj = null;
        // redis键的现值
        Object presentObj = null;

        if (isIncrement) {
            // 通过配置来生成redis的key
            YwdhRedisPair pairCondi = new YwdhRedisPair(sqbm, config.getCurrenttime());
            List<YwdhRedisPair> pairSelectResult = ywdhRedisPairRepository.selectYwdhKeyConfig(pairCondi);
            YwdhRedisPair pair = pairSelectResult.get(0);
            keyName = pair.getKeyname();
            originalObj = redisTemplate.opsForValue().get(keyName);
            // redis未获取到key,从mysql进行手动恢复并为其赋值
            if (originalObj == null) {
                redisTemplate.opsForValue().set(keyName, Long.parseLong(pair.getValue()));
            }

            // 入库单号赋值替换
            List rkd0s = ykrkd0[0];
            List rkd0Flags = ykrkd0[1];
            int dataSize = rkd0s.size();
            for (int i = 0; i < dataSize; i++) {
                Object rkd0Flag = rkd0Flags.get(i);
                if (WebServiceUtils.rowOp_add.equalsIgnoreCase(rkd0Flag.toString())) {
                    Object rkd0 = rkd0s.get(i);
                    Map<String, Object> rkd = (Map<String, Object>) rkd0;
                    String presentValue = dhs.generateIncrementSequenceNumber(config, keyName);
                    if (rkd.get("RKDH") != null) {
                        rkd.put("RKDH", presentValue);
                    } else if (rkd.get("rkdh") != null) {
                        rkd.put("rkdh", presentValue);
                    }
                }
            }

            presentObj = redisTemplate.opsForValue().get(keyName);
            Integer presentVal = Integer.parseInt(presentObj.toString());

            // 生成单号之后需要修改配置信息(更新单号起始号)
            Map<String, Object> update = new HashMap<>();
            update.put("SQBM", sqbm);
            update.put("CURRENTTIME", config.getCurrenttime());
            List<YwdhConfig> needToUpdateConfigs =
                    mapsToBeans(ywdhConfigRepository.selectYwdhConfig(update),
                            YwdhConfig.class, true);
            needToUpdateConfigs.forEach(b -> b.setDhqsh(presentVal));
            dba.update(null, needToUpdateConfigs);

            // 并且修改mysql键值对
            pairSelectResult.forEach(b -> b.setValue(presentVal.toString()));
            dba.update(null, pairSelectResult);
        }

        FuncResult funcResult;
        try {
            // TODO 调用真实的入库业务
            funcResult = doRKAction(ykrkd0, ykrkd1);
        } catch (Exception e) {
            if (isIncrement) {
                // 发生异常需要将redis中设置的递增序列手动回滚
                if (originalObj == null) {
                    redisTemplate.delete(keyName);
                } else {
                    redisTemplate.opsForValue().set(keyName, originalObj);
                }
            }
            // 异常抛出用作程序本身的回滚
            throw AppException.warn(e.getMessage(), e);
        }
        return funcResult;
    }

标签:自增,get,CAS,List,Redis,null,config,dataMap,append
From: https://www.cnblogs.com/DJOSIMON/p/17192340.html

相关文章

  • 10、Redis集群模式
    1.Redis主从模式和cluster分片集群区别redis主从模式,是所有Redis数据一致,但是key过多了会影响性能cluster分片集群,可以将数据分散到多个Redis节点,数据分片存储,能够提高re......
  • 7、Redis持久化存储的两种方式
    1.Redis持久化存储的两种方式RDB方式RDB存储是Redis实现的一种存储机制(默认开启)AOF方式AOF存储方式,直接把操作的命令记录下来,保存到一个文件里,类似mysql的......
  • 8、Redis的RDB工具分析key的大小
    1.什么是Redis的大key?(BigKey)redis存储数据的时候,当某个key的值比较大(包括字符串、列表等数据类型),key的数据越大,占用的内存和空间就越多。BigKey(大key)Bigkey指的是redis......
  • 5、Redis慢日志和key有效期
    一、Redis的慢日志问题:如果有人反馈redis慢,如何进行排查?系统资源情况查看慢日志情况1.查看慢日志的默认配置>CONFIGGETslow*#查看慢日志的配......
  • 6、Redis禁用危险命令和压测工具
    1.Redis禁用危险命令Redis危险的命令有哪些?>FLUSHALL会清空Redis所有数据>FLUSHDB会清除当前DB所有数据>KEYS*在键过多的时候......
  • redis之列表、redis之hash、redis其他操作、redis 管道、django中使用redis、celery介
    目录1redis之列表2redis之hash3redis其他操作4redis管道5django中使用redis5celery介绍和安装6celery快速使用7celery包结构#1登录注册前端 -登录 -手......
  • 3、Redis库切换和运维监控命令
    一.Redis不同数据库的切换1.配置文件查看Redis有多少个库?#默认是16个[root@localhost~]#grepdatabases/etc/redis.confdatabases162.Redis数据库的切换>sel......
  • showcase专用卸载工具,完全彻底卸载删除干净showcase各种残留注册表和文件的方法和步骤
    showcase专用卸载工具,完全彻底卸载删除干净showcase各种残留注册表和文件的方法和步骤。如何卸载showcase呢?有很多同学想把showcase卸载后重新安装,但是发现showcase安装到......
  • 2、Redis常用数据类型
    1.Redis-cli使用和认证登录#客户端工具redis-cli登录redis-cli#默认127.0.0.16379redis-cli-hipredis-cli-hip-pportredis-cli-h......
  • 关于redis的击穿,穿透,雪崩
    Redis提供了一些技术手段来防止缓存击穿、缓存雪崩和缓存穿透,这些技术手段包括:缓存击穿缓存击穿是指一个不存在于缓存中的key,每次访问时都会穿透到数据库,导致数据库负......