一、前言
背景:公司新建了写字楼、公寓,买了一家公司的门锁,门锁自带有一套公寓系统。需要与本公司HR系统打通。但是后面对接发现他们系统没有暴露写入人员的接口、并且也没有读取房客的接口,所以解决办法只能是从公寓系统取到所有房间,然后再一个个房间去获取到所有人,写入到HR表中。
技术分析:该系统采用了Sqlserver数据库,Java语言,Spring Boot框架等技术进行编程实现。代码要是存在欠缺考虑,还请各位大佬不吝指教,本人不胜感激。
二、实体类Entity
1、RoomEntity实体类
//房间实体类
@Data
public class RoomEntity {
//主键
private int id;
//房间ID
private String roomId;
//房间名称
private String roomName;
//房间楼层
private String floorName;
}
2、RoomPeople实体类
@Data
public class HxPeople implements Serializable {
//ID
private Integer id;
//状态:1:正常,2:到期
private String phoneNo;
//状态:1:正常,2:到期
private Integer status;
private Long endTime;
/房间号
private String roomId;
private static final long serialVersionUID = 1L;
}
三、控制器类Controller与定时器HxScheduledTask
1、Controller控制器类
/**
* @Author :XieStrong
* @Description : 门锁工具类
* @Date :2024-02-27 13:58
*/
@RestController
@RequestMapping("/time")
public class HxLockTimngClient {
@Autowired
HttpClient httpClient;
@Autowired
HxRoomService hxRoomService;
@Autowired
HxPeopleService hxPeopleService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10), new ThreadPoolExecutor.CallerRunsPolicy());
@PostMapping("/getToken")
public String getToken() {
String token = redisTemplate.opsForValue().get("lock_token");
if(StringUtils.isBlank(token)){
JSONObject obj = new JSONObject();
JSONObject data = new JSONObject();
data.put("accountName", "15641545geg2");
data.put("password", "gewgewgwhwehb8d2ed616675");
obj.put("method", "apartmentLogin");
obj.put("data", data);
JSONObject jsonObject = httpClient.hXHttpClient(obj, "1.3");
token = JSONObject.parseObject(jsonObject.get("data").toString()).get("tokenId").toString();
redisTemplate.opsForValue().set("lock_token", token, 3600, TimeUnit.SECONDS);
}
return token;
}
/***
*
* 多线程方法,每次查十条数据,开启最大线程10进行查询,查询全部数据保存在数据库中
* @param
* @author: XieStrong
* @Date: 2024-02-28
* @return: com.alibaba.fastjson2.JSONObject
*/
@PostMapping("/saveRoomList")
public JSONObject ThreadSaveRoomList() throws ExecutionException, InterruptedException {
//通过接口获取最新的token
String token = getToken();
List<RoomEntity> roomEntities = new ArrayList<>();
for (int i = 0; i < 100; i += 10) {
//开始节点
int start = i;
Future<List<RoomEntity>> submit = executor.submit(() -> getRoomEntity(token, start));
roomEntities.addAll(submit.get());
}
System.out.println(roomEntities);
//将房间数据存到数据库中
hxRoomService.addRoomData(roomEntities);
executor.shutdown();
return new JSONObject();
}
/***
* 获取所有房间数据
* @param token TokenId
* @param start 查询开始节点
* @author: XieStrong
* @Date: 2024-02-28
* @return: java.util.List<com.ruoyi.system.domain.hx.RoomEntity>
*/
private List<RoomEntity> getRoomEntity(String token, int start) {
//构造请求参数
JSONObject obj = new JSONObject();
JSONObject data = new JSONObject();
data.put("startNum", start);
//每次取10笔数据
data.put("getNum", 10);
obj.put("method", "apartmentRoomList");
obj.put("tokenId", token);
obj.put("data", data);
JSONObject jsonObject = httpClient.hXHttpClient(obj, "1.2");
JSONObject jsonObject1 = jsonObject.getJSONObject("data");
List<RoomEntity> roomEntities = JSONArray.parseArray(jsonObject1.getString("list"), RoomEntity.class);
return roomEntities;
}
/***
*
* 根据房间ID找到所有房间里面的人保存到数据库中后面进行对比
* @param token 秘钥
* @param roomid 房间
* @author: XieStrong
* @Date: 2024-02-28
* @return: ArrayList<HxPeople>
*/
public ArrayList<HxPeople> saveKeyList(String token, String roomid) {
//构造请求参数
JSONObject obj = new JSONObject();
JSONObject data = new JSONObject();
data.put("roomId", roomid);
data.put("startNum", 0);
data.put("getNum", 50);
obj.put("method", "apartmentKeyList");
obj.put("tokenId", token);
obj.put("data", data);
JSONObject jsonObject = httpClient.hXHttpClient(obj, "1.1");
JSONObject jsonObject1 = jsonObject.getJSONObject("data");
//将接口返回的数据通过JSONArray,字段自动匹配转成对应实体类,收集成实体类的List
List<HxPeople> peopleEntities = JSONArray.parseArray(jsonObject1.getString("list"), HxPeople.class);
if (peopleEntities != null) {
//通过HashSet将房间的钥匙进行去重过滤,钥匙存在重复的
Set<HxPeople> hxPeople = new HashSet<>(peopleEntities);
//获取当前时间时间戳
Long timestamp = System.currentTimeMillis() / 1000;
//将对象的getEndTime属性与当前时间戳进行对比
hxPeople.stream().forEach(peo -> {
//小于的话说明钥匙已经到期,更改状态
if (Long.compare(peo.getEndTime(), timestamp) <= 0) {
peo.setStatus(2); //到期状态
peo.setRoomId(roomid);//房间ID
} else {
//大于的话说明钥匙正常
peo.setStatus(1);//正常状态
peo.setRoomId(roomid);
}
});
//将处理过的hxPeople对象转成ArrayList
ArrayList<HxPeople> list = new ArrayList<>(hxPeople);
//保存到数据库中
hxPeopleService.addPeopleData(list);
//有就返回
return list;
}
//没有的话返回空的ArrayList
return new ArrayList<>();
}
@PostMapping("/getPhone")
public List findPeolplebyPhone(){
List list = hxPeopleService.selectPeoplePhone();
return list;
}
@PostMapping("/delPhoneList")
public void delbyPhone(@RequestBody List<String> phoneList){
hxPeopleService.deleteByPhone(phoneList);
}
}
2、定时器HxScheduledTask
@Component
public class HxScheduledTask {
@Autowired
HxLockTimngClient hxLockTimngClient;
@Autowired
HxRoomService hxRoomService;
@Autowired
HxPeopleService hxPeopleService;
/***
*
* 定时执行任务:每5分钟执行,保存房间里面所有的钥匙
* @param
* @author: XieStrong
* @Date: 2024-02-29
* @return: void @Scheduled(cron = "0 0/5 * * * ?")
*/
// 这个方法将在项目启动时立即执行,然后每隔10秒执行一次
@Scheduled(cron = "0 0/5 * * * ?")
public void getKey() {
//创建线程
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(10), new ThreadPoolExecutor.CallerRunsPolicy());
//获取请求的token
String token = hxLockTimngClient.getToken();
//查询所有房间
List<RoomEntity> list = hxRoomService.list();
//AtomicInteger的作用:就是让程序在不加锁的时候也能保障线程安全,确保自增运算的时候是线程安全的
AtomicInteger integer = new AtomicInteger(0);
//ArrayList 作为共享变量的话,是线程不安全的,所以这里我们使用一种线程安全的 List,叫做 CopyOnWriteArrayList
CopyOnWriteArrayList<HxPeople> arrayList = new CopyOnWriteArrayList();
list.stream(). forEach(room -> {
//请求接口次数限制10秒20次,这里我们请求了15次的话沉睡10秒
if (integer.get() == 15) {
try {
//沉睡10秒
Thread.sleep(10100);
//确保自增运算的时候是线程安全的
integer.set(0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
确保自增运算的时候是线程安全的
integer.incrementAndGet();
//executor.submit()有返回值的,executor.execute()是没有返回值的
Future<ArrayList<HxPeople>> submit = executor.submit(() ->
hxLockTimngClient.saveKeyList(token, room.getRoomId()));
try {
//submit.get()拿到 executor.submit()的返回值
ArrayList<HxPeople> hxPeople = submit.get();
if (hxPeople != null) {
//不等于空的话存到CopyOnWriteArrayList中
arrayList.addAll(hxPeople);
}
}catch (Exception e){
}
});
//关闭线程
executor.shutdown();
//将arrayList对象抽取PhoneNo属性,并收集成List集合
List<String> collect = arrayList.stream().map(HxPeople::getPhoneNo).collect(Collectors.toList());
//查询数据存储的所有钥匙
List<String> peolplebyPhoneList = hxLockTimngClient.findPeolplebyPhone();
//通过stream操作,将上方两个钥匙集合进行对比。本地数据库没有,接口钥匙集合存在,说明需要将这个删掉
List<String> people = peolplebyPhoneList.stream()
.filter(peo -> !collect.contains(peo)).collect(Collectors.toList());
if (!people.isEmpty()) {
//执行删除对应的钥匙
hxLockTimngClient.delbyPhone(people);
}
}
}
四、Service接口
1、HxRoomService
public interface HxRoomService {
void addRoomData(List<RoomEntity> roomEntity);
List<RoomEntity> list();
}
2、HxPeopleService
public interface HxPeopleService{
void addPeopleData(List<HxPeople> hxPeople);
List selectPeoplePhone();
void deleteByPhone(List<String> phoneList);
}
五、ServiceImpl实现类
1、HxRoomServiceImpl
@Service
public class HxRoomServiceImpl implements HxRoomService {
@Resource
HxRoomMapper hxRoomMapper;
@Override
public void addRoomData(List<RoomEntity> roomEntity) {
hxRoomMapper.insertHxRoom(roomEntity);
}
@Override
public List<RoomEntity> list() {
return hxRoomMapper.selectList();
}
}
2、HxPeopleServiceImpl
@Service
public class HxPeopleServiceImpl implements HxPeopleService{
@Resource
HxPeopleMapper hxPeopleMapper;
@Override
public void addPeopleData(List<HxPeople> hxPeople) {
hxPeopleMapper.insertOrUpdateHxPeople(hxPeople);
}
@Override
public List selectPeoplePhone() {
List<String> list = hxPeopleMapper.selectAllPhone();
return list;
}
@Override
public void deleteByPhone(List<String> phoneList) {
hxPeopleMapper.delByPhoneList(phoneList);
}
}
六、Mapper接口
1、HxRoomMapper
@Mapper
public interface HxRoomMapper {
public void insertHxRoom(@Param("roomEntity") List<RoomEntity> roomEntity);
List<RoomEntity> selectList();
}
2、HxPeopleMapper
public interface HxPeopleMapper{
public void insertOrUpdateHxPeople(@Param("People") List<HxPeople> hxPeople);
@Select("select phone_no from hx_people")
List<String> selectAllPhone();
void delByPhoneList(@Param("PhoneList") List<String> phoneList);
}
七、Mapper.xml文件
1、HxRoomMapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.hx.HxRoomMapper">
<resultMap type="roomEntity" id="RoomEntityResult">
<id property="id" column="id"/>
<result property="roomId" column="room_id"/>
<result property="roomName" column="room_name"/>
<result property="floorName" column="floor_name"/>
</resultMap>
<insert id="insertHxRoom" parameterType="RoomEntity" useGeneratedKeys="true">
insert into hx_room(room_id,room_name,floor_name) values
<foreach collection="roomEntity" item="item" separator=",">
(
#{item.roomId},#{item.roomName},#{item.floorName}
)
</foreach>
</insert>
<select id="selectList" parameterType="roomEntity" resultType="roomEntity" resultMap="RoomEntityResult" >
select * from hx_room
</select>
</mapper>
2、HxPeopleMapper
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.system.mapper.hx.HxPeopleMapper">
<resultMap id="BaseResultMap" type="com.ruoyi.system.domain.hx.HxPeople">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="phone_no" column="phoneNo" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="INTEGER"/>
<result property="room_id" column="roomId" jdbcType="VARCHAR"/>
</resultMap>
<insert id="insertHxPeople">
insert into hx_people(phone_no,status,room_id) values
<foreach collection="People" item="item" separator=",">
(
#{item.phoneNo},#{item.status},#{item.roomId}
)
</foreach>
</insert>
<insert id="insertOrUpdateHxPeople" parameterType="java.util.List">
MERGE INTO hx_people AS target
USING (
VALUES
<foreach collection="People" item="item" separator=",">
(#{item.phoneNo}, #{item.status}, #{item.roomId})
</foreach>
) AS source (phone_no, status, room_id)
ON target.phone_no = source.phone_no
WHEN MATCHED THEN
UPDATE SET
status = source.status,
room_id = source.room_id
WHEN NOT MATCHED THEN
INSERT (phone_no, status, room_id)
VALUES (source.phone_no, source.status, source.room_id);
</insert>
<delete id="delByPhoneList" parameterType="java.util.List">
DELETE hx_people WHERE phone_no IN
<foreach collection="PhoneList" item="item" open="(" close=")" separator="," >
#{item}
</foreach>
</delete>
</mapper>