From 37a72958f128427a7c5f84da52d45bbcc8156367 Mon Sep 17 00:00:00 2001 From: 18792927508 <1322446236@qq.com> Date: Fri, 5 Jan 2024 15:34:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/common/CommonController.java | 54 +++- .../wisdomarbitrate/TemplateController.java | 59 ----- .../ruoyi/common/annotation/DictConvert.java | 25 ++ .../ruoyi/common/annotation/EnumsConvert.java | 24 ++ .../common/core/domain/entity/SysUser.java | 12 + .../com/ruoyi/common/enums/YesOrNoEnum.java | 32 ++- .../handler/ResponseBodyAdviceHandler.java | 236 ++++++++++++++++++ .../common/interfaces/EnumsInterface.java | 38 +++ .../service/ITemplateService.java | 16 -- .../service/impl/TemplateServiceImpl.java | 54 ---- 10 files changed, 414 insertions(+), 136 deletions(-) delete mode 100644 ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/TemplateController.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictConvert.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/annotation/EnumsConvert.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/handler/ResponseBodyAdviceHandler.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/interfaces/EnumsInterface.java delete mode 100644 ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/ITemplateService.java delete mode 100644 ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/TemplateServiceImpl.java diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java index cec5006..e325a15 100644 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java +++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java @@ -4,14 +4,16 @@ import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; + +import com.ruoyi.common.utils.SecurityUtils; +import com.ruoyi.wisdomarbitrate.domain.CaseApplication; +import com.ruoyi.wisdomarbitrate.domain.CaseAttach; +import com.ruoyi.wisdomarbitrate.mapper.CaseAttachMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import com.ruoyi.common.config.RuoYiConfig; import com.ruoyi.common.constant.Constants; @@ -34,6 +36,8 @@ public class CommonController @Autowired private ServerConfig serverConfig; + @Autowired + private CaseAttachMapper caseAttachMapper; private static final String FILE_DELIMETER = ","; @@ -73,7 +77,7 @@ public class CommonController * 通用上传请求(单个) */ @PostMapping("/upload") - public AjaxResult uploadFile(MultipartFile file) throws Exception + public AjaxResult uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("annexType") Integer annexType, @RequestParam("id") Long id) throws Exception { try { @@ -82,6 +86,7 @@ public class CommonController // 上传并返回新文件名称 String fileName = FileUploadUtils.upload(filePath, file); String url = serverConfig.getUrl() + fileName; + saveCaseAttach(id, annexType, fileName,file.getOriginalFilename()); AjaxResult ajax = AjaxResult.success(); ajax.put("url", url); ajax.put("fileName", fileName); @@ -95,11 +100,47 @@ public class CommonController } } + /** + * 保存到案件附件表 + * @param id + * @param annexType + * @param fileName + * @param originalFilename + */ + private void saveCaseAttach(Long id, Integer annexType, String fileName, String originalFilename) { + CaseAttach caseAttach = CaseAttach.builder().caseAppliId(id) + .annexName(originalFilename) + .annexPath(fileName) + .annexType(annexType) + .userId(SecurityUtils.getUserId()) + .userName(SecurityUtils.getUsername()) + .build(); + caseAttachMapper.save(caseAttach); + } + /** + * 根据案件id获取附件 + * @param caseAppliId + * @param annexTypeList + * @param + * @return + */ + @GetMapping("/fileList") + public AjaxResult fileList(@RequestParam("caseAppliId")Long caseAppliId, @RequestParam(value = "annexTypeList",required = false) List annexTypeList){ + if(caseAppliId==null){ + return AjaxResult.error("案件id不能为空"); + } + CaseApplication caseApplication = new CaseApplication(); + caseApplication.setId(caseAppliId); + caseApplication.setAnnexTypeList(annexTypeList); + List caseAttachList = caseAttachMapper.queryCaseAttachList(caseApplication); + return AjaxResult.success(caseAttachList); + } + /** * 通用上传请求(多个) */ @PostMapping("/uploads") - public AjaxResult uploadFiles(List files) throws Exception + public AjaxResult uploadFiles(@RequestParam("files") MultipartFile[] files, @RequestParam("annexType")Integer annexType, @RequestParam("id")Long id ) throws Exception { try { @@ -118,6 +159,7 @@ public class CommonController fileNames.add(fileName); newFileNames.add(FileUtils.getName(fileName)); originalFilenames.add(file.getOriginalFilename()); + saveCaseAttach(id, annexType, fileName,file.getOriginalFilename()); } AjaxResult ajax = AjaxResult.success(); ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER)); diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/TemplateController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/TemplateController.java deleted file mode 100644 index 78b5db1..0000000 --- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/wisdomarbitrate/TemplateController.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.ruoyi.web.controller.wisdomarbitrate; - -import com.ruoyi.common.core.controller.BaseController; -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.common.core.page.TableDataInfo; -import com.ruoyi.wisdomarbitrate.domain.DeptIdentify; -import com.ruoyi.wisdomarbitrate.domain.TemplateManual; -import com.ruoyi.wisdomarbitrate.service.ITemplateService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping("/template") -public class TemplateController extends BaseController { - @Autowired - private ITemplateService templateService; - /** - * 新增模板 - * @param templateManual - * @return - */ - @PostMapping("/insert") - public AjaxResult insertTemplate(@RequestBody TemplateManual templateManual){ - return templateService.insertTemplate(templateManual); - } - - /** - * 删除模板 - * @param id - * @return - */ - @DeleteMapping("/delete") - public AjaxResult deleteTemplate(Long id){ - return templateService.deleteTemplate(id); - } - - /** - * 修改模板 - * @param templateManual - * @return - */ - @PutMapping("/update") - public AjaxResult updateTemplate(@RequestBody TemplateManual templateManual){ - return templateService.updateTemplate(templateManual); - } - - /** - * 查询模板 - */ - @GetMapping("/list") - public TableDataInfo list( TemplateManual templateManual) { - startPage(); - List list = templateService.selectTemplate(templateManual); - return getDataTable(list); - } - -} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictConvert.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictConvert.java new file mode 100644 index 0000000..1a99e6d --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/DictConvert.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.annotation; + +import java.lang.annotation.*; +/** + * 查询时字典转换注解 支持的返回类型:PageRes,List,VO类 + * @Author wangqiong + * @Date 2023/01/5 + * @Version V1.0 + */ +// 标注这个类它可以标注的位置 +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +// 标注这个注解的注解保留时期 +@Retention(RetentionPolicy.RUNTIME) +// 是否生成注解文档 +@Documented +public @interface DictConvert { + //转换字典的目标字段,不填时默认为注解的字段本身 + String targetField() default ""; + //是否多选 + boolean isMultiple() default false; + //分隔符 默认为逗号 + String separator() default ","; + // 字典类型 + String dictType() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/annotation/EnumsConvert.java b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/EnumsConvert.java new file mode 100644 index 0000000..219b9fb --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/annotation/EnumsConvert.java @@ -0,0 +1,24 @@ +package com.ruoyi.common.annotation; + + +import com.ruoyi.common.interfaces.EnumsInterface; + +import java.lang.annotation.*; +/** + * 枚举转义 + * @Author wangqiong + * @Date 2023/01/5 + * @Version V1.0 + */ +// 标注这个类它可以标注的位置 +@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) +// 标注这个注解的注解保留时期 +@Retention(RetentionPolicy.RUNTIME) +// 是否生成注解文档 +@Documented +public @interface EnumsConvert { + Class getEnumsInterface(); + + //转换字典的目标字段,不填时默认为注解的字段本身 + String targetField() default ""; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java index 4fd0af0..c7e0d81 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/core/domain/entity/SysUser.java @@ -3,6 +3,8 @@ package com.ruoyi.common.core.domain.entity; import java.util.Date; import java.util.List; import javax.validation.constraints.*; + +import com.ruoyi.common.annotation.DictConvert; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.ruoyi.common.annotation.Excel; @@ -64,7 +66,9 @@ public class SysUser extends BaseEntity /** 用户性别 */ @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知") + @DictConvert(targetField = "sexName",dictType = "sys_user_sex") private String sex; + private String sexName; /** 用户头像 */ private String avatar; @@ -141,6 +145,14 @@ public class SysUser extends BaseEntity return deptId; } + public String getSexName() { + return sexName; + } + + public void setSexName(String sexName) { + this.sexName = sexName; + } + public void setDeptId(Long deptId) { this.deptId = deptId; diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/enums/YesOrNoEnum.java b/ruoyi-common/src/main/java/com/ruoyi/common/enums/YesOrNoEnum.java index 412c338..4e3c2d8 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/enums/YesOrNoEnum.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/enums/YesOrNoEnum.java @@ -1,11 +1,13 @@ package com.ruoyi.common.enums; +import com.ruoyi.common.interfaces.EnumsInterface; + /** * @author wangqiong * @description 是否枚举 * @date 2023-11-17 14:05 */ -public enum YesOrNoEnum +public enum YesOrNoEnum implements EnumsInterface { NO(0, "否"), YES(1, "是"), @@ -30,4 +32,32 @@ public enum YesOrNoEnum { return text; } + + /** + * 根据code获取text + * @param codeNo + * @return + */ + public static String getTextByCode(Integer codeNo){ + for (YesOrNoEnum value : YesOrNoEnum.values()) { + if (value.getCode().equals(codeNo)){ + return value.getText(); + } + } + return codeNo.toString(); + } + + /** + * 根据text获取code + * @param textStr + * @return + */ + public static String getCodeByText(String textStr){ + for (YesOrNoEnum value : YesOrNoEnum.values()) { + if (value.getText().equals(textStr)){ + return value.getText(); + } + } + return textStr; + } } diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/handler/ResponseBodyAdviceHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/handler/ResponseBodyAdviceHandler.java new file mode 100644 index 0000000..bb8c126 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/handler/ResponseBodyAdviceHandler.java @@ -0,0 +1,236 @@ +package com.ruoyi.common.handler; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.ruoyi.common.annotation.DictConvert; +import com.ruoyi.common.annotation.EnumsConvert; +import com.ruoyi.common.core.domain.AjaxResult; +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.common.interfaces.EnumsInterface; +import com.ruoyi.common.utils.DictUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.MethodParameter; +import org.springframework.http.MediaType; +import org.springframework.http.server.ServerHttpRequest; +import org.springframework.http.server.ServerHttpResponse; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; + + +/** + * 响应数据统一处理 + * + * @Author wangqiong + * @Date 2023/1/5 + * @Version V1.0 + */ +@Slf4j +@RestControllerAdvice +public class ResponseBodyAdviceHandler implements ResponseBodyAdvice { + @Override + public boolean supports(MethodParameter methodParameter, Class aClass) { + return true; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest request, ServerHttpResponse serverHttpResponse) { + if (body instanceof String) { + ObjectMapper om = new ObjectMapper(); + try { + return om.writeValueAsString(AjaxResult.success(body)); + } catch (JsonProcessingException e) { + log.error("JsonProcessingException e:{}", e); + } + } + //判断类型,然后转换字典 + turnType(body); + AjaxResult result = AjaxResult.success(body); + return result; + } + + /** + * 判断类型,然后转换字典 + * + * @param body + */ + private void turnType(Object body) { + if (body instanceof AjaxResult) { + AjaxResult result = (AjaxResult) body; + if(result.isSuccess()){ + Object data = result.get("data"); + if(data!=null){ + if(data instanceof List){ + List list = (List) data; + convertList(list); + }else if(data instanceof TableDataInfo){ + TableDataInfo pageRes = (TableDataInfo) data; + List list = pageRes.getRows(); + convertList(list); + }else{ + loopTurn(data); + } + } + } + } else if (body instanceof TableDataInfo) { + TableDataInfo pageRes = (TableDataInfo) body; + List list = pageRes.getRows(); + convertList(list); + } else if (body instanceof List) { + List list = (List) body; + convertList(list); + } else { + loopTurn(body); + } + } + + /** + * 递归转换列表数据,将列表套列表数据都进行转换 + * + * @param list 列表数据 + */ + private void convertList(List list) { + if (list != null && list.size() > 0) { + for (Object o : list) { + if (o instanceof List) { + convertList((List) o); + } else { + loopTurn(o); + } + } + } + } + + /** + * 递归转换枚举、字典 + * + * @param obj 属性 + */ + private void loopTurn(Object obj) { + if (obj != null) { + Class clazz = obj.getClass(); + //获取类的所有字段 + for (Field field : clazz.getDeclaredFields()) { + Object value = null; + try { + field.setAccessible(true); + //获取属性值 + value = field.get(obj); + } catch (IllegalAccessException e) { + log.error("turnDict getField Value error!", e); + } + //List属性递归每个子集 + if (value != null && value instanceof List) { + for (Object child : (List) value) { + loopTurn(child); + } + //非List进行显示转换 + } else { + turnEnum(obj, clazz, field, value); + turnDict(obj, clazz, field, value); + + } + } + } + } + + /** + * 转换枚举类型 + * + * @param obj 属性判断 + */ + private void turnEnum(Object obj, Class clazz, Field field, Object value) { + EnumsConvert enumsConvert = field.getAnnotation(EnumsConvert.class); + //判断是否有枚举转换的注解 + if (enumsConvert != null) { + if (value != null && StrUtil.isNotEmpty(value.toString())) { + String key = value.toString(); + String target = enumsConvert.targetField(); + + Field targetField = getTargetField(clazz, field, target); + + try { + if (targetField != null) { + targetField.setAccessible(true); + //给目标字段设置转换后的值 + targetField.set(obj, EnumsInterface.getText(enumsConvert.getEnumsInterface(), key)); + } + } catch (IllegalAccessException e) { + log.error("turnEnum set value error!", e); + } + } + } + } + + /** + * 字典转换 + * + * @param obj 转换对象 + */ + private void turnDict(Object obj, Class clazz, Field field, Object value) { + DictConvert dictConvert = field.getAnnotation(DictConvert.class); + //判断是否有字典转换的注解 + if (dictConvert != null) { + if (value != null && StrUtil.isNotEmpty(value.toString())) { + String key = value.toString(); + String target = dictConvert.targetField(); + + Field targetField = getTargetField(clazz, field, target); + + try { + if (targetField != null) { + targetField.setAccessible(true); + String dictType = dictConvert.dictType(); + if(StrUtil.isEmpty(dictType)){ + return; + } + //单个字典时直接设置值 + if (!dictConvert.isMultiple()) { + + targetField.set(obj, ObjectUtil.isNotEmpty( DictUtils.getDictLabel(dictType,key)) ? DictUtils.getDictLabel(dictType,key):null); + } else { + //多个字典时,用逗号拼接值 + List textList = new ArrayList<>(); + String[] ids = key.split(dictConvert.separator()); + for (String id : ids) { + if (ObjectUtil.isNotEmpty(DictUtils.getDictLabel(dictType,id))&&StrUtil.isNotEmpty(DictUtils.getDictLabel(dictType,id))) { + textList.add(DictUtils.getDictLabel(dictType,id)); + } + } + targetField.set(obj, StrUtil.join(dictConvert.separator(), textList)); + } + } + } catch (IllegalAccessException e) { + log.error("turnDict set value error!", e); + } + } + } + } + /** + * 获取目标属性 + * + * @param clazz 取值对象 + * @param field 属性 + * @param target 属性名 + * @return Field 目标属性 + */ + private Field getTargetField(Class clazz, Field field, String target) { + Field targetField = null; + //转换的目标字段为当前字段 + if (StrUtil.isEmpty(target)) { + targetField = field; + } else { + try { + //目标字段为设置的字段 + targetField = clazz.getDeclaredField(target); + } catch (NoSuchFieldException e) { + log.error("getTargetField getDeclaredField error!", e); + } + } + return targetField; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/interfaces/EnumsInterface.java b/ruoyi-common/src/main/java/com/ruoyi/common/interfaces/EnumsInterface.java new file mode 100644 index 0000000..86a1db0 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/interfaces/EnumsInterface.java @@ -0,0 +1,38 @@ +package com.ruoyi.common.interfaces; + +import java.lang.reflect.Method; + +/** + * 枚举接口,反射出枚举code对应的文本 或 文本对应的code + * + * @Author wangqiong + * @Date 2023/01/5 + * @Version V1.0 + */ +public interface EnumsInterface { + static String getText(Class clazz, String code){ + try { + Method method = clazz.getMethod("getTextByCode", String.class); + if(method!=null){ + return (String)method.invoke(null,code); + } + }catch (Exception e){ + System.err.println("getTextByCode方法不存在"); + e.printStackTrace(); + } + return code; + } + + static String getCode(Class clazz,String text){ + try { + Method method = clazz.getMethod("getCodeByText", String.class); + if(method!=null){ + return (String)method.invoke(null,text); + } + }catch (Exception e){ + System.err.println("getCodeByText方法不存在"); + e.printStackTrace(); + } + return text; + } +} diff --git a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/ITemplateService.java b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/ITemplateService.java deleted file mode 100644 index fb80219..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/ITemplateService.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.ruoyi.wisdomarbitrate.service; - -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.wisdomarbitrate.domain.TemplateManual; - -import java.util.List; - -public interface ITemplateService { - AjaxResult insertTemplate(TemplateManual templateManual); - - AjaxResult deleteTemplate(Long id); - - AjaxResult updateTemplate(TemplateManual templateManual); - - List selectTemplate(TemplateManual templateManual); -} diff --git a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/TemplateServiceImpl.java b/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/TemplateServiceImpl.java deleted file mode 100644 index cbefff2..0000000 --- a/ruoyi-system/src/main/java/com/ruoyi/wisdomarbitrate/service/impl/TemplateServiceImpl.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.ruoyi.wisdomarbitrate.service.impl; - -import com.ruoyi.common.core.domain.AjaxResult; -import com.ruoyi.wisdomarbitrate.domain.DeptIdentify; -import com.ruoyi.wisdomarbitrate.domain.TemplateManual; -import com.ruoyi.wisdomarbitrate.mapper.TemplateManualMapper; -import com.ruoyi.wisdomarbitrate.service.ITemplateService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -@Service -public class TemplateServiceImpl implements ITemplateService { - @Autowired - private TemplateManualMapper templateManualMapper; - @Override - @Transactional - public AjaxResult insertTemplate(TemplateManual templateManual) { - int i = templateManualMapper.insertTemplateManual(templateManual); - if (i>0){ - return AjaxResult.success("新增成功"); - } - return AjaxResult.error("新增失败"); - } - - @Override - @Transactional - public AjaxResult deleteTemplate(Long id) { - TemplateManual templateManual = new TemplateManual(); - templateManual.setId(id); - templateManual.setDelFlag(2); - int i = templateManualMapper.updateTemplateManual(templateManual); - if (i > 0) { - return AjaxResult.success("删除成功"); - } - return AjaxResult.error(); - } - - @Override - @Transactional - public AjaxResult updateTemplate(TemplateManual templateManual) { - int i = templateManualMapper.updateTemplateManual(templateManual); - if (i > 0) { - return AjaxResult.success("修改成功"); - } - return AjaxResult.error(); - } - - @Override - public List selectTemplate(TemplateManual templateManual) { - return templateManualMapper.selectTemplateManual(templateManual); - } -} -- 2.54.0