package com.xy.service; import cn.hutool.http.HttpRequest; import cn.hutool.json.JSONArray; import cn.hutool.json.JSONObject; import cn.hutool.json.JSONUtil; import com.alibaba.excel.EasyExcel; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.read.listener.ReadListener; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.github.yitter.idgen.YitIdHelper; import com.xy.collections.list.JArrayList; import com.xy.collections.list.JList; import com.xy.collections.map.JHashMap; import com.xy.collections.map.JMap; import com.xy.config.DeviceThreadPoolConfig; import com.xy.dto.CommandMqtt; import com.xy.dto.DeviceAnnualFeeDto; import com.xy.dto.MqttUserDto; import com.xy.dto.be.MercDto; import com.xy.entity.*; import com.xy.error.CommRuntimeException; import com.xy.mapper.DeviceAnnualFeeMapper; import com.xy.service.be.MercService; import com.xy.utils.*; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.AllArgsConstructor; import lombok.Data; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import java.io.InputStream; import java.io.OutputStream; import java.time.LocalDateTime; import java.util.*; import static com.xy.utils.PlusBeans.toIPage; import static com.xy.utils.PlusBeans.toPageBean; /** *

* 设备年费表 服务实现类 *

* * @author lijin * @since 2025-02-28 */ @Slf4j @Service @AllArgsConstructor(onConstructor_ = @Lazy) @Api(tags = "设备年费表") public class DeviceAnnualFeeServiceImpl extends ServiceImpl implements DeviceAnnualFeeService { private DeviceCreateIdsServiceImpl deviceCreateIdsService; private DeviceRegisterServiceImpl deviceRegisterService; private DeviceInfoServiceImpl deviceInfoService; private DeviceSysinfoServiceImpl deviceSysinfoService; private DeviceStatusServiceImpl deviceStatusService; private DevicePartServiceImpl devicePartService; private MqttUserServiceImpl mqttUserService; private MercService mercService; private MqttService mqttService; @PostMapping("check") @ApiOperation("检查") public R check(@RequestBody @Validated DeviceAnnualFeeDto.Check check) { log.info("独立部署检查:{},{},运行环境:{}", check.getDeviceId(), check.getSn(), check.getRunPlatform()); Long deviceId = check.getDeviceId(); String sn = check.getSn(); if (!Emptys.check(deviceId) && !Emptys.check(sn)) { throw new CommRuntimeException("deviceId和sn必须传入一个"); } if (!Emptys.check(deviceId)) { DeviceSysinfo deviceSysinfo = deviceSysinfoService.getOne(new LambdaQueryWrapper() .eq(DeviceSysinfo::getDeviceSn, sn) ); if (deviceSysinfo == null) { throw new CommRuntimeException("sn未查找到设备"); } deviceId = deviceSysinfo.getDeviceId(); } DeviceAnnualFee deviceAnnualFee = getOne(new LambdaQueryWrapper() .eq(DeviceAnnualFee::getDeviceId, deviceId) .eq(DeviceAnnualFee::getIsThis, true) ); if (deviceAnnualFee == null) { return R.fail("设备未分配到年费商家"); } deviceAnnualFee.setRunPlatform(check.getRunPlatform()); updateById(deviceAnnualFee); String timeout = DataTime.toString(deviceAnnualFee.getTimeout()); int i = DataTime.stringContrast(timeout, DataTime.getSring()); if (i < 0) { return R.fail("已欠费"); } return R.ok(timeout); } @PostMapping("page") @ApiOperation("分页查询") public R> page(@RequestBody DeviceAnnualFeeDto.Page page) { LambdaQueryWrapper lambdaQueryWrapper = new MybatisPlusQuery() .eqWrapper(page, DeviceAnnualFee.class) .ge(DeviceAnnualFee::getTimeout, page.getBeginTime()) .le(DeviceAnnualFee::getTimeout, page.getEndTime()) .build(); IPage iPage = page(toIPage(page.getPage()), lambdaQueryWrapper); PageBean pageBean = toPageBean(DeviceAnnualFeeDto.Vo.class, iPage); List records = pageBean.getRecords(); if (Emptys.check(records)) { List mercs = mercService.list(new MercDto.SelectList().setMercIds(new JArrayList<>(records).getProperty(DeviceAnnualFeeDto.Vo::getMercId).comparing())).getData(); JMap cover = new JArrayList<>(mercs).toMap(MercDto.Vo::getId).cover(); Map stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee"); for (DeviceAnnualFeeDto.Vo record : records) { MercDto.Vo merc = cover.get(record.getMercId()); SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(record.getMercId())); JSONObject jsonObject = JSONUtil.parseObj(sysDictRedis.getValue()); record.setMoney(jsonObject.getInt("money")) .setMercName(merc.getName()); } } return R.ok(pageBean); } @PostMapping("mercPage") @ApiOperation("商家分页查询(无token)") public R> mercPage(@RequestBody DeviceAnnualFeeDto.Page page) { if (!Emptys.check(page.getCode())) { return R.fail("code不能为空"); } Map stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee"); SysDictRedis sysDictRedis = stringSysDictRedisMap.get(page.getCode()); if (sysDictRedis == null) { return R.fail("code未配置商家"); } page.setMercId(Long.valueOf(sysDictRedis.getValue())).setRegisterStatus(true); return page(page); } @SneakyThrows @ApiOperation("导出年费模版") @PostMapping("download") public void download(HttpServletResponse response) { InputStream inputStream = IoUtils.inputStream("device_annual_fee_templet.xlsx").get(); response.setHeader("Content-Disposition", "attachment; filename=" + "device_annual_fee_templet.xlsx"); response.setContentType("application/xlsx"); byte[] buffer = new byte[1024]; int bytesRead; OutputStream outputStream = response.getOutputStream(); while ((bytesRead = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, bytesRead); } inputStream.close(); outputStream.close(); } @SneakyThrows @ApiOperation("导入年费数据") @PostMapping("upload") public R upload(@RequestParam("file") MultipartFile file) { EasyExcel.read(file.getInputStream(), UploadInfo.class, new UploadListener(this)).sheet().doRead(); return R.ok(); } @PostMapping("sendDeviceInfo") @ApiOperation("推送设备到商家平台(无token)") public R sendDeviceInfo(@RequestBody @Validated DeviceAnnualFeeDto.SendDeviceInfo sendDeviceInfo) { List deviceIds = sendDeviceInfo.getDeviceIds(); //设备年费信息 List deviceAnnualFees = list(new LambdaQueryWrapper() .in(DeviceAnnualFee::getDeviceId, deviceIds) .eq(DeviceAnnualFee::getIsThis, true) ); JMap deviceAnnualFeesJMaps = new JArrayList<>(deviceAnnualFees).toMap(DeviceAnnualFee::getDeviceId).cover(); //设备id创建信息 List deviceCreateIdss = deviceCreateIdsService.listByIds(deviceIds); JMap deviceCreateIdsJMaps = new JArrayList<>(deviceCreateIdss).toMap(DeviceCreateIds::getDeviceId).cover(); //设备注册信息 List deviceRegisters = deviceRegisterService.listByIds(deviceIds); JMap deviceRegistersJMaps = new JArrayList<>(deviceRegisters).toMap(DeviceRegister::getDeviceId).cover(); //设备信息 List deviceInfos = deviceInfoService.listByIds(deviceIds); JMap deviceInfosJMaps = new JArrayList<>(deviceInfos).toMap(DeviceInfo::getDeviceId).cover(); //设备系统信息 List deviceSysinfos = deviceSysinfoService.listByIds(deviceIds); JMap deviceSysinfosJMaps = new JArrayList<>(deviceSysinfos).toMap(DeviceSysinfo::getDeviceId).cover(); //设备状态信息 List deviceStatuses = deviceStatusService.listByIds(deviceIds); JMap deviceStatusesJMaps = new JArrayList<>(deviceStatuses).toMap(DeviceStatus::getDeviceId).cover(); //设备配件信息 List devicePartss = devicePartService.list(new LambdaQueryWrapper() .in(DevicePart::getDeviceId, deviceIds) ); JMap> devicePartsJMaps = new JHashMap<>(); if (Emptys.check(devicePartss)) { devicePartsJMaps = new JArrayList<>(devicePartss).toMap(DevicePart::getDeviceId).group(); } JMap> finalDevicePartsJMaps = devicePartsJMaps; //mqtt连接信息 List mqttUsers = mqttUserService.list(new LambdaQueryWrapper() .in(MqttUser::getDeviceId, deviceIds) ); JMap mqttUsersJMaps = new JArrayList<>(mqttUsers).toMap(MqttUser::getDeviceId).cover(); //字典信息 Map stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee"); //组装请求对象 FunctionUtils.NoParamsResult> getJsonObjects = () -> { Map jsonObjects = new HashMap<>(); for (Long deviceId : deviceIds) { JSONObject jsonObject = new JSONObject() .set("deviceId", deviceId); //设备id创建信息 DeviceCreateIds deviceCreateIds = deviceCreateIdsJMaps.get(deviceId) .setDeviceQrMercId(null); JSONObject deviceCreateIdsObj = JSONUtil.parseObj(deviceCreateIds) .set("bindTime", DataTime.toString(deviceCreateIds.getBindTime())) .set("createTime", DataTime.toString(deviceCreateIds.getCreateTime())); if (deviceCreateIds.getLastExportQrCodeTime() != null) { deviceCreateIdsObj.set("lastExportQrCodeTime", DataTime.toString(deviceCreateIds.getLastExportQrCodeTime())); } jsonObject.set("deviceCreateIds", deviceCreateIdsObj); //设备注册信息 DeviceRegister deviceRegister = deviceRegistersJMaps.get(deviceId); JSONObject deviceRegisterObj = JSONUtil.parseObj(deviceRegister) .set("createTime", DataTime.toString(deviceRegister.getCreateTime())); jsonObject.set("deviceRegister", deviceRegisterObj); //设备信息 DeviceInfo deviceInfo = deviceInfosJMaps.get(deviceId); SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(deviceInfo.getMercId())); JSONObject obj = JSONUtil.parseObj(sysDictRedis.getValue()); deviceInfo.setMercId(obj.getLong("target_merc_id")).setMercCode(obj.getStr("target_merc_code")); JSONObject deviceInfoObj = JSONUtil.parseObj(deviceInfo) .set("createTime", DataTime.toString(deviceInfo.getCreateTime())); if (deviceInfo.getUpdateTime() != null) { deviceInfoObj.set("updateTime", DataTime.toString(deviceInfo.getUpdateTime())); } if (deviceInfo.getActiveTime() != null) { deviceInfoObj.set("activeTime", DataTime.toString(deviceInfo.getActiveTime())); } jsonObject.set("deviceInfo", deviceInfoObj); //设备系统信息 DeviceSysinfo deviceSysinfo = deviceSysinfosJMaps.get(deviceId); JSONObject deviceSysinfoObj = JSONUtil.parseObj(deviceSysinfo); if (deviceSysinfo.getCreateTime() != null) { deviceSysinfoObj.set("createTime", DataTime.toString(deviceSysinfo.getCreateTime())); } jsonObject.set("deviceSysinfo", deviceSysinfoObj); //设备状态信息 DeviceStatus deviceStatus = deviceStatusesJMaps.get(deviceId); JSONObject deviceStatusObj = JSONUtil.parseObj(deviceStatus) .set("netState", 2); if (deviceStatus.getUpdateTime() != null) { deviceStatusObj.set("updateTime", DataTime.toString(deviceStatus.getUpdateTime())); } if (deviceStatus.getSysStartTime() != null) { deviceStatusObj.set("sysStartTime", DataTime.toString(deviceStatus.getSysStartTime())); } if (deviceStatus.getSysCurTime() != null) { deviceStatusObj.set("sysCurTime", DataTime.toString(deviceStatus.getSysCurTime())); } jsonObject.set("deviceStatus", deviceStatusObj); //设备配件信息 List deviceParts = finalDevicePartsJMaps.get(deviceId); if (Emptys.check(deviceParts)) { List devicePartObjs = new ArrayList<>(deviceParts.size()); for (DevicePart devicePart : deviceParts) { JSONObject devicePartObj = JSONUtil.parseObj(devicePart) .set("createTime", DataTime.toString(devicePart.getCreateTime())); if (devicePart.getUpdateTime() != null) { devicePartObj.set("updateTime", DataTime.toString(devicePart.getUpdateTime())); } devicePartObjs.add(devicePartObj); } jsonObject.set("deviceParts", devicePartObjs); } //mqtt连接信息 MqttUser mqttUser = mqttUsersJMaps.get(deviceId); JSONObject mqttUserObj = JSONUtil.parseObj(mqttUser) .set("created", DataTime.toString(mqttUser.getCreated())) .set("id", null); jsonObject.set("mqttUser", mqttUserObj); jsonObjects.put(deviceId, jsonObject); } return jsonObjects; }; //发送请求 FunctionUtils.ParamsNoResult> sendPost = jsonObjects -> { LocalDateTime now = LocalDateTime.now(); Map map = SysDictUtils.get("mqtt_cmd_templet_task"); String value = map.get("env").getValue(); ThreadPoolUtils.Execute execute = ThreadPoolUtils.excPoll(DeviceThreadPoolConfig.DEVICE_COMMON_POLL, deviceIds.size()); //多线程逐条发送,发送集合担心参数体过大 deviceIds.forEach(deviceId -> execute.execute(() -> { DeviceAnnualFee deviceAnnualFee = deviceAnnualFeesJMaps.get(deviceId); if (deviceAnnualFee.getRegisterStatus()) { return; } SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(deviceAnnualFee.getMercId())); JSONObject obj = JSONUtil.parseObj(sysDictRedis.getValue()); String url = obj.getStr("domain"); JSONObject jsonObject = jsonObjects.get(deviceId); JSONObject result = send(url + "/device/device-annual-fee/receiveDeviceInfo", jsonObject, "发送设备数据到商家平台"); //修改设备年费注册信息 if (result.getInt("code") == 200) { deviceAnnualFee.setRegisterStatus(true) .setRegisterTime(now) .setTimeout(now.plusDays(10)); updateById(deviceAnnualFee); //发送环境切换指令 JSONObject env = JSONUtil.parseObj(value); JSONObject data = env.getJSONObject("data") .set("env", obj.getStr("env")); env.set("data", data); CommandMqtt commandMqtt = new CommandMqtt() .setDeviceId(deviceId) .setTemplet(env); mqttService.senCommand(Collections.singletonList(commandMqtt)); } }) .error(e -> log.error("注册报错了:", e)) .end() ); }; Map jsonObjects = getJsonObjects.run(); sendPost.run(jsonObjects); return R.ok(); } @PostMapping("receiveDeviceInfo") @ApiOperation("接收兴元设备数据(无token)") @Transactional(rollbackFor = Exception.class) public R receiveDeviceInfo(@RequestBody Map map) { //设备id创建信息 FunctionUtils.NoParamsNoResult deviceCreateIdsFun = () -> { JSONObject deviceCreateIdsSJson = JSONUtil.parseObj(map.get("deviceCreateIds")); DeviceCreateIds deviceCreateIds = deviceCreateIdsSJson.toBean(DeviceCreateIds.class); DeviceCreateIds byId = deviceCreateIdsService.getById(deviceCreateIds.getDeviceId()); if (byId != null) { return; } deviceCreateIdsService.save(deviceCreateIds); }; //设备注册信息 FunctionUtils.NoParamsNoResult deviceRegisterFun = () -> { JSONObject deviceRegisterJson = JSONUtil.parseObj(map.get("deviceRegister")); DeviceRegister deviceRegister = deviceRegisterJson.toBean(DeviceRegister.class); DeviceRegister byId = deviceRegisterService.getById(deviceRegister.getDeviceId()); if (byId != null) { return; } deviceRegisterService.save(deviceRegister); }; //设备信息 FunctionUtils.NoParamsNoResult deviceInfoFun = () -> { JSONObject deviceInfoJson = JSONUtil.parseObj(map.get("deviceInfo")); DeviceInfo deviceInfo = deviceInfoJson.toBean(DeviceInfo.class); DeviceInfo byId = deviceInfoService.getById(deviceInfo.getDeviceId()); if (byId != null) { return; } MercDto.Vo merc = mercService.obj(new MercDto.Vo().setId(deviceInfo.getMercId())).getData(); deviceInfo.setMercName(merc.getName()); deviceInfoService.save(deviceInfo); }; //设备系统信息 FunctionUtils.NoParamsNoResult deviceSysinfoFun = () -> { JSONObject deviceSysinfoJson = JSONUtil.parseObj(map.get("deviceSysinfo")); DeviceSysinfo deviceSysinfo = deviceSysinfoJson.toBean(DeviceSysinfo.class); DeviceSysinfo byId = deviceSysinfoService.getById(deviceSysinfo.getDeviceId()); if (byId != null) { return; } deviceSysinfoService.save(deviceSysinfo); }; //设备状态信息 FunctionUtils.NoParamsNoResult deviceStatusFun = () -> { JSONObject deviceStatusJson = JSONUtil.parseObj(map.get("deviceStatus")); DeviceStatus deviceStatus = deviceStatusJson.toBean(DeviceStatus.class); DeviceStatus byId = deviceStatusService.getById(deviceStatus.getDeviceId()); if (byId != null) { return; } deviceStatusService.save(deviceStatus); }; //设备配件信息 FunctionUtils.NoParamsNoResult devicePartsFun = () -> { JSONArray array = JSONUtil.parseArray(map.get("deviceParts")); List deviceParts = new ArrayList<>(); for (Object o : array) { JSONObject devicePartsJson = JSONUtil.parseObj(o); DevicePart devicePart = devicePartsJson.toBean(DevicePart.class) .setId(YitIdHelper.nextId()); DevicePart one = devicePartService.getOne(new LambdaQueryWrapper() .eq(DevicePart::getDeviceId, devicePart.getDeviceId()) .eq(DevicePart::getCode, devicePart.getCode()) ); if (one == null) { deviceParts.add(devicePart); } } if (Emptys.check(deviceParts)) { devicePartService.saveBatch(deviceParts); } }; //mqtt连接信息 FunctionUtils.NoParamsNoResult mqttUserFun = () -> { JSONObject mqttUserJson = JSONUtil.parseObj(map.get("mqttUser")); MqttUser mqttUser = mqttUserJson.toBean(MqttUser.class); MqttUser one = mqttUserService.getOne(new LambdaQueryWrapper() .eq(MqttUser::getDeviceId, mqttUser.getDeviceId()) .eq(MqttUser::getSn, mqttUser.getSn()) ); if (one != null) { return; } MqttUserDto.Save mqttUserInfo = Beans.copy(MqttUserDto.Save.class, mqttUser); mqttUserService.save(mqttUserInfo); }; long deviceId = Long.parseLong(map.get("deviceId").toString()); return new LockUtils2().name("receiveDeviceInfo_" + deviceId) .success(() -> { deviceCreateIdsFun.run(); deviceRegisterFun.run(); deviceInfoFun.run(); deviceSysinfoFun.run(); deviceStatusFun.run(); devicePartsFun.run(); mqttUserFun.run(); return R.ok(); }); } @PostMapping("evn") @ApiOperation("切换环境(无token)") public R evn(@RequestBody @Validated DeviceAnnualFeeDto.Evn evn) { DeviceInfo deviceInfo = deviceInfoService.getById(evn.getDeviceId()); Map stringSysDictRedisMap = SysDictUtils.get("merc_device_annual_fee"); SysDictRedis sysDictRedis = stringSysDictRedisMap.get(String.valueOf(deviceInfo.getMercId())); JSONObject obj = JSONUtil.parseObj(sysDictRedis.getValue()); //切换到商家系统 if (evn.getType() == 2) { Map map = SysDictUtils.get("mqtt_cmd_templet_task"); //发送环境切换指令 JSONObject env = JSONUtil.parseObj(map.get("env").getValue()); JSONObject data = env.getJSONObject("data") .set("env", obj.getStr("env")); env.set("data", data); CommandMqtt commandMqtt = new CommandMqtt() .setDeviceId(evn.getDeviceId()) .setTemplet(env); mqttService.senCommand(Collections.singletonList(commandMqtt)); } //切换到兴元系统 if (evn.getType() == 1) { String url = obj.getStr("domain"); HttpUtils.send(url + "/device/device-annual-fee/evnXy", new JSONObject().set("deviceId", deviceInfo.getDeviceId()), "切换到兴元系统"); } return R.ok(); } @PostMapping("evnXy") @ApiOperation("切换环境到兴元(无token)") public R evnXy(@RequestBody DeviceAnnualFeeDto.Evn evn) { //发送环境切换指令 Map map = SysDictUtils.get("mqtt_cmd_templet_task"); JSONObject env = JSONUtil.parseObj(map.get("env").getValue()); JSONObject data = env.getJSONObject("data") .set("env", "prod"); env.set("data", data); CommandMqtt commandMqtt = new CommandMqtt() .setDeviceId(evn.getDeviceId()) .setTemplet(env); mqttService.senCommand(Collections.singletonList(commandMqtt)); return R.ok(); } private JSONObject send(String url, JSONObject params, String msg) { return send(url, params, msg, 5000); } private JSONObject send(String url, JSONObject params, String msg, int timeout) { try { String body = params.toString(); log.info("{}请求参数:{}", msg, body); String result = HttpRequest.post(url) .body(body) .timeout(timeout) .execute() .body(); log.info("{}响应参数:{}", msg, result); return JSONUtil.parseObj(result); } catch (Exception e) { log.error("", e); return new JSONObject().set("code", 500); } } @Slf4j @RequiredArgsConstructor public static class UploadListener implements ReadListener { private final DeviceAnnualFeeServiceImpl deviceAnnualFeeService; private JList uploadInfos = new JArrayList<>(); /** * 这个每一条数据解析都会来调用 * * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()} * @param context */ @Override public void invoke(UploadInfo data, AnalysisContext context) { uploadInfos.add(data); } /** * 所有数据解析完成了 都会来调用 * * @param context */ @Override public void doAfterAllAnalysed(AnalysisContext context) { if (!Emptys.check(uploadInfos)) { throw new RuntimeException("导入不能为空"); } List deviceAnnualFees = deviceAnnualFeeService.list(new LambdaQueryWrapper() .in(DeviceAnnualFee::getDeviceId, uploadInfos.getProperty(UploadInfo::getDeviceId)) .eq(DeviceAnnualFee::getIsThis, true) ); if (!Emptys.check(deviceAnnualFees)) { throw new RuntimeException("设备不存在"); } JMap cover = new JArrayList<>(deviceAnnualFees).toMap(DeviceAnnualFee::getDeviceId).cover(); LocalDateTime now = LocalDateTime.now(); List updates = new ArrayList<>(uploadInfos.size()); for (UploadInfo uploadInfo : uploadInfos) { DeviceAnnualFee deviceAnnualFee = cover.get(uploadInfo.getDeviceId()); if (deviceAnnualFee == null) { throw new RuntimeException("设备" + uploadInfo.getDeviceId() + "不存在"); } deviceAnnualFee.setTimeout(DataTime.toLocal(uploadInfo.getTimeout() + " 23:59:59")); if (deviceAnnualFee.getFirstTime() == null) { deviceAnnualFee.setFirstTime(now); } updates.add(deviceAnnualFee); } deviceAnnualFeeService.updateBatchById(updates); } } @Data public static class UploadInfo { @ExcelProperty(value = "设备ID") private Long deviceId; @ExcelProperty(value = "到期时间(yyyy-MM-dd)") private String timeout; } }