Redis + CAS,配置自增单号

Redis + CAS(乐观锁),配置自增业务单号

Posted by Deng Jia on 2023-03-08

DB 脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
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(业务单号配置)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
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键值对)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<?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(业务单号操作,枚举类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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(业务单号配置 业务接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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(业务单号配置 业务类)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
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

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface NumberHelper {

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

}

NumberHelperImpl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

@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;
}
}

具体业务(药库入库)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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;
}