趁热打铁:用户模块剩余服务层接口实现

本文档详细介绍了用户模块的接口实现过程,包括新增用户表个性签名字段,完善UserService接口,实现用户登录逻辑,剩余逻辑实现,常量提取以及单元测试的完善。通过mybatis动态SQL特性进行数据操作,并讨论了密码匹配和角色验证的方法。同时,文章提倡将重复的字符串提取为常量以提高代码可读性,并展示了如何进行单元测试的优化。

接下来我们将趁热打铁完成UserService剩余的接口定义和实现。在这之前我们先完善下user实体的设计,增加一个个性签名的字段personal_signature

新增用户表个性签名

显然,我们第一个要改的就是pdm模型,

在这里插入图片描述

然后改本地h2库,右键新增column,

在这里插入图片描述

在这里插入图片描述

然后,放开build.gradle中注释掉的apply from: 'mbgen.gradle',刷新下gradle,user生成的model和mapper则更新完成。

完善UserService接口定义

下面我们一鼓作气,把剩余的用户接口都定义出来:

package com.xiaojuan.boot.service;

import ...

public interface UserService {

    ...

    UserInfoDTO login(String username, String password) throws BusinessException;

    void updatePersonalSignature(long userId, String signature);

    void checkAdminRole(byte role) throws BusinessException;

}

温馨提醒

当我们的接口命名做到见名知意的话,可以不写接口的文档注释

这里我们定义了一个用来保存登录成功后的用户信息的DTO:

package com.xiaojuan.boot.dto;

import ...

@Data
public class UserInfoDTO {
    /** 用户id */
    private Long id;
    private String userName;
    /** 用户角色 1-普通用户 2-管理员 */
    private Byte role;
    private String personalSignature;
}

实现用户登录逻辑

UserServiceImpl中实现:

@Override
public UserInfoDTO login(String username, String password) throws BusinessException {
    Assert.hasText(username, "用户名不能为空");
    Assert.hasText(password, "密码不能为空");
    // 按照用户名查询用户
    Optional<User> userOptional = userMapper.selectOne(c -> c.where(UserDynamicSqlSupport.username, isEqualTo(username)));
    if (!userOptional.isPresent()) {
        throw new BusinessException("用户名错误");
    }
    User user = userOptional.get();
    if (!passwordEncoder.matches(password, user.getPassword())) {
        throw new BusinessException("密码错误");
    }
    UserInfoDTO userInfoDTO = new UserInfoDTO();
    BeanUtils.copyProperties(user, userInfoDTO);
    return userInfoDTO;
}

代码说明

注意这里灵活运用了mybatis3的dynamic sql的特性来查询单条记录。

密码的匹配不能直接加密后和数据库比较,要先查出来,再用spring安全框架的PasswordEncodermatches来匹配,解铃还须系铃人嘛。

最后,因为UserInfoDTO中的字段都在User实体类中被涵盖了,因此我们无需手动一个个赋值,直接用spring提供的工具类,进行相同属性的拷贝即可。

剩余逻辑实现

@Override
public void updatePersonalSignature(long userId, String signature) {
    userMapper.update(c -> c.set(personalSignature).equalTo(signature)
                      .set(updateTime).equalTo(new Date())
                      .where(id, isEqualTo(userId)));
}

@Override
public void checkAdminRole(byte role) throws BusinessException {
    if (Role.ADMIN.getValue() != role) {
        throw new BusinessException("非管理员角色,不能操作!");
    }
}

代码说明

同样根据用户id来更新签名信息,我们也采用mybatis dynamic sql的形式。

在检查用户角色时,我们不是直接取管理员的角色数值进行比较,而是将角色维护为一个枚举:

package com.xiaojuan.boot.enums;

import ...

@Getter
@AllArgsConstructor
public enum Role {

    USER("普通用户", (byte)1),
    ADMIN("管理员", (byte)2);

    private final String label;
    private final Byte value;
}

因为在生成model时,对数据库的tinyint类型映射为java的byte类型,而不是int类型,虽然这不是我们的本意,但生成器也没提供很好的字段类型映射用户配置方式,我们就保留byte类型,勉为其难的做一次转换吧。

常量提取

不知道大家发现没有,我们在抛出异常消息和断言异常消息时,会写重复的字符串,很显然,更好的做法是将它们提取为常量。下面是提取技巧:

右键,Refactor:

在这里插入图片描述

提取为常量:

在这里插入图片描述

在这里插入图片描述

全部提取出来,放到UserService接口中

package com.xiaojuan.boot.service;

import ...

public interface UserService {

    String MSG_USERNAME_REQUIRED = "用户名不能为空";
    String MSG_USERNAME_EXISTS = "用户名已存在";
    String MSG_USERNAME_ERROR = "用户名错误";
    String MSG_PASSWORD_REQUIRED = "密码不能为空";
    String MSG_PASSWORD_ERROR = "密码错误";
    String MSG_ADMIN_ROLE_REQUIRED = "非管理员角色,不能操作!";

    ...
}

这样我们在单元测试中直接引用这些常量即可。

完善单元测试

最后将我们的service层单元测试完善下:

@Test
public void testComposition() {
    // 先登录失败
    assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> {
        userService.login("zhangsan", "123");
    }).withMessage(MSG_USERNAME_ERROR);

    // 注册张三用户
    userService.register(new UserRegisterDTO("zhangsan", "123"));

    // 密码错误
    assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> {
        userService.login("zhangsan", "666");
    }).withMessage(MSG_PASSWORD_ERROR);

    // 登录成功
    UserInfoDTO user = userService.login("zhangsan", "123");

    // 检查没有权限
    assertThatExceptionOfType(BusinessException.class).isThrownBy(() -> {
        userService.checkAdminRole(user.getRole());
    }).withMessage(MSG_ADMIN_ROLE_REQUIRED);
}

我们把所有的单元测试跑下:

在这里插入图片描述

在这里插入图片描述

ok!都搞定!

在这里插入图片描述

### 如何在微信公众平台配置AI Bot 为了在微信公众平台上配置AI Bot,需先理解不同类型的公众账号及其特点。服务号和订阅号的主要区别在于功能权限和服务对象。 #### 服务号与订阅号的区别 - **服务号**:每月可发送4条群发消息给关注者;支持更丰富的接口能力,如支付、卡券等功能;适合企业和商家提供客户服务。 - **订阅号**:每天可以向用户推送一条信息;主要用于媒体和个人发布资讯类内容;侧重于内容传播而非商业交易[^1]。 对于希望集成AI Bot的应用场景来说,无论是哪种类型的公众号都可以通过API实现自动化回复等功能。具体操作如下: #### 获取AppId 无论选择何种类型的公众号,在开始之前都需要获得该公众号对应的`AppId`。这一步骤相同,即访问微信公众平台官网进入“设置与开发-基本配置-公众号开发信息”,从中找到并记录下“开发者ID(AppID)”。这一标识符用于后续对接过程中验证身份合法性[^3]。 #### 接入Coze Bot或其他第三方机器人框架 以接入Coze Bot为例说明流程: - 登录至目标机器人的管理后台; - 寻找有关微信公众号连接的相关选项; - 在指定页面内输入先前准备好的微信公众号`AppId`完成初步关联设定。 另外一种情况是利用像LinkAI这样的服务平台构建自定义聊天机器人,则除了上述提到的基础参数外还可能涉及到更多高级特性配置,比如自然语言处理模型的选择等[^2]。 ```python import requests def send_message_to_wechat(app_id, message): url = f"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN" payload = { "touser": "USER_ID", "msgtype": "text", "text": {"content":message} } headers = {'Content-Type': 'application/json'} response = requests.post(url, json=payload, headers=headers) return response.json() ``` 以上代码片段展示了如何使用Python脚本调用微信开放平台提供的客服消息接口发送文本形式的消息给特定用户。实际部署时需要替换掉其中的占位符变量(`ACCESS_TOKEN`, `USER_ID`)为真实的值,并确保已按照官方文档指引正确获取必要的凭证令牌。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java小卷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值