Merge remote-tracking branch 'origin/master'
# Conflicts: # yudao-module-home/yudao-module-home-api/src/main/java/cn/iocoder/yudao/module/home/enums/ErrorCodeConstants.java
This commit is contained in:
commit
780e9ca9ba
2
pom.xml
2
pom.xml
@ -18,7 +18,7 @@
|
|||||||
<!-- <module>yudao-module-member</module>-->
|
<!-- <module>yudao-module-member</module>-->
|
||||||
<module>yudao-module-bpm</module>
|
<module>yudao-module-bpm</module>
|
||||||
<module>yudao-module-home</module>
|
<module>yudao-module-home</module>
|
||||||
<!-- <module>yudao-module-report</module>-->
|
<!-- <module>yudao-module-report</module>-->
|
||||||
<!-- <module>yudao-module-mp</module>-->
|
<!-- <module>yudao-module-mp</module>-->
|
||||||
<!-- <module>yudao-module-pay</module>-->
|
<!-- <module>yudao-module-pay</module>-->
|
||||||
<!-- <module>yudao-module-mall</module>-->
|
<!-- <module>yudao-module-mall</module>-->
|
||||||
|
@ -14,6 +14,6 @@ public interface ErrorCodeConstants {
|
|||||||
ErrorCode PJ_NOT_EXISTS = new ErrorCode(1_009_002_000, "主要用于首页的项目数据不存在");
|
ErrorCode PJ_NOT_EXISTS = new ErrorCode(1_009_002_000, "主要用于首页的项目数据不存在");
|
||||||
// ========== 公告内容 1_009_003_000 ==========
|
// ========== 公告内容 1_009_003_000 ==========
|
||||||
ErrorCode IMG_NOT_EXISTS = new ErrorCode(1_009_003_000, "公告内容不存在");
|
ErrorCode IMG_NOT_EXISTS = new ErrorCode(1_009_003_000, "公告内容不存在");
|
||||||
// ========== 请假管理 1_009_004_000 ==========
|
// ========== 公司新闻 1-011-003-000 ==========
|
||||||
ErrorCode QJGL_NOT_EXISTS = new ErrorCode(1_009_004_000, "请假管理不存在");
|
ErrorCode NEWS_NOT_EXISTS = new ErrorCode(1_011_003_000, "公司新闻不存在");
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import lombok.*;
|
|||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||||
import org.springframework.format.annotation.DateTimeFormat;
|
import org.springframework.format.annotation.DateTimeFormat;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||||
@ -15,30 +17,35 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_
|
|||||||
public class HomeimgPageReqVO extends PageParam {
|
public class HomeimgPageReqVO extends PageParam {
|
||||||
|
|
||||||
@Schema(description = "公告id", example = "17311")
|
@Schema(description = "公告id", example = "17311")
|
||||||
private Long imgId;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "公告名称", example = "李四")
|
@Schema(description = "公告名称", example = "李四")
|
||||||
private String imgName;
|
private String newsName;
|
||||||
|
|
||||||
@Schema(description = "发布时间")
|
@Schema(description = "发布时间")
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
private LocalDateTime[] imgCreatetime;
|
private LocalDateTime[] newsCreatetime;
|
||||||
|
|
||||||
@Schema(description = "轮换图片")
|
@Schema(description = "轮换图片")
|
||||||
// private String[] imgImg;
|
private String newsImg;
|
||||||
private String imgImg;
|
|
||||||
|
|
||||||
@Schema(description = "发布内容")
|
@Schema(description = "发布内容")
|
||||||
private String imgContent;
|
private String newsContent;
|
||||||
|
|
||||||
@Schema(description = "公告分类", example = "2")
|
@Schema(description = "公告分类", example = "2")
|
||||||
private Integer contentType;
|
private Integer contentType;
|
||||||
|
|
||||||
@Schema(description = "公告图片状态", example = "1")
|
@Schema(description = "公告图片状态", example = "1")
|
||||||
private Integer imgStatus;
|
private Integer newsStatus;
|
||||||
|
|
||||||
@Schema(description = "公告状态", example = "1")
|
@Schema(description = "公告状态", example = "1")
|
||||||
private Integer contentStatus;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "部门id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31402")
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
@Schema(description = "发布用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31402")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
@Schema(description = "创建时间")
|
@Schema(description = "创建时间")
|
||||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||||
|
@ -8,6 +8,8 @@ import com.alibaba.excel.annotation.*;
|
|||||||
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
|
||||||
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
@Schema(description = "管理后台 - 公告管理 Response VO")
|
@Schema(description = "管理后台 - 公告管理 Response VO")
|
||||||
@Data
|
@Data
|
||||||
@ExcelIgnoreUnannotated
|
@ExcelIgnoreUnannotated
|
||||||
@ -15,24 +17,23 @@ public class HomeimgRespVO {
|
|||||||
|
|
||||||
@Schema(description = "公告id", requiredMode = Schema.RequiredMode.REQUIRED, example = "17311")
|
@Schema(description = "公告id", requiredMode = Schema.RequiredMode.REQUIRED, example = "17311")
|
||||||
@ExcelProperty("公告id")
|
@ExcelProperty("公告id")
|
||||||
private Long imgId;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "公告名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
@Schema(description = "公告名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||||
@ExcelProperty("公告名称")
|
@ExcelProperty("公告名称")
|
||||||
private String imgName;
|
private String newsName;
|
||||||
|
|
||||||
@Schema(description = "发布时间")
|
@Schema(description = "发布时间")
|
||||||
@ExcelProperty("发布时间")
|
@ExcelProperty("发布时间")
|
||||||
private LocalDateTime imgCreatetime;
|
private LocalDateTime newsCreatetime;
|
||||||
|
|
||||||
@Schema(description = "轮换图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "轮换图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@ExcelProperty("轮换图片")
|
@ExcelProperty("轮换图片")
|
||||||
// private String[] imgImg;
|
private String newsImg;
|
||||||
private String imgImg;
|
|
||||||
|
|
||||||
@Schema(description = "发布内容")
|
@Schema(description = "发布内容")
|
||||||
@ExcelProperty("发布内容")
|
@ExcelProperty("发布内容")
|
||||||
private String imgContent;
|
private String newsContent;
|
||||||
|
|
||||||
@Schema(description = "公告分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
@Schema(description = "公告分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
@ExcelProperty(value = "公告分类", converter = DictConvert.class)
|
@ExcelProperty(value = "公告分类", converter = DictConvert.class)
|
||||||
@ -42,12 +43,20 @@ public class HomeimgRespVO {
|
|||||||
@Schema(description = "公告图片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "公告图片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@ExcelProperty(value = "公告图片状态", converter = DictConvert.class)
|
@ExcelProperty(value = "公告图片状态", converter = DictConvert.class)
|
||||||
@DictFormat("home_img_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
@DictFormat("home_img_status") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||||
private Integer imgStatus;
|
private Integer newsStatus;
|
||||||
|
|
||||||
@Schema(description = "公告状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "公告状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@ExcelProperty(value = "公告状态", converter = DictConvert.class)
|
@ExcelProperty(value = "公告状态", converter = DictConvert.class)
|
||||||
@DictFormat("home_content") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
@DictFormat("home_content") // TODO 代码优化:建议设置到对应的 DictTypeConstants 枚举类中
|
||||||
private Integer contentStatus;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "部门id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31402")
|
||||||
|
@NotNull(message = "部门id不能为空")
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
@Schema(description = "发布用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31402")
|
||||||
|
@NotNull(message = "发布用户id不能为空")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@ExcelProperty("创建时间")
|
@ExcelProperty("创建时间")
|
||||||
|
@ -11,22 +11,21 @@ import java.time.LocalDateTime;
|
|||||||
public class HomeimgSaveReqVO {
|
public class HomeimgSaveReqVO {
|
||||||
|
|
||||||
@Schema(description = "公告id", requiredMode = Schema.RequiredMode.REQUIRED, example = "17311")
|
@Schema(description = "公告id", requiredMode = Schema.RequiredMode.REQUIRED, example = "17311")
|
||||||
private Long imgId;
|
private Long id;
|
||||||
|
|
||||||
@Schema(description = "公告名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
@Schema(description = "公告名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||||
@NotEmpty(message = "公告名称不能为空")
|
@NotEmpty(message = "公告名称不能为空")
|
||||||
private String imgName;
|
private String newsName;
|
||||||
|
|
||||||
@Schema(description = "发布时间")
|
@Schema(description = "发布时间")
|
||||||
private LocalDateTime imgCreatetime;
|
private LocalDateTime newsCreatetime;
|
||||||
|
|
||||||
@Schema(description = "轮换图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
@Schema(description = "轮换图片", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||||
@NotEmpty(message = "轮换图片不能为空")
|
@NotEmpty(message = "轮换图片不能为空")
|
||||||
// private String[] imgImg;
|
private String newsImg;
|
||||||
private String imgImg;
|
|
||||||
|
|
||||||
@Schema(description = "发布内容")
|
@Schema(description = "发布内容")
|
||||||
private String imgContent;
|
private String newsContent;
|
||||||
|
|
||||||
@Schema(description = "公告分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
@Schema(description = "公告分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
|
||||||
@NotNull(message = "公告分类不能为空")
|
@NotNull(message = "公告分类不能为空")
|
||||||
@ -34,10 +33,19 @@ public class HomeimgSaveReqVO {
|
|||||||
|
|
||||||
@Schema(description = "公告图片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "公告图片状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotNull(message = "公告图片状态不能为空")
|
@NotNull(message = "公告图片状态不能为空")
|
||||||
private Integer imgStatus;
|
private Integer newsStatus;
|
||||||
|
|
||||||
@Schema(description = "公告状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
@Schema(description = "公告状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||||
@NotNull(message = "公告状态不能为空")
|
@NotNull(message = "公告状态不能为空")
|
||||||
private Integer contentStatus;
|
private Integer status;
|
||||||
|
|
||||||
|
@Schema(description = "部门id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31402")
|
||||||
|
@NotNull(message = "部门id不能为空")
|
||||||
|
private Long deptId;
|
||||||
|
|
||||||
|
@Schema(description = "发布用户id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31402")
|
||||||
|
@NotNull(message = "发布用户id不能为空")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package cn.iocoder.yudao.module.home.dal.dataobject.homeimg;
|
package cn.iocoder.yudao.module.home.dal.dataobject.homeimg;
|
||||||
|
|
||||||
|
import com.sun.xml.bind.v2.TODO;
|
||||||
import lombok.*;
|
import lombok.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -13,8 +14,8 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
|||||||
*
|
*
|
||||||
* @author xaol
|
* @author xaol
|
||||||
*/
|
*/
|
||||||
@TableName("des_homeimg")
|
@TableName("oa_news")
|
||||||
@KeySequence("des_homeimg_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
@KeySequence("oa_news_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
@ -24,44 +25,51 @@ import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
|||||||
public class HomeimgDO extends BaseDO {
|
public class HomeimgDO extends BaseDO {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 公告id
|
* 新闻id
|
||||||
*/
|
*/
|
||||||
@TableId
|
@TableId
|
||||||
private Long imgId;
|
private Long id;
|
||||||
/**
|
/**
|
||||||
* 公告名称
|
* 新闻名称
|
||||||
*/
|
*/
|
||||||
private String imgName;
|
private String newsName;
|
||||||
/**
|
/**
|
||||||
* 发布时间
|
* 发布时间
|
||||||
*/
|
*/
|
||||||
private LocalDateTime imgCreatetime;
|
private LocalDateTime newsCreatetime;
|
||||||
/**
|
/**
|
||||||
* 轮换图片
|
* 轮换图片
|
||||||
*/
|
*/
|
||||||
// private String[] imgImg;
|
private String newsImg;
|
||||||
private String imgImg;
|
|
||||||
/**
|
/**
|
||||||
* 发布内容
|
* 发布内容修改为String类型不然会返回字节
|
||||||
*/
|
*/
|
||||||
private String imgContent;
|
private String newsContent;
|
||||||
/**
|
/**
|
||||||
* 公告分类
|
* 新闻分类
|
||||||
*
|
*
|
||||||
* 枚举 {@link TODO home_content_status 对应的类}
|
* 枚举 {@link TODO oa_news_type 对应的类}
|
||||||
*/
|
*/
|
||||||
private Integer contentType;
|
private Integer contentType;
|
||||||
/**
|
/**
|
||||||
* 公告图片状态
|
* 新闻图片状态
|
||||||
*
|
*
|
||||||
* 枚举 {@link TODO home_img_status 对应的类}
|
* 枚举 {@link TODO oa_news_img_status 对应的类}
|
||||||
*/
|
*/
|
||||||
private Integer imgStatus;
|
private String newsStatus;
|
||||||
/**
|
/**
|
||||||
* 公告状态
|
* 新闻状态
|
||||||
*
|
*
|
||||||
* 枚举 {@link TODO home_content 对应的类}
|
* 枚举 {@link TODO oa_news_status 对应的类}
|
||||||
*/
|
*/
|
||||||
private Integer contentStatus;
|
private String status;
|
||||||
|
/**
|
||||||
|
* 部门id
|
||||||
|
*/
|
||||||
|
private Long deptId;
|
||||||
|
/**
|
||||||
|
* 部门id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,16 +17,18 @@ public interface HomeimgMapper extends BaseMapperX<HomeimgDO> {
|
|||||||
|
|
||||||
default PageResult<HomeimgDO> selectPage(HomeimgPageReqVO reqVO) {
|
default PageResult<HomeimgDO> selectPage(HomeimgPageReqVO reqVO) {
|
||||||
return selectPage(reqVO, new LambdaQueryWrapperX<HomeimgDO>()
|
return selectPage(reqVO, new LambdaQueryWrapperX<HomeimgDO>()
|
||||||
.eqIfPresent(HomeimgDO::getImgId, reqVO.getImgId())
|
.eqIfPresent(HomeimgDO::getId, reqVO.getId())
|
||||||
.likeIfPresent(HomeimgDO::getImgName, reqVO.getImgName())
|
.likeIfPresent(HomeimgDO::getNewsName, reqVO.getNewsName())
|
||||||
.betweenIfPresent(HomeimgDO::getImgCreatetime, reqVO.getImgCreatetime())
|
.betweenIfPresent(HomeimgDO::getNewsCreatetime, reqVO.getNewsCreatetime())
|
||||||
.eqIfPresent(HomeimgDO::getImgImg, reqVO.getImgImg())
|
.eqIfPresent(HomeimgDO::getNewsImg, reqVO.getNewsImg())
|
||||||
.eqIfPresent(HomeimgDO::getImgContent, reqVO.getImgContent())
|
.eqIfPresent(HomeimgDO::getNewsContent, reqVO.getNewsContent())
|
||||||
.eqIfPresent(HomeimgDO::getContentType, reqVO.getContentType())
|
.eqIfPresent(HomeimgDO::getContentType, reqVO.getContentType())
|
||||||
.eqIfPresent(HomeimgDO::getImgStatus, reqVO.getImgStatus())
|
.eqIfPresent(HomeimgDO::getNewsStatus, reqVO.getNewsStatus())
|
||||||
.eqIfPresent(HomeimgDO::getContentStatus, reqVO.getContentStatus())
|
.eqIfPresent(HomeimgDO::getStatus, reqVO.getStatus())
|
||||||
|
.eqIfPresent(HomeimgDO::getDeptId, reqVO.getDeptId())
|
||||||
|
.eqIfPresent(HomeimgDO::getUserId, reqVO.getUserId())
|
||||||
.betweenIfPresent(HomeimgDO::getCreateTime, reqVO.getCreateTime())
|
.betweenIfPresent(HomeimgDO::getCreateTime, reqVO.getCreateTime())
|
||||||
.orderByDesc(HomeimgDO::getImgId));
|
.orderByDesc(HomeimgDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -33,13 +33,13 @@ public class HomeimgServiceImpl implements HomeimgService {
|
|||||||
HomeimgDO img = BeanUtils.toBean(createReqVO, HomeimgDO.class);
|
HomeimgDO img = BeanUtils.toBean(createReqVO, HomeimgDO.class);
|
||||||
imgMapper.insert(img);
|
imgMapper.insert(img);
|
||||||
// 返回
|
// 返回
|
||||||
return img.getImgId();
|
return img.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateimg(HomeimgSaveReqVO updateReqVO) {
|
public void updateimg(HomeimgSaveReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateimgExists(updateReqVO.getImgId());
|
validateimgExists(updateReqVO.getId());
|
||||||
// 更新
|
// 更新
|
||||||
HomeimgDO updateObj = BeanUtils.toBean(updateReqVO, HomeimgDO.class);
|
HomeimgDO updateObj = BeanUtils.toBean(updateReqVO, HomeimgDO.class);
|
||||||
imgMapper.updateById(updateObj);
|
imgMapper.updateById(updateObj);
|
||||||
@ -55,7 +55,7 @@ public class HomeimgServiceImpl implements HomeimgService {
|
|||||||
|
|
||||||
private void validateimgExists(Long id) {
|
private void validateimgExists(Long id) {
|
||||||
if (imgMapper.selectById(id) == null) {
|
if (imgMapper.selectById(id) == null) {
|
||||||
throw exception(IMG_NOT_EXISTS);
|
throw exception(NEWS_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,15 +68,9 @@ public class FileServiceImpl implements FileService {
|
|||||||
file.setType(type);
|
file.setType(type);
|
||||||
file.setSize(content.length);
|
file.setSize(content.length);
|
||||||
fileMapper.insert(file);
|
fileMapper.insert(file);
|
||||||
// JSONObject jsonObject = new JSONObject();
|
|
||||||
// jsonObject.put("name",name);
|
|
||||||
// jsonObject.put("url",url);
|
|
||||||
String[] liss = new String[2];
|
String[] liss = new String[2];
|
||||||
// System.out.println(name);
|
|
||||||
// System.out.println(url);
|
|
||||||
liss[0] = name;
|
liss[0] = name;
|
||||||
liss[1] = url;
|
liss[1] = url;
|
||||||
// System.out.println(Arrays.toString(liss));
|
|
||||||
return liss;
|
return liss;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user