From 03b50542fa99b85622d09f2a1917da90692e4baf Mon Sep 17 00:00:00 2001 From: dzq Date: Wed, 5 Nov 2025 11:30:38 +0800 Subject: [PATCH] =?UTF-8?q?feat(wx=E7=94=A8=E6=88=B7):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=BE=AE=E4=BF=A1=E7=94=A8=E6=88=B7=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 实现微信用户模块的完整功能,包括: 1. 新增用户增删改查基础功能 2. 添加用户余额管理功能 3. 实现用户关联数据查询 4. 完善参数校验和业务规则 新增错误码COMMON_BAD_REQUEST用于参数校验 --- .../common/exception/error/ErrorCode.java | 1 + .../wx/user/WxUserApplicationService.java | 169 +++++++++++++++++ .../wx/user/command/AddWxUserCommand.java | 35 ++++ .../wx/user/command/UpdateWxUserCommand.java | 17 ++ .../domain/wx/user/db/SearchWxUserDO.java | 35 ++++ .../domain/wx/user/db/WxUserEntity.java | 61 ++++++ .../domain/wx/user/db/WxUserMapper.java | 49 +++++ .../domain/wx/user/db/WxUserService.java | 65 +++++++ .../wx/user/db/impl/WxUserServiceImpl.java | 101 ++++++++++ .../domain/wx/user/dto/WxUserDTO.java | 71 +++++++ .../domain/wx/user/model/WxUserModel.java | 177 ++++++++++++++++++ .../wx/user/model/WxUserModelFactory.java | 58 ++++++ .../wx/user/query/SearchWxUserQuery.java | 53 ++++++ doc/DDD-CQRS开发指南-新增表完整实现.md | 19 +- 14 files changed, 902 insertions(+), 9 deletions(-) create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/WxUserApplicationService.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/AddWxUserCommand.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/UpdateWxUserCommand.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/SearchWxUserDO.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserEntity.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserMapper.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserService.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/impl/WxUserServiceImpl.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/dto/WxUserDTO.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModel.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModelFactory.java create mode 100644 agileboot-domain/src/main/java/com/agileboot/domain/wx/user/query/SearchWxUserQuery.java diff --git a/agileboot-common/src/main/java/com/agileboot/common/exception/error/ErrorCode.java b/agileboot-common/src/main/java/com/agileboot/common/exception/error/ErrorCode.java index f693e52..6da6782 100644 --- a/agileboot-common/src/main/java/com/agileboot/common/exception/error/ErrorCode.java +++ b/agileboot-common/src/main/java/com/agileboot/common/exception/error/ErrorCode.java @@ -204,6 +204,7 @@ public enum ErrorCode implements ErrorCodeInterface { USER_ADMIN_CAN_NOT_BE_MODIFY(10515, "管理员不允许做任何修改", "Business.USER_ADMIN_CAN_NOT_BE_MODIFY"), + COMMON_BAD_REQUEST(10516, "请求参数错误", "Business.COMMON_BAD_REQUEST"), ; diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/WxUserApplicationService.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/WxUserApplicationService.java new file mode 100644 index 0000000..ef33afd --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/WxUserApplicationService.java @@ -0,0 +1,169 @@ +package com.agileboot.domain.wx.user; + +import com.agileboot.common.core.page.PageDTO; +import com.agileboot.domain.common.command.BulkOperationCommand; +import com.agileboot.domain.wx.user.command.AddWxUserCommand; +import com.agileboot.domain.wx.user.command.UpdateWxUserCommand; +import com.agileboot.domain.wx.user.db.SearchWxUserDO; +import com.agileboot.domain.wx.user.db.WxUserEntity; +import com.agileboot.domain.wx.user.db.WxUserService; +import com.agileboot.domain.wx.user.dto.WxUserDTO; +import com.agileboot.domain.wx.user.model.WxUserModel; +import com.agileboot.domain.wx.user.model.WxUserModelFactory; +import com.agileboot.domain.wx.user.query.SearchWxUserQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import java.util.List; +import java.util.stream.Collectors; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +/** + * 微信用户应用服务 + * + * @author your-name + * @since 2025-01-01 + */ +@Service +@RequiredArgsConstructor +public class WxUserApplicationService { + + private final WxUserService userService; + + private final WxUserModelFactory modelFactory; + + /** + * 获取微信用户列表(分页) + */ + public PageDTO getUserList(SearchWxUserQuery query) { + Page userPage = userService.getUserListWithJoin(query); + List userDTOList = userPage.getRecords() + .stream() + .map(WxUserDTO::new) + .collect(Collectors.toList()); + return new PageDTO<>(userDTOList, userPage.getTotal()); + } + + /** + * 获取微信用户详情 + */ + public WxUserDTO getUserDetailInfo(Long wxUserId) { + WxUserEntity userEntity = userService.getById(wxUserId); + if (userEntity == null) { + return null; + } + return new WxUserDTO(userEntity); + } + + /** + * 根据openid获取微信用户详情 + */ + public WxUserDTO getUserDetailByOpenid(String openid) { + WxUserEntity userEntity = userService.getByOpenid(openid); + if (userEntity == null) { + return null; + } + return new WxUserDTO(userEntity); + } + + /** + * 添加微信用户 + */ + @Transactional(rollbackFor = Exception.class) + public void addUser(AddWxUserCommand command) { + WxUserModel model = modelFactory.create(); + model.loadAddWxUserCommand(command); + + // 业务校验 + model.validateBeforeSave(); + + // 保存 + model.insert(); + } + + /** + * 更新微信用户 + */ + @Transactional(rollbackFor = Exception.class) + public void updateUser(UpdateWxUserCommand command) { + WxUserModel model = modelFactory.loadById(command.getWxUserId()); + model.loadUpdateWxUserCommand(command); + + // 业务校验 + model.checkOpenidIsUnique(); + model.checkTelIsUnique(); + model.checkFieldRelatedEntityExist(); + model.checkBalanceIsValid(); + + // 更新 + model.updateById(); + } + + /** + * 删除微信用户(单个) + */ + @Transactional(rollbackFor = Exception.class) + public void deleteUser(Long wxUserId) { + WxUserModel model = modelFactory.loadById(wxUserId); + + // 业务校验 + model.checkCanBeDelete(); + + // 软删除 + model.deleteById(); + } + + /** + * 批量删除微信用户 + */ + @Transactional(rollbackFor = Exception.class) + public void deleteUsers(BulkOperationCommand command) { + for (Long wxUserId : command.getIds()) { + WxUserModel model = modelFactory.loadById(wxUserId); + model.checkCanBeDelete(); + model.deleteById(); + } + } + + /** + * 增加余额 + */ + @Transactional(rollbackFor = Exception.class) + public void increaseBalance(Long wxUserId, Integer amount) { + WxUserModel model = modelFactory.loadById(wxUserId); + + model.increaseBalance(amount); + + // 更新 + model.updateById(); + } + + /** + * 减少余额 + */ + @Transactional(rollbackFor = Exception.class) + public void decreaseBalance(Long wxUserId, Integer amount) { + WxUserModel model = modelFactory.loadById(wxUserId); + + model.decreaseBalance(amount); + + // 更新 + model.updateById(); + } + + /** + * 设置余额 + */ + @Transactional(rollbackFor = Exception.class) + public void setBalance(Long wxUserId, Integer balance) { + WxUserModel model = modelFactory.loadById(wxUserId); + + model.setWxBalance(balance); + + // 校验 + model.checkBalanceIsValid(); + + // 更新 + model.updateById(); + } +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/AddWxUserCommand.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/AddWxUserCommand.java new file mode 100644 index 0000000..79032d1 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/AddWxUserCommand.java @@ -0,0 +1,35 @@ +package com.agileboot.domain.wx.user.command; + +import com.agileboot.common.annotation.ExcelColumn; +import lombok.Data; + +/** + * 添加微信用户命令 + * + * @author your-name + * @since 2025-01-01 + */ +@Data +public class AddWxUserCommand { + + @ExcelColumn(name = "openid") + private String openid; + + @ExcelColumn(name = "汇邦云用户ID") + private Long ab98UserId; + + @ExcelColumn(name = "企业用户ID") + private Long qyUserId; + + @ExcelColumn(name = "昵称") + private String nickName; + + @ExcelColumn(name = "手机号码") + private String tel; + + @ExcelColumn(name = "余额(分)") + private String wxBalance; // 使用 String 接收,前端传入 + + @ExcelColumn(name = "备注") + private String remark; +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/UpdateWxUserCommand.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/UpdateWxUserCommand.java new file mode 100644 index 0000000..44f9e78 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/command/UpdateWxUserCommand.java @@ -0,0 +1,17 @@ +package com.agileboot.domain.wx.user.command; + +import com.agileboot.common.annotation.ExcelColumn; +import lombok.Data; + +/** + * 更新微信用户命令 + * + * @author your-name + * @since 2025-01-01 + */ +@Data +public class UpdateWxUserCommand extends AddWxUserCommand { + + @ExcelColumn(name = "主键ID") + private Long wxUserId; +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/SearchWxUserDO.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/SearchWxUserDO.java new file mode 100644 index 0000000..3133f4c --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/SearchWxUserDO.java @@ -0,0 +1,35 @@ +package com.agileboot.domain.wx.user.db; + +import com.baomidou.mybatisplus.annotation.TableField; +import java.util.Date; +import lombok.Data; + +/** + * 微信用户查询数据对象(包含关联字段) + * + * @author your-name + * @since 2025-01-01 + */ +@Data +public class SearchWxUserDO { + + private Long wxUserId; + + private String openid; + + private Long ab98UserId; + + private Long qyUserId; + + private String nickName; + + private String tel; + + private Integer wxBalance; + + private Date createTime; + + private Date updateTime; + + private String remark; +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserEntity.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserEntity.java new file mode 100644 index 0000000..8fef569 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserEntity.java @@ -0,0 +1,61 @@ +package com.agileboot.domain.wx.user.db; + +import com.agileboot.common.core.base.BaseEntity; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import java.io.Serializable; +import java.util.Date; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 微信用户信息表 + * + * @author your-name + * @since 2025-01-01 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wx_user") +@ApiModel(value = "WxUserEntity对象", description = "微信用户信息表") +public class WxUserEntity extends BaseEntity { + + private static final long serialVersionUID = 1L; + + @ApiModelProperty("主键ID") + @TableId(value = "wx_user_id", type = IdType.AUTO) + private Long wxUserId; + + @ApiModelProperty("openid") + @TableField("openid") + private String openid; + + @ApiModelProperty("汇邦云用户ID") + @TableField("ab98_user_id") + private Long ab98UserId; + + @ApiModelProperty("企业用户id") + @TableField("qy_user_id") + private Long qyUserId; + + @ApiModelProperty("昵称") + @TableField("nick_name") + private String nickName; + + @ApiModelProperty("手机号码") + @TableField("tel") + private String tel; + + @ApiModelProperty("用户余额(单位:分)") + @TableField("wx_balance") + private Integer wxBalance; + + @Override + public Serializable pkVal() { + return this.wxUserId; + } +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserMapper.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserMapper.java new file mode 100644 index 0000000..0205850 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserMapper.java @@ -0,0 +1,49 @@ +package com.agileboot.domain.wx.user.db; + +import com.agileboot.common.core.page.AbstractPageQuery; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +/** + * 微信用户信息表 Mapper 接口 + * + * @author your-name + * @since 2025-01-01 + */ +@Mapper +public interface WxUserMapper extends BaseMapper { + + /** + * 分页查询微信用户列表(带关联数据) + * + * @param page 分页参数 + * @param query 查询条件 + * @return 微信用户列表 + */ + @Select({ + "" + }) + Page selectUserListWithJoin( + Page page, + @Param("query") AbstractPageQuery query + ); +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserService.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserService.java new file mode 100644 index 0000000..b64e3f1 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/WxUserService.java @@ -0,0 +1,65 @@ +package com.agileboot.domain.wx.user.db; + +import com.agileboot.common.core.page.AbstractPageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * 微信用户信息表 服务类 + * + * @author your-name + * @since 2025-01-01 + */ +public interface WxUserService extends IService { + + /** + * 检测openid是否唯一 + * + * @param openid openid + * @param wxUserId 微信用户ID(更新时传入,排除自身) + * @return 是否唯一 + */ + boolean isOpenidUnique(String openid, Long wxUserId); + + /** + * 检测手机号是否唯一 + * + * @param tel 手机号 + * @param wxUserId 微信用户ID(更新时传入,排除自身) + * @return 是否唯一 + */ + boolean isTelUnique(String tel, Long wxUserId); + + /** + * 根据条件分页查询微信用户列表 + * + * @param query 查询参数 + * @return 微信用户信息集合 + */ + Page getUserList(AbstractPageQuery query); + + /** + * 根据条件分页查询微信用户列表(带额外字段) + * + * @param query 查询参数 + * @return 微信用户信息集合(含关联字段) + */ + Page getUserListWithJoin(AbstractPageQuery query); + + /** + * 根据openid获取微信用户 + * + * @param openid openid + * @return 微信用户信息 + */ + WxUserEntity getByOpenid(String openid); + + /** + * 更新用户余额 + * + * @param wxUserId 微信用户ID + * @param balance 余额(单位:分) + * @return 是否成功 + */ + boolean updateBalance(Long wxUserId, Integer balance); +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/impl/WxUserServiceImpl.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/impl/WxUserServiceImpl.java new file mode 100644 index 0000000..9b64287 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/db/impl/WxUserServiceImpl.java @@ -0,0 +1,101 @@ +package com.agileboot.domain.wx.user.db.impl; + +import cn.hutool.core.util.StrUtil; +import com.agileboot.common.core.page.AbstractPageQuery; +import com.agileboot.domain.wx.user.db.WxUserEntity; +import com.agileboot.domain.wx.user.db.SearchWxUserDO; +import com.agileboot.domain.wx.user.db.WxUserMapper; +import com.agileboot.domain.wx.user.db.WxUserService; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import java.util.Objects; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +/** + * 微信用户信息表 服务实现类 + * + * @author your-name + * @since 2025-01-01 + */ +@Service +@RequiredArgsConstructor +public class WxUserServiceImpl extends ServiceImpl implements WxUserService { + + @Override + public boolean isOpenidUnique(String openid, Long wxUserId) { + if (StrUtil.isEmpty(openid)) { + return true; + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(WxUserEntity::getOpenid, openid) + .eq(WxUserEntity::getDeleted, 0); + + // 如果是更新操作,排除自身 + if (wxUserId != null) { + wrapper.ne(WxUserEntity::getWxUserId, wxUserId); + } + + return !(this.count(wrapper) > 0); + } + + @Override + public boolean isTelUnique(String tel, Long wxUserId) { + if (StrUtil.isEmpty(tel)) { + return true; + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(WxUserEntity::getTel, tel) + .eq(WxUserEntity::getDeleted, 0); + + // 如果是更新操作,排除自身 + if (wxUserId != null) { + wrapper.ne(WxUserEntity::getWxUserId, wxUserId); + } + + return !(this.count(wrapper) > 0); + } + + @Override + public Page getUserList(AbstractPageQuery query) { + return this.page(query.toPage(), query.addQueryCondition()); + } + + @Override + public Page getUserListWithJoin(AbstractPageQuery query) { + // 自定义 SQL 查询,返回关联数据 + return this.getBaseMapper().selectUserListWithJoin(query.toPage(), query); + } + + @Override + public WxUserEntity getByOpenid(String openid) { + if (StrUtil.isEmpty(openid)) { + return null; + } + + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(WxUserEntity::getOpenid, openid) + .eq(WxUserEntity::getDeleted, 0); + + return this.getOne(wrapper); + } + + @Override + public boolean updateBalance(Long wxUserId, Integer balance) { + if (wxUserId == null || balance == null) { + return false; + } + + WxUserEntity entity = new WxUserEntity(); + entity.setWxUserId(wxUserId); + entity.setWxBalance(balance); + + return this.updateById(entity); + } +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/dto/WxUserDTO.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/dto/WxUserDTO.java new file mode 100644 index 0000000..a7a219c --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/dto/WxUserDTO.java @@ -0,0 +1,71 @@ +package com.agileboot.domain.wx.user.dto; + +import cn.hutool.core.bean.BeanUtil; +import com.agileboot.common.annotation.ExcelColumn; +import com.agileboot.common.annotation.ExcelSheet; +import com.agileboot.domain.wx.user.db.WxUserEntity; +import com.agileboot.domain.wx.user.db.SearchWxUserDO; +import java.math.BigDecimal; +import java.util.Date; +import lombok.Data; + +/** + * 微信用户DTO + * + * @author your-name + * @since 2025-01-01 + */ +@ExcelSheet(name = "微信用户列表") +@Data +public class WxUserDTO { + + public WxUserDTO(WxUserEntity entity) { + if (entity != null) { + BeanUtil.copyProperties(entity, this); + + // 从缓存中获取关联数据(如果需要) + // if (entity.getAb98UserId() != null) { + // this.ab98NickName = CacheCenter.ab98UserCache.get(entity.getAb98UserId() + "")...; + // } + } + } + + public WxUserDTO(SearchWxUserDO entity) { + if (entity != null) { + BeanUtil.copyProperties(entity, this); + } + } + + @ExcelColumn(name = "主键ID") + private Long wxUserId; + + @ExcelColumn(name = "openid") + private String openid; + + @ExcelColumn(name = "汇邦云用户ID") + private Long ab98UserId; + + @ExcelColumn(name = "企业用户ID") + private Long qyUserId; + + @ExcelColumn(name = "昵称") + private String nickName; + + @ExcelColumn(name = "手机号码") + private String tel; + + @ExcelColumn(name = "余额(分)") + private Integer wxBalance; + + @ExcelColumn(name = "余额(元)") + private BigDecimal wxBalanceYuan; // 转换为元显示 + + @ExcelColumn(name = "创建时间") + private Date createTime; + + @ExcelColumn(name = "更新时间") + private Date updateTime; + + @ExcelColumn(name = "备注") + private String remark; +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModel.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModel.java new file mode 100644 index 0000000..49d2845 --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModel.java @@ -0,0 +1,177 @@ +package com.agileboot.domain.wx.user.model; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.StrUtil; +import com.agileboot.common.exception.ApiException; +import com.agileboot.common.exception.error.ErrorCode; +import com.agileboot.domain.wx.user.command.AddWxUserCommand; +import com.agileboot.domain.wx.user.command.UpdateWxUserCommand; +import com.agileboot.domain.wx.user.db.WxUserEntity; +import com.agileboot.domain.wx.user.db.WxUserService; +import java.math.BigDecimal; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +/** + * 微信用户领域模型 + * + * @author your-name + * @since 2025-01-01 + */ +@EqualsAndHashCode(callSuper = true) +@Data +@NoArgsConstructor +public class WxUserModel extends WxUserEntity { + + private WxUserService userService; + + public WxUserModel(WxUserEntity entity, WxUserService userService) { + if (entity != null) { + BeanUtil.copyProperties(entity, this); + } + this.userService = userService; + } + + public WxUserModel(WxUserService userService) { + this.userService = userService; + } + + /** + * 加载添加微信用户命令 + */ + public void loadAddWxUserCommand(AddWxUserCommand command) { + if (command != null) { + BeanUtil.copyProperties(command, this, "wxUserId"); + + // 转换余额字段 + if (StrUtil.isNotEmpty(command.getWxBalance())) { + try { + // 前端传入的是元,需要转换为分 + BigDecimal balanceYuan = new BigDecimal(command.getWxBalance()); + this.setWxBalance(balanceYuan.multiply(new BigDecimal("100")).intValue()); + } catch (NumberFormatException e) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "余额格式不正确"); + } + } + + // 设置默认值 + if (this.getWxBalance() == null) { + this.setWxBalance(0); // 默认余额0分 + } + } + } + + /** + * 加载更新微信用户命令 + */ + public void loadUpdateWxUserCommand(UpdateWxUserCommand command) { + if (command != null) { + loadAddWxUserCommand(command); + } + } + + /** + * 校验openid唯一性 + */ + public void checkOpenidIsUnique() { + if (!userService.isOpenidUnique(getOpenid(), getWxUserId())) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "openid已存在"); + } + } + + /** + * 校验手机号唯一性 + */ + public void checkTelIsUnique() { + if (!userService.isTelUnique(getTel(), getWxUserId())) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "手机号已存在"); + } + } + + /** + * 校验关联数据是否存在 + */ + public void checkFieldRelatedEntityExist() { + // 示例:校验汇邦云用户是否存在 + if (getAb98UserId() != null) { + // 可以通过其他 Service 校验 + // Example: ab98UserService.getById(getAb98UserId()); + } + + // 示例:校验企业用户是否存在 + if (getQyUserId() != null) { + // 可以通过其他 Service 校验 + // Example: qyUserService.getById(getQyUserId()); + } + } + + /** + * 校验是否可以删除 + */ + public void checkCanBeDelete() { + // 业务逻辑:例如检查用户是否有未完成的订单 + // if (hasUnfinishedOrders()) { + // throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "用户存在未完成的订单,无法删除"); + // } + } + + /** + * 校验余额是否合法 + */ + public void checkBalanceIsValid() { + if (getWxBalance() != null && getWxBalance() < 0) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "余额不能为负数"); + } + } + + /** + * 增加余额 + * + * @param amount 增加的金额(单位:分) + */ + public void increaseBalance(Integer amount) { + if (amount == null || amount <= 0) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "增加金额必须大于0"); + } + + Integer newBalance = (this.getWxBalance() == null ? 0 : this.getWxBalance()) + amount; + this.setWxBalance(newBalance); + } + + /** + * 减少余额 + * + * @param amount 减少的金额(单位:分) + */ + public void decreaseBalance(Integer amount) { + if (amount == null || amount <= 0) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "减少金额必须大于0"); + } + + Integer currentBalance = this.getWxBalance() == null ? 0 : this.getWxBalance(); + if (currentBalance < amount) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "余额不足"); + } + + this.setWxBalance(currentBalance - amount); + } + + /** + * 预保存校验 + */ + public void validateBeforeSave() { + if (StrUtil.isEmpty(getOpenid())) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "openid不能为空"); + } + + if (StrUtil.isEmpty(getNickName())) { + throw new ApiException(ErrorCode.Business.COMMON_BAD_REQUEST, "昵称不能为空"); + } + + checkOpenidIsUnique(); + checkTelIsUnique(); + checkFieldRelatedEntityExist(); + checkBalanceIsValid(); + } +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModelFactory.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModelFactory.java new file mode 100644 index 0000000..019f90f --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/model/WxUserModelFactory.java @@ -0,0 +1,58 @@ +package com.agileboot.domain.wx.user.model; + +import com.agileboot.common.exception.ApiException; +import com.agileboot.common.exception.error.ErrorCode; +import com.agileboot.domain.wx.user.db.WxUserEntity; +import com.agileboot.domain.wx.user.db.WxUserService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +/** + * 微信用户模型工厂 + * + * @author your-name + * @since 2025-01-01 + */ +@Component +@RequiredArgsConstructor +public class WxUserModelFactory { + + private final WxUserService userService; + + /** + * 根据ID加载微信用户模型 + * + * @param wxUserId 微信用户ID + * @return 微信用户模型 + */ + public WxUserModel loadById(Long wxUserId) { + WxUserEntity entity = userService.getById(wxUserId); + if (entity == null) { + throw new ApiException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, wxUserId, "微信用户"); + } + return new WxUserModel(entity, userService); + } + + /** + * 根据openid加载微信用户模型 + * + * @param openid openid + * @return 微信用户模型 + */ + public WxUserModel loadByOpenid(String openid) { + WxUserEntity entity = userService.getByOpenid(openid); + if (entity == null) { + throw new ApiException(ErrorCode.Business.COMMON_OBJECT_NOT_FOUND, openid, "openid"); + } + return new WxUserModel(entity, userService); + } + + /** + * 创建新微信用户模型 + * + * @return 微信用户模型 + */ + public WxUserModel create() { + return new WxUserModel(userService); + } +} diff --git a/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/query/SearchWxUserQuery.java b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/query/SearchWxUserQuery.java new file mode 100644 index 0000000..f25b6fa --- /dev/null +++ b/agileboot-domain/src/main/java/com/agileboot/domain/wx/user/query/SearchWxUserQuery.java @@ -0,0 +1,53 @@ +package com.agileboot.domain.wx.user.query; + +import cn.hutool.core.util.StrUtil; +import com.agileboot.common.core.page.AbstractPageQuery; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 微信用户查询条件 + * + * @author your-name + * @since 2025-01-01 + */ +@EqualsAndHashCode(callSuper = true) +@Data +public class SearchWxUserQuery extends AbstractPageQuery { + + private Long wxUserId; + + private String openid; + + private String nickName; + + private String tel; + + private Long ab98UserId; + + private Long qyUserId; + + private Integer minBalance; + + private Integer maxBalance; + + @Override + public QueryWrapper addQueryCondition() { + QueryWrapper queryWrapper = new QueryWrapper<>(); + + queryWrapper.like(StrUtil.isNotEmpty(openid), "openid", openid) + .like(StrUtil.isNotEmpty(nickName), "nick_name", nickName) + .like(StrUtil.isNotEmpty(tel), "tel", tel) + .eq(wxUserId != null, "wx_user_id", wxUserId) + .eq(ab98UserId != null, "ab98_user_id", ab98UserId) + .eq(qyUserId != null, "qy_user_id", qyUserId) + .eq("deleted", 0) + .between(minBalance != null && maxBalance != null, "wx_balance", minBalance, maxBalance); + + // 设置时间范围排序字段 + this.timeRangeColumn = "create_time"; + + return queryWrapper; + } +} diff --git a/doc/DDD-CQRS开发指南-新增表完整实现.md b/doc/DDD-CQRS开发指南-新增表完整实现.md index c417b68..2f4e523 100644 --- a/doc/DDD-CQRS开发指南-新增表完整实现.md +++ b/doc/DDD-CQRS开发指南-新增表完整实现.md @@ -275,8 +275,9 @@ public class ExampleUserServiceImpl extends ServiceImpl wrapper = new LambdaQueryWrapper<>() - .eq(ExampleUserEntity::getEmail, email) + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(ExampleUserEntity::getEmail, email) .eq(ExampleUserEntity::getDeleted, 0); // 如果是更新操作,排除自身 @@ -284,7 +285,7 @@ public class ExampleUserServiceImpl extends ServiceImpl 0; + return !(this.count(wrapper) > 0); } @Override @@ -293,11 +294,12 @@ public class ExampleUserServiceImpl extends ServiceImpl wrapper = new LambdaQueryWrapper<>() - .eq(ExampleUserEntity::getUserName, userName) + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + + wrapper.eq(ExampleUserEntity::getUserName, userName) .eq(ExampleUserEntity::getDeleted, 0); - return !this.count(wrapper) > 0; + return !(this.count(wrapper) > 0); } @Override @@ -312,13 +314,13 @@ public class ExampleUserServiceImpl extends ServiceImpl getUserList(AbstractPageQuery query) { - return this.page(query.buildPage(), query.addQueryCondition()); + return this.page(query.toPage(), query.addQueryCondition()); } @Override public Page getUserListWithJoin(AbstractPageQuery query) { // 自定义 SQL 查询,返回关联数据 - return this.getBaseMapper().selectUserListWithJoin(query.buildPage(), query); + return this.getBaseMapper().selectUserListWithJoin(query.toPage(), query); } } ``` @@ -961,7 +963,6 @@ public class UserModelFactory { ```java package com.agileboot.domain.example.user; -import cn.hutool.core.convert.Convert; import com.agileboot.common.core.page.PageDTO; import com.agileboot.domain.common.command.BulkOperationCommand; import com.agileboot.domain.example.user.command.AddUserCommand;