会员中心:会员管理 50%

This commit is contained in:
YunaiV 2023-08-19 14:12:03 +08:00
parent 61fbb9909a
commit e8c1bdde3d
15 changed files with 349 additions and 44 deletions

View File

@ -23,6 +23,8 @@ public class DateUtils {
*/
public static final long SECOND_MILLIS = 1000;
public static final String FORMAT_YEAR_MONTH_DAY = "yyyy-MM-dd";
public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss";
public static final String FORMAT_HOUR_MINUTE_SECOND = "HH:mm:ss";

View File

@ -120,7 +120,6 @@
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
<el-table-column label="序号" type="index" width="70px" />
#foreach($column in $columns)
#if ($column.listOperationResult)
#set ($dictType=$column.dictType)
@ -142,7 +141,7 @@
</template>
</el-table-column>
#else
<el-table-column label="${comment}" align="center" prop="${javaField}" width="150px"/>
<el-table-column label="${comment}" align="center" prop="${javaField}" width="150px" />
#end
#end
#end
@ -180,7 +179,7 @@
<${simpleClassName}Form ref="formRef" @success="getList" />
</template>
<script setup lang="ts" name="${table.className}">
<script setup lang="ts">
#if ($dictMethods.size() > 0)
import { DICT_TYPE#foreach ($dictMethod in $dictMethods), ${dictMethod}#end } from '@/utils/dict'
#end
@ -193,6 +192,9 @@ import { dateFormatter } from '@/utils/formatTime'
import download from '@/utils/download'
import * as ${simpleClassName}Api from '@/api/${table.moduleName}/${classNameVar}'
import ${simpleClassName}Form from './${simpleClassName}Form.vue'
defineOptions({ name: '${table.className}' })
const message = useMessage() // 消息弹窗
const { t } = useI18n() // 国际化

View File

@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.member.api.user;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.member.convert.user.UserConvert;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import org.springframework.stereotype.Service;
@ -26,22 +26,22 @@ public class MemberUserApiImpl implements MemberUserApi {
@Override
public MemberUserRespDTO getUser(Long id) {
MemberUserDO user = userService.getUser(id);
return UserConvert.INSTANCE.convert2(user);
return MemberUserConvert.INSTANCE.convert2(user);
}
@Override
public List<MemberUserRespDTO> getUserList(Collection<Long> ids) {
return UserConvert.INSTANCE.convertList2(userService.getUserList(ids));
return MemberUserConvert.INSTANCE.convertList2(userService.getUserList(ids));
}
@Override
public List<MemberUserRespDTO> getUserListByNickname(String nickname) {
return UserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname));
return MemberUserConvert.INSTANCE.convertList2(userService.getUserListByNickname(nickname));
}
@Override
public MemberUserRespDTO getUserByMobile(String mobile) {
return UserConvert.INSTANCE.convert2(userService.getUserByMobile(mobile));
return MemberUserConvert.INSTANCE.convert2(userService.getUserByMobile(mobile));
}
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.member.controller.admin.user;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.springframework.security.access.prepost.PreAuthorize;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import javax.validation.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.*;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
@Tag(name = "管理后台 - 会员用户")
@RestController
@RequestMapping("/member/user")
@Validated
public class MemberUserController {
@Resource
private MemberUserService memberUserService;
@PutMapping("/update")
@Operation(summary = "更新会员用户")
@PreAuthorize("@ss.hasPermission('member:user:update')")
public CommonResult<Boolean> updateUser(@Valid @RequestBody MemberUserUpdateReqVO updateReqVO) {
memberUserService.updateUser(updateReqVO);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得会员用户")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('member:user:query')")
public CommonResult<MemberUserRespVO> getUser(@RequestParam("id") Long id) {
MemberUserDO user = memberUserService.getUser(id);
return success(MemberUserConvert.INSTANCE.convert03(user));
}
@GetMapping("/page")
@Operation(summary = "获得会员用户分页")
@PreAuthorize("@ss.hasPermission('member:user:query')")
public CommonResult<PageResult<MemberUserRespVO>> getUserPage(@Valid MemberUserPageReqVO pageVO) {
PageResult<MemberUserDO> pageResult = memberUserService.getUserPage(pageVO);
return success(MemberUserConvert.INSTANCE.convertPage(pageResult));
}
}

View File

@ -1 +0,0 @@
package cn.iocoder.yudao.module.member.controller.admin.user;

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.member.controller.admin.user.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
/**
* 会员用户 Base VO提供给添加修改详细的子 VO 使用
* 如果子 VO 存在差异的字段请不要添加到这里影响 Swagger 文档生成
*/
@Data
public class MemberUserBaseVO {
@Schema(description = "手机号", requiredMode = Schema.RequiredMode.REQUIRED, example = "15601691300")
@NotNull(message = "手机号不能为空")
private String mobile;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "状态不能为空")
private Byte status;
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
@NotNull(message = "用户昵称不能为空")
private String nickname;
@Schema(description = "用户昵称", example = "李四")
private String name;
@Schema(description = "用户性别", example = "1")
private Byte sex;
@Schema(description = "所在地", example = "4371")
private Long areaId;
@Schema(description = "出生日期", example = "2023-03-12")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
private LocalDateTime birthday;
@Schema(description = "会员备注", example = "我是小备注")
private String mark;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.member.controller.admin.user.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 会员用户分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserPageReqVO extends PageParam {
@Schema(description = "手机号", example = "15601691300")
private String mobile;
@Schema(description = "用户昵称", example = "李四")
private String nickname;
@Schema(description = "最后登录时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] loginDate;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.member.controller.admin.user.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 会员用户 Response VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserRespVO extends MemberUserBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788")
private Long id;
@Schema(description = "注册 IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
private String registerIp;
@Schema(description = "最后登录IP", requiredMode = Schema.RequiredMode.REQUIRED, example = "127.0.0.1")
private String loginIp;
@Schema(description = "最后登录时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime loginDate;
@Schema(description = "头像", requiredMode = Schema.RequiredMode.REQUIRED, example = "https://www.iocoder.cn/x.png")
@NotNull(message = "头像不能为空")
private String avatar;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.member.controller.admin.user.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import javax.validation.constraints.NotNull;
@Schema(description = "管理后台 - 会员用户更新 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MemberUserUpdateReqVO extends MemberUserBaseVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23788")
@NotNull(message = "编号不能为空")
private Long id;
}

View File

@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.convert.user.UserConvert;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.service.user.MemberUserService;
import io.swagger.v3.oas.annotations.tags.Tag;
@ -56,7 +56,7 @@ public class AppUserController {
@PreAuthenticated
public CommonResult<AppUserInfoRespVO> getUserInfo() {
MemberUserDO user = userService.getUser(getLoginUserId());
return success(UserConvert.INSTANCE.convert(user));
return success(MemberUserConvert.INSTANCE.convert(user));
}
@PostMapping("/update-mobile")

View File

@ -1,6 +1,9 @@
package cn.iocoder.yudao.module.member.convert.user;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserRespVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserInfoRespVO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import org.mapstruct.Mapper;
@ -9,9 +12,9 @@ import org.mapstruct.factory.Mappers;
import java.util.List;
@Mapper
public interface UserConvert {
public interface MemberUserConvert {
UserConvert INSTANCE = Mappers.getMapper(UserConvert.class);
MemberUserConvert INSTANCE = Mappers.getMapper(MemberUserConvert.class);
AppUserInfoRespVO convert(MemberUserDO bean);
@ -19,4 +22,10 @@ public interface UserConvert {
List<MemberUserRespDTO> convertList2(List<MemberUserDO> list);
MemberUserDO convert(MemberUserUpdateReqVO bean);
PageResult<MemberUserRespVO> convertPage(PageResult<MemberUserDO> page);
MemberUserRespVO convert03(MemberUserDO bean);
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.member.dal.dataobject.user;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO;
import cn.iocoder.yudao.module.system.enums.common.SexEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -26,26 +28,13 @@ import java.time.LocalDateTime;
@AllArgsConstructor
public class MemberUserDO extends TenantBaseDO {
// ========== 账号信息 ==========
/**
* 用户ID
*/
@TableId
private Long id;
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatar;
/**
* 帐号状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 手机
*/
@ -56,6 +45,12 @@ public class MemberUserDO extends TenantBaseDO {
* 因为目前使用 {@link BCryptPasswordEncoder} 加密器所以无需自己处理 salt
*/
private String password;
/**
* 帐号状态
*
* 枚举 {@link CommonStatusEnum}
*/
private Integer status;
/**
* 注册 IP
*/
@ -69,10 +64,44 @@ public class MemberUserDO extends TenantBaseDO {
*/
private LocalDateTime loginDate;
// TODO 芋艿name 真实名字
// TODO 芋艿email 邮箱
// TODO 芋艿gender 性别
// TODO 芋艿score 积分
// TODO 芋艿payPassword 支付密码
// ========== 基础信息 ==========
/**
* 用户昵称
*/
private String nickname;
/**
* 用户头像
*/
private String avatar;
/**
* 真实名字
*/
private String name;
/**
* 性别
*
* 枚举 {@link SexEnum}
*/
private Integer sex;
/**
* 出生日期
*/
private LocalDateTime birthday;
/**
* 所在地
*
* 关联 {@link Area#getId()} 字段
*/
private Integer areaId;
/**
* 用户备注
*/
private String mark;
// ========== 其它信息 ==========
// TODO 积分成长值会员等级等等
}

View File

@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.member.dal.mysql.user;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import org.apache.ibatis.annotations.Mapper;
@ -24,4 +26,13 @@ public interface MemberUserMapper extends BaseMapperX<MemberUserDO> {
.likeIfPresent(MemberUserDO::getNickname, nickname));
}
default PageResult<MemberUserDO> selectPage(MemberUserPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<MemberUserDO>()
.likeIfPresent(MemberUserDO::getMobile, reqVO.getMobile())
.betweenIfPresent(MemberUserDO::getLoginDate, reqVO.getLoginDate())
.likeIfPresent(MemberUserDO::getNickname, reqVO.getNickname())
.betweenIfPresent(MemberUserDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(MemberUserDO::getId));
}
}

View File

@ -1,9 +1,13 @@
package cn.iocoder.yudao.module.member.service.user;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import javax.validation.Valid;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
@ -66,14 +70,16 @@ public interface MemberUserService {
List<MemberUserDO> getUserList(Collection<Long> ids);
/**
* 修改用户昵称
* 会员修改用户昵称
*
* @param userId 用户id
* @param nickname 用户新昵称
*/
void updateUserNickname(Long userId, String nickname);
/**
* 修改用户头像
* 会员修改用户头像
*
* @param userId 用户id
* @param inputStream 头像文件
* @return 头像url
@ -81,7 +87,8 @@ public interface MemberUserService {
String updateUserAvatar(Long userId, InputStream inputStream) throws Exception;
/**
* 修改手机
* 会员修改手机
*
* @param userId 用户id
* @param reqVO 请求实体
*/
@ -96,4 +103,19 @@ public interface MemberUserService {
*/
boolean isPasswordMatch(String rawPassword, String encodedPassword);
/**
* 管理员更新会员用户
*
* @param updateReqVO 更新信息
*/
void updateUser(@Valid MemberUserUpdateReqVO updateReqVO);
/**
* 管理员获得会员用户分页
*
* @param pageReqVO 分页查询
* @return 会员用户分页
*/
PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO);
}

View File

@ -2,9 +2,14 @@ package cn.iocoder.yudao.module.member.service.user;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserPageReqVO;
import cn.iocoder.yudao.module.member.controller.admin.user.vo.MemberUserUpdateReqVO;
import cn.iocoder.yudao.module.member.controller.app.user.vo.AppUserUpdateMobileReqVO;
import cn.iocoder.yudao.module.member.convert.user.MemberUserConvert;
import cn.iocoder.yudao.module.member.dal.dataobject.user.MemberUserDO;
import cn.iocoder.yudao.module.member.dal.mysql.user.MemberUserMapper;
import cn.iocoder.yudao.module.system.api.sms.SmsCodeApi;
@ -19,14 +24,14 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.InputStream;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.module.member.enums.ErrorCodeConstants.USER_NOT_EXISTS;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_MOBILE_EXISTS;
/**
* 会员 User Service 实现类
@ -101,7 +106,7 @@ public class MemberUserServiceImpl implements MemberUserService {
@Override
public void updateUserNickname(Long userId, String nickname) {
MemberUserDO user = this.checkUserExists(userId);
MemberUserDO user = this.validateUserExists(userId);
// 仅当新昵称不等于旧昵称时进行修改
if (nickname.equals(user.getNickname())){
return;
@ -113,8 +118,8 @@ public class MemberUserServiceImpl implements MemberUserService {
}
@Override
public String updateUserAvatar(Long userId, InputStream avatarFile) throws Exception {
this.checkUserExists(userId);
public String updateUserAvatar(Long userId, InputStream avatarFile) {
validateUserExists(userId);
// 创建文件
String avatar = fileApi.createFile(IoUtil.readBytes(avatarFile));
// 更新头像路径
@ -126,7 +131,7 @@ public class MemberUserServiceImpl implements MemberUserService {
@Transactional(rollbackFor = Exception.class)
public void updateUserMobile(Long userId, AppUserUpdateMobileReqVO reqVO) {
// 检测用户是否存在
checkUserExists(userId);
validateUserExists(userId);
// TODO 芋艿oldMobile 应该不用传递
// 校验旧手机和旧验证码
@ -155,8 +160,20 @@ public class MemberUserServiceImpl implements MemberUserService {
return passwordEncoder.encode(password);
}
@Override
public void updateUser(MemberUserUpdateReqVO updateReqVO) {
// 校验存在
validateUserExists(updateReqVO.getId());
// 校验手机唯一
validateMobileUnique(updateReqVO.getId(), updateReqVO.getMobile());
// 更新
MemberUserDO updateObj = MemberUserConvert.INSTANCE.convert(updateReqVO);
memberUserMapper.updateById(updateObj);
}
@VisibleForTesting
public MemberUserDO checkUserExists(Long id) {
MemberUserDO validateUserExists(Long id) {
if (id == null) {
return null;
}
@ -167,4 +184,27 @@ public class MemberUserServiceImpl implements MemberUserService {
return user;
}
@VisibleForTesting
void validateMobileUnique(Long id, String mobile) {
if (StrUtil.isBlank(mobile)) {
return;
}
MemberUserDO user = memberUserMapper.selectByMobile(mobile);
if (user == null) {
return;
}
// 如果 id 为空说明不用比较是否为相同 id 的用户
if (id == null) {
throw exception(USER_MOBILE_EXISTS);
}
if (!user.getId().equals(id)) {
throw exception(USER_MOBILE_EXISTS);
}
}
@Override
public PageResult<MemberUserDO> getUserPage(MemberUserPageReqVO pageReqVO) {
return memberUserMapper.selectPage(pageReqVO);
}
}