Skip to content

1.java中常用的工具类

java
StringUtils.isAnyBlank

💃常用于只要任意一个为空

java
StringUtils.isNotBlank

💃用于不为空

java
   QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(username)) {
            queryWrapper.like("user_name", username);

🕺用于查询匹配单个字段

2. 增删改查代码书写规范

  • 增 save
  • 删 remoceById
  • 改 UpdateById
    • list(全部数据)
    • 使用queryWrapper.like(单个数据)
1.查询(返回列表)
java
   @ApiOperation("管理员查询用户")
    @GetMapping("/search")
    public BaseResponse <List<User>> getUserById (String username, HttpServletRequest request){
        if (!userService.isAdmin(request)) {         //调用逻辑,判断是否为管理员登录
            throw new BusinessException(ErrorCode.NO_AUTH,"您不是管理员   !");
        }
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if (StringUtils.isNotBlank(username)) {
            queryWrapper.like("user_name", username);
        }
        List<User> userList = userService.list(queryWrapper);
        List<User> list = userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
        return ResultUtils.success(list);
    }

🕺常用于查询所有用户

3. 如何将数据进行脱敏处理,再返回

  1. 服务类
java
/**
 * 用户脱敏
 * @param originUser
 * @return User
 */
@Override public User getSafetyUser(User originUser) {
    if (originUser == null) {
        throw new BusinessException(ErrorCode.PARAMS_ERROR,"脱敏数据为空!");
    }
    User safetyUser = new User();
    safetyUser.setId(originUser.getId());
    safetyUser.setUserName(originUser.getUserName());
    safetyUser.setUserAccount(originUser.getUserAccount());
    safetyUser.setAvatarUrl(originUser.getAvatarUrl());
    safetyUser.setGender(originUser.getGender());
    safetyUser.setPhone(originUser.getPhone());
    safetyUser.setEmail(originUser.getEmail());
    safetyUser.setPlanetCode(originUser.getPlanetCode());
    safetyUser.setUserRole(originUser.getUserRole());
    safetyUser.setUserStatus(originUser.getUserStatus());
    safetyUser.setCreateTime(originUser.getCreateTime());
    return safetyUser;
}
  1. 实现类
java
   /**
     *用户脱敏
     * @param originUser
     * @return
     */
    User getSafetyUser(User originUser);
  1. controller业务类
java
   //查询用户中返回的数据需要脱敏
List<User> userList = userService.list(queryWrapper);
List<User> list = userList.stream().map(user -> userService.getSafetyUser(user)).collect(Collectors.toList());
return ResultUtils.success(list);

4、Spring Boot 和Spring MVC 和Spring的区别

  • Spring:是一个开源的Java应用程序框架,用于构建企业级Java应用程序。

  • Spring MVC:是Spring框架中的一个Web应用程序开发框架,用于构建Web应用程序,实现了MVC(模型-视图-控制器)模式

  • Spring Boot:是基于Spring框架的快速应用程序开发框架,旨在简化和加速Spring应用程序的创建和配置过程。

    简而言之,Spring是整个框架,提供了丰富的功能和特性。Spring MVC是Spring框架中的一部分,专注于构建Web应用程序。Spring Boot是Spring框架的扩展,旨在简化和加速应用程序的开发过程。使用Spring Boot可以更快地启动和开发Spring应用程序,而Spring MVC是用于处理Web请求的一种方式。

5.后端项目经典分层架构介绍

分层架构

表示层 通常是指让用户交互和查看信息的前端界面,比如用户点击按钮后能够发送一个请求,也可以叫用户层、界面层等。 发送请求后,会经过 接入层 ,比如 Nginx 网关、或者其他中间件,对请求做一个预处理或转发,比如实现负载均衡。这一层不是必须存在的,通常更适用于中大型项目,前端也可以直接请求后端。

接入层会将请求转发到 控制层(Controller),负责接受请求、调用业务逻辑层(Service)的代码实现功能、然后响应结果。控制层一般不建议写复杂的业务逻辑,尽量保持精简。

接下来是 业务逻辑层(Service),负责处理复杂的业务逻辑,比如对请求数据进行校验、处理、调用数据访问层以将结果存到数据库中等,也是我们做系统时主要开发编码的部分。

通用业务层(Manager、Module)是一种特殊的业务逻辑层,主要的作用是抽取了一些需要被多个业务调用的公共代码,比如上传文件到对象存储、鉴权等,从而实现复用。

数据访问层(Dao / Mapper)负责操作底层的数据源,比如对数据库、文件、缓存等进行增删改查。

最后是 系统资源层 ,也可以叫基础设施层,包括各种基础服务、系统环境等,比如数据库、消息队列、Redis、文件存储、Linux 系统、Docker 等。复杂的基础设施可能还包括 K8S 容器资源编排、资源调度平台等。

示例项目结构

基于分层架构,我们可以将项目按照特定的目录名(包名)来组织代码,比如:

  • controller:控制层

  • service:业务逻辑层

  • mapper:数据访问层

  • model:数据模型

还可以按照业务或文件的类型来划分目录,比如:

  • constant:常量
  • annotation:注解类
  • common:公共类
  • config:配置类
  • job:任务
  • exception:异常处理相关
  • utils:工具类

其他知识

1)计算机网络也是采用了经典的分层架构,OSI 七层参考模型中,把计算机网络自底向上分为了物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。每个层只处理特定的功能,比如数据传输、数据的路由;层与层之间通过接口(或者叫协议)进行通信。

2)需要注意的是,我们常用的后端开发框架 Spring MVC 是基于 MVC(Model-View-Controller)设计模式构建的,而不能算是传统的分层架构。而且一般现在的项目中只使用 Spring MVC 作为整个项目的控制层,不过大多数用了 Spring MVC 框架的项目基本都使用了分层架构。

6、测试接口,配置swagger文档

根据官方文档选择适配自己的SpringBoot2.0或者3.0版本

官方链接:https://doc.xiaominfo.com/

Knife4j 的用法很简单,按照官方文档的指引,首先根据自己项目的 Spring Boot 版本引入对应的依赖,比如 Spring Boot 2.x 推荐使用以下 Maven 配置:

xml
<dependency>
    <groupId>com.github.xiaoymin</groupId>
    <artifactId>knife4j-openapi2-spring-boot-starter</artifactId>
    <version>4.3.0</version>
</dependency>

然后在 Spring Boot 的配置文件 application.yml 中添加 Knife4j 相关配置,比如:

yaml
knife4j:
  enable: true
  openapi:
   	title: Knife4j 文档

Knife4j 的文档地址(一般是项目地址 + /doc.html),即可查看自动根据 Controller 接口代码而生成的接口文档。

Knife4j 的底层也是依赖 Swagger 的,所以你还可以使用 Swagger 注解来自定义接口的信息。 比如下列代码,就使用了 Swagger 的 @Api 和 @ApiOperation 注解给接口添加名称和描述信息:

java
@RestController
@RequestMapping("/api/posts")
@Api(tags = "帖子管理")
public class PostController {

    @PostMapping
    @ApiOperation(value = "创建帖子", notes = "用于创建新的帖子")
    public ResponseEntity<String> createPost(@RequestBody PostRequest postRequest) {
        // 在这里实现创建帖子的逻辑
        return ResponseEntity.ok("帖子创建成功");
    }
}

7、MultipartHttpServletRequest的作用:

方便地处理带有文件上传的多部分请求。通过该接口,我们可以获取上传的文件、访问文件的元数据以及执行文件上传相关的操作。

java
@PostMapping("/upload")
public String handleFileUpload(MultipartHttpServletRequest request) {
    MultipartFile file = request.getFile("file"); // 获取上传的文件

    if (file != null) {
        try {
            // 执行文件处理操作,如保存到磁盘、处理文件内容等
            file.transferTo(new File("/path/to/save/" + file.getOriginalFilename()));
            return "File uploaded successfully!";
        } catch (IOException e) {
            return "Failed to upload file.";
        }
    }
    
    return "No file uploaded.";
}

8、配置表的设计

1.设计配置表

sql
CREATE TABLE `perf_payment_cfg` (
  `id` int unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',
  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '类型 WECHAT_PAYMENT-微信支付 ALIPAY_PAYMENT-支付宝支付',
  `setting_value` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin COMMENT '配置值value',
  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志(0代表存在 2代表删除)',
  `create_by` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb3 COMMENT='支付配置表';

🎫type:枚举类型

🎫setting_value:配置数值(json数据)

image-20240312092614951

2.编写控制层代码

获取配置信息详情代码逻辑:

  • 步骤一、使用 SettingEnum.value查询前端传入的数值为什么(枚举)
  • 步骤二、在定义的(配置表)实体类中通过使用key查询,对应的数值
  • 步骤三、使用switch判断用户输入的值是什么?
  • 步骤四、如果用户输入的微信支付配置,看数据库中是否有信息,如果没有就返回一个新对象(vo),如果有则返回将实体类中的value的json格式的数据转换为Java对象
  • 步骤五、如果用户输入的支付宝支付配置,看数据库中是否有信息,如果没有就返回一个新对象(vo),如果有则返回将实体类中的value的json格式的数据转换为Java对象
  • 步骤六、使用default 抛出异常

修改配置信息代码逻辑:

  • 步骤一、使用SettingEnum.value查看用户输入的数值是什么?
  • 步骤二、在实体类中传入需要查询的数据,使用key,实际查询settingEnum.name()
  • 步骤三、如果没有得到查询到的对象,则新增一个对象,并将前端传入的数值保存在对象中
  • 步骤四、如果查找到了对象,则在对象中设置value值(appid , 秘钥,key)
  • 步骤五、最后保存数据到实体类中
  • 步骤六、返回成功的状态码
java
  /**
     * 获取支付配置详细信息
     */
    @ApiOperation("获取支付配置详细信息")
    @PreAuthorize("@ss.hasPermi('payment:cfg:query')")
    @GetMapping(value = "/{key}")
    public AjaxResult getInfo(@PathVariable("key") String key)
    {
    //获取type值,看输入的输什么类型
        SettingEnum settingEnum = SettingEnum.valueOf(key);
        //在实体类中通过前端传入的type查询需要的数据
        PerfPaymentCfgEntity perfPaymentCfgEntity = perfPaymentCfgEntityService.selectPerfPaymentCfgEntityById(key);
        switch (settingEnum) {
            //支付宝支付配置
            case ALIPAY_PAYMENT:
                return perfPaymentCfgEntity ==null ?
                        success(new AlipayPaymentSetting()):
                        success(JSONUtil.toBean(perfPaymentCfgEntity.getSettingValue(), AlipayPaymentSetting.class));
            //微信支付配置
             case WECHAT_PAYMENT:
                 return perfPaymentCfgEntity == null?
                         success(new WechatPaymentSetting()):
                         success(JSONUtil.toBean(perfPaymentCfgEntity.getSettingValue(),WechatPaymentSetting.class));
            default:
                throw  new ServiceException(ResultCode.SETTING_NOT_TO_SET);
        }
    }

    /**
     * 新增支付配置
     */
    @PreAuthorize("@ss.hasPermi('payment:cfg:add')")
    @Log(title = "支付配置", businessType = BusinessType.INSERT)
    @PostMapping
    public AjaxResult add(@RequestBody PerfPaymentCfgEntity perfPaymentCfgEntity)
    {
        return toAjax(perfPaymentCfgEntityService.insertPerfPaymentCfgEntity(perfPaymentCfgEntity));
    }
    
     
    /**
     * 更新支付配置
     */
    @ApiOperation("更新支付配置")
    @PreAuthorize("@ss.hasPermi('payment:cfg:edit')")
    @Log(title = "支付配置", businessType = BusinessType.UPDATE)
    @PutMapping("put/{key}")
    public AjaxResult edit(@PathVariable String key,@RequestBody String settingValue)
    {
        // 1. 使用key获取用户请求的枚举值
        SettingEnum settingEnum =SettingEnum.valueOf(key);

        // 2. 获取系统中已经配置的配置
        PerfPaymentCfgEntity perfPaymentCfgEntity1 = perfPaymentCfgEntityService.selectPerfPaymentCfgEntityById(settingEnum.name());

        // 3. 如果没有得到对象,则新增一个对象,并将其表中的value值设置为枚举类型的名称(ALIPAY_PAYMENT,等等。。)
            //如果获取到的对象为空
        if (perfPaymentCfgEntity1 == null ){
            //创建一个新对象
            perfPaymentCfgEntity1 = new PerfPaymentCfgEntity();
            //将枚举值(ALIPAY_PAYMENT 或 WECHAT_PAYMENT) 保存在type中
            perfPaymentCfgEntity1.setType(settingEnum.name());
        }
        //如果不为空,则设置value值( appid, key, 秘钥)
        perfPaymentCfgEntity1.setSettingValue(settingValue);
        //将设置之后数值的进行保存
        perfPaymentCfgEntityService.updatePerfPaymentCfgEntity(perfPaymentCfgEntity1);
        return success();
    }

2.SettingEnum(设置枚举值)

java
package com.ruoyi.common.modules.system.entity.enums;

/**
 * 系统设置常量
 *
 * @author Chopper
 * @since 2020/9/11 17:03
 */
public enum SettingEnum {
    //基础配置
    BASE_SETTING,
    //提现设置
    WITHDRAWAL_SETTING,
    //分销设置
    DISTRIBUTION_SETTING,
    //邮箱配置
    EMAIL_SETTING,
    //商品设置
    GOODS_SETTING,
    //快递设置
    LOGISTICS_SETTING,
    //订单配置
    ORDER_SETTING,
    //阿里OSS配置
    OSS_SETTING,
    //阿里短信配置
    SMS_SETTING,
    //积分设置
    POINT_SETTING,
    //经验值设置
    EXPERIENCE_SETTING,
    //秒杀活动设置
    SECKILL_SETTING,
    //IM 配置
    IM_SETTING,

    //微信 联合登陆设置
    WECHAT_CONNECT,
    //QQ 浏览器 联合登录设置
    QQ_CONNECT,

    //各端支持支付设置
    PAYMENT_SUPPORT,
    //支付宝支付设置
    ALIPAY_PAYMENT,
    //微信支付设置
    WECHAT_PAYMENT,
    //银联支付设置
    UNIONPAY_PAYMENT,
    //热词设置
    HOT_WORDS
}

3. AlipayPaymentSetting(vale数值)VO

java
package com.ruoyi.common.modules.system.entity.dto.payment;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 支付宝支付设置
 *
 * @author Chopper
 * @since 2020-12-02 10:09
 */
@Data
@Accessors(chain = true)
public class AlipayPaymentSetting {

    /**
     * 应用id
     */
    private String appId;

    /**
     * 私钥
     */
    private String privateKey;

    /**
     * 应用证书
     */
    private String certPath;

    /**
     * 支付宝公钥
     */
    private String alipayPublicCertPath;

    /**
     * 支付宝根证书
     */
    private String rootCertPath;

}

4. WechatPaymentSetting(vale数值)VO

java
package com.ruoyi.common.modules.system.entity.dto.payment;

import lombok.Data;
import lombok.experimental.Accessors;

/**
 * 微信支付设置
 *
 * @author Chopper
 * @since 2020-12-02 10:08
 */
@Data
@Accessors(chain = true)
public class WechatPaymentSetting {

    /**
     * APP应用id
     */
    private String appId;
    /**
     * 小程序应用id
     */
    private String mpAppId;
    /**
     * 服务号应用id
     */
    private String serviceAppId;
    /**
     * 商户号
     */
    private String mchId;
    /**
     * 私钥
     */
    private String apiclient_key;
    /**
     * pem 证书
     */
    private String apiclient_cert_pem;
    /**
     * p12 证书
     */
    private String apiclient_cert_p12;
    /**
     * 商户证书序列号
     */
    private String serialNumber;
    /**
     * apiv3私钥
     */
    private String apiKey3;
}

5.mapper.xml的修改

前端传来的Key值,在数据库中也就是type

xml
    <select id="selectPerfPaymentCfgEntityById" parameterType="String" resultMap="PerfPaymentCfgEntityResult">
        <include refid="selectPerfPaymentCfgEntityVo"/>
        where type = #{key}
    </select>

6.启动成功之后进行测试

获取详情的测试

image-20240313004248392

修改配置的请求

image-20240313004504625

9.JWT令牌(如果没有令牌则跳转登录)

1.jwt的组成

  • 第一部分:Header(头) 作用:记录令牌类型,签名算法
  • 第二部分:payload(有效荷载):携带一些用户信息及其过期时间
  • 第三部分:Signature(签名):防止token被篡改,确保安全性

image-20240318211912117

2.JWT的生成

1.引入依赖

xml
<!-- jwt核心源码库 -->
		<dependency>
			<groupId>com.auth0</groupId>
			<artifactId>java-jwt</artifactId>
			<version>3.3.0</version>
		</dependency>
		<!-- java开发jwt的依赖jar包 -->
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.0</version>
		</dependency>

image-20240318214034145

3、JWT实战使用

  1. 使用工具类

    java
      //根据负载生成jwt token
        private String createToken(Map<String, Object> claims) {
            //jjwt构建jwt builder
            //设置信息,过期时间,signnature
            return Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(expirationDate())
                    .signWith(SignatureAlgorithm.ES512, SECRET)
                    .compact();
        }
      //生成token失效时间
        private Date expirationDate() {
            //失效时间为:系统当前毫秒数+我们设置的时间(s)*1000=》毫秒
            //其实就是未来7天
            return new Date(System.currentTimeMillis() + expiration * 1000);
        }

10、过滤器和拦截器的区别

DANGER

在Java中,拦截器和过滤器都是常见的组件,用于拦截和处理请求、操作或事件。它们在不同的应用场景中有各自的具体应用。

拦截器的具体应用场景包括:

  1. 身份验证和授权:拦截器可以用于验证用户的身份,并进行访问控制和授权检查。例如,在Web应用程序中,可以使用拦截器来拦截请求,并检查用户的登录状态或角色权限。
  2. 日志记录:拦截器可以用于记录请求和响应的信息,以及应用程序的行为。通过在拦截器中添加日志记录功能,可以方便地跟踪和审计应用程序的运行情况,包括请求的处理时间、错误日志等。
  3. 异常处理:拦截器可以捕获应用程序中的异常情况,并进行相应的处理。例如,可以使用拦截器捕获异常并返回自定义的错误页面或错误消息,以改善用户体验。
  4. 缓存和性能优化:拦截器可以用于实现请求或响应的缓存,以提高应用程序的性能和响应时间。通过在拦截器中检查缓存是否存在并有效,可以避免不必要的计算或数据库访问,从而减轻服务器的负载。

DANGER

过滤器的具体应用场景包括:

  1. 请求过滤和预处理:过滤器可以在请求到达应用程序之前拦截并对其进行预处理。例如,在Web应用程序中,过滤器可以用于验证请求的合法性、解析请求参数、设置字符编码等。
  2. 响应过滤和后处理:过滤器可以在应用程序生成响应后对其进行过滤和处理。例如,可以使用过滤器对响应进行压缩、加密、设置响应头等操作,以优化响应的传输和处理。
  3. 安全性和防护:过滤器可以用于实施安全策略和防护机制。例如,在Web应用程序中,过滤器可以用于防止跨站脚本攻击(XSS)、SQL注入等安全漏洞。
  4. 请求转发和重定向:过滤器可以用于请求转发或重定向到不同的资源或URL。例如,在Web应用程序中,过滤器可以拦截请求并将其转发到不同的Servlet或JSP页面,实现请求的路由和分发。

这些是拦截器和过滤器在Java中的一些常见应用场景,但具体的使用方式和实现可能会根据应用程序的需求和框架的要求而有所不同。

11.get和post请求的使用(todo❌未完成)

1.get请求

java
    // todo使用get请求
    @ApiOperation("获取用户配置详细信息get")
    @GetMapping(value = "/config")
    public AjaxResult getInfo(@RequestParam("type") String type) {
    /** 
    *   @RequestParam("type")绑定到访问域名
    *   如果type为空,则为他设置一个默认值,自动访问默认值@RequestParam(defaultvalue = "type")
    **/
        Object userConfig = loginUserService.getUserConfigInfo(type);
        return success(userConfig);
    }

2.post请求(使用DTO)(前端输入,后端查询)

1.controller层

java
 // todo使用post请求
    @ApiOperation("获取用户配置详细信息post")
    @PostMapping(value = "/config")
   /** 
    *  PerfUserConfigDTO,使用DTO,控制前端可以传输的参数
    **/
    public AjaxResult getInfoPost(@RequestBody PerfUserConfigDTO perfUserConfigDTO) {
        Object userConfig = loginUserService.getUserConfigInfo(perfUserConfigDTO.getType());
        return success(userConfig);
    }

2.servicesiml层

java

    /**
     * 查询支付配置
     *
     * @param type 支付配置主键
     * @return 支付配置
     */
    @Override
    public UserSetting getUserConfigInfo(String type)
    {
        //获取枚举类型
        SettingEnum settingEnum = SettingEnum.valueOf(type);

        //拿配置表中的value值
        PerfConfigEntity perfConfigEntity = perfPaymentCfgEntityMapper.selectPerfPaymentCfgEntityById(type);

        switch (settingEnum) {
            //超级会员
            case SUPER_MEMBER_SETTING:
                //推荐好友
            case RECOMMEND_FRIENDS_SETTING:
                //超级会员权益
            case SUPER_MEMBER_RIGHTS_SETTING:
                return perfConfigEntity == null ? new UserSetting() :
                        JSONUtil.toBean(perfConfigEntity.getSettingValue(), UserSetting.class);
            default:
                throw new ServiceException(ResultCode.SETTING_NOT_TO_SET);
        }

    }

3.DTO层

java
package com.perf.dto.User;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * @author bohai
 * @date :2024-03-29
 **/
@Data
public class PerfUserConfigDTO {

    @ApiModelProperty("用户配置类型")
    private String type;
}

3.get请求(使用VO)(后端查询,前端展示)

1.controller

java
	@ApiOperation(value = "小程序用户详情")
    @GetMapping(value = "/info")
    public AjaxResult getUserInfo(@RequestParam("userId") Long userId) {
        return success(loginUserService.getUserInfo(userId));
    }

2.impl

java
    /**
     * 小程序用户详情
     * @param userId userId
     * @return
     */
    @Override
    public PerfUserInfoVO getUserInfo(Long userId) {
        PerfUserEntity perfUser = baseMapper.selectPerfUserById(userId);
        if (ObjectUtil.isEmpty(perfUser)){
            throw new ServiceException("用户不存在");
        }
        PerfUserInfoVO resultVO = new PerfUserInfoVO();
        BeanUtil.copyProperties(perfUser,resultVO);
        return resultVO;
    }

3.VO

java
 // todo

12.个人信息返回接口(使用枚举--->url返回json)

前端原型页面

image-20240402211749165

实现步骤

  • 1、写用户配置接口获取(url)详情信息,传入type输出url

    • 输入值: type

    • 输出值: UserConfigVO

    • java
      /**
       * @author bohai
       * @date :2024-03-25
       **/
      @Data
      @Accessors(chain = true)
      public class UserConfigVO {
      
      
          @ApiModelProperty(value = "会员_url")
          private String vipUrl;
          @ApiModelProperty(value = "推荐_url")
          private String rmUrl;
          @ApiModelProperty(value = "超级会员_url")
          private String superUrl;
      
          @ApiModelProperty(value = "超级会员权益说明")
          private String superVipInstructions;
      
          @ApiModelProperty(value = "邀请有礼说明")
          private String inviteCourtesyInstructions;
      
      }
  • 2、静态数据使用set进行直接设置

  • java

// 静态数据 userConfigVO.setSuperVipInstructions("开工季!超级会员Pro买1年送3月"); userConfigVO.setInviteCourtesyInstructions("六一活动获得 20 % 奖励");


- 3、因为输入一次type,需要返回**所有的url**,输入的**key是List<String>集合**

- ```java
List<String> keys = Arrays.asList(
    //Arrays.asList数组或一系列元素转换成一个固定大小的列表(List)
                SettingEnum.SUPER_MEMBER_SETTING.getCode(),
                SettingEnum.RECOMMEND_FRIENDS_SETTING.getCode(),
                SettingEnum.SUPER_MEMBER_RIGHTS_SETTING.getCode()
        );
  • 4、根据Key查询数据库中是否有数据,否则返回VO对象

  • java
      // 查询数据库信息
             List<PerfConfigEntity> configEntities = perfPaymentCfgEntityMapper.selectPerfPaymentCfgByKeys(keys);
             if (CollectionUtils.isEmpty(configEntities)) {
                 return userConfigVO;
             }
  • 5、将查询结果按照类型分组

  • java
       //将查询结果按照类型分组,得到一个Map,键的类型是String,值的类型是PerfConfigEntity
     Map<String, List<PerfConfigEntity>> configEntitiesMap = configEntities.stream()
                     .collect(Collectors.groupingBy(PerfConfigEntity::getType));
  • 6、设置vip, 设置推荐好友,设置超级会员 的url ❌

    java
      // 设置VIP url
            List<PerfConfigEntity> vipUrlList = configEntitiesMap.get(SettingEnum.SUPER_MEMBER_SETTING.getCode());
            if (!CollectionUtils.isEmpty(vipUrlList)) {
                String jsonSettingValue = vipUrlList.get(0).getSettingValue();
                // 使用Gson的JsonParser来解析JSON字符串
                JsonElement jsonElement = JsonParser.parseString(jsonSettingValue);
                JsonObject asJsonObject = jsonElement.getAsJsonObject();
                String url = asJsonObject.get("url").getAsString();
                userConfigVO.setVipUrl(url);
            }
    java
       // 设置推荐好友 url
            List<PerfConfigEntity> rmUrlList = configEntitiesMap.get(SettingEnum.RECOMMEND_FRIENDS_SETTING.getCode());
            if (!CollectionUtils.isEmpty(rmUrlList)) {
                String jsonSettingValue = rmUrlList.get(0).getSettingValue();
                // 使用Gson的JsonParser来解析JSON字符串
                JsonElement jsonElement = JsonParser.parseString(jsonSettingValue);
                JsonObject asJsonObject = jsonElement.getAsJsonObject();
                String url = asJsonObject.get("url").getAsString();
                userConfigVO.setRmUrl(url);
            }
    java
    // 设置超级会员 url
            List<PerfConfigEntity> superUrlList = configEntitiesMap.get(SettingEnum.SUPER_MEMBER_RIGHTS_SETTING.getCode());
            if (!CollectionUtils.isEmpty(superUrlList)) {
                String jsonSettingValue = superUrlList.get(0).getSettingValue();
                // 使用Gson的JsonParser来解析JSON字符串
                JsonElement jsonElement = JsonParser.parseString(jsonSettingValue);
                JsonObject asJsonObject = jsonElement.getAsJsonObject();
                String url = asJsonObject.get("url").getAsString();
                userConfigVO.setSuperUrl(url);
            }
  • 6、serviceimpl层

  • java
     /**
         * 小程序-->获取用户配置
         *
         * @param key 类型
         * @return UserConfigVO
         */
        @Override
        public UserConfigVO getUserConfigInfo(String key) {
            UserConfigVO userConfigVO = new UserConfigVO();
      
            // 静态数据
            userConfigVO.setSuperVipInstructions("开工季!超级会员Pro买1年送3月");
            userConfigVO.setInviteCourtesyInstructions("六一活动获得 20 % 奖励");
      
            // 查询的key
            List<String> keys = Arrays.asList(
                    SettingEnum.SUPER_MEMBER_SETTING.getCode(),
                    SettingEnum.RECOMMEND_FRIENDS_SETTING.getCode(),
                    SettingEnum.SUPER_MEMBER_RIGHTS_SETTING.getCode()
            );
      
            // 查询数据库信息
            List<PerfConfigEntity> configEntities = perfPaymentCfgEntityMapper.selectPerfPaymentCfgByKeys(keys);
            if (CollectionUtils.isEmpty(configEntities)) {
                return userConfigVO;
            }
      
            // 将查询结果按类型分组
            Map<String, List<PerfConfigEntity>> configEntitiesMap = configEntities.stream()
                    .collect(Collectors.groupingBy(PerfConfigEntity::getType));
      
            // 设置VIP url
            List<PerfConfigEntity> vipUrlList = configEntitiesMap.get(SettingEnum.SUPER_MEMBER_SETTING.getCode());
            if (!CollectionUtils.isEmpty(vipUrlList)) {
                String jsonSettingValue = vipUrlList.get(0).getSettingValue();
                // 使用Gson的JsonParser来解析JSON字符串
                JsonElement jsonElement = JsonParser.parseString(jsonSettingValue);
                JsonObject asJsonObject = jsonElement.getAsJsonObject();
                String url = asJsonObject.get("url").getAsString();
                userConfigVO.setVipUrl(url);
            }
        // 设置推荐好友 url
        List<PerfConfigEntity> rmUrlList = configEntitiesMap.get(SettingEnum.RECOMMEND_FRIENDS_SETTING.getCode());
        if (!CollectionUtils.isEmpty(rmUrlList)) {
            String jsonSettingValue = rmUrlList.get(0).getSettingValue();
            // 使用Gson的JsonParser来解析JSON字符串
            JsonElement jsonElement = JsonParser.parseString(jsonSettingValue);
            JsonObject asJsonObject = jsonElement.getAsJsonObject();
            String url = asJsonObject.get("url").getAsString();
            userConfigVO.setRmUrl(url);
        }
      
        // 设置超级会员 url
        List<PerfConfigEntity> superUrlList = configEntitiesMap.get(SettingEnum.SUPER_MEMBER_RIGHTS_SETTING.getCode());
        if (!CollectionUtils.isEmpty(superUrlList)) {
            String jsonSettingValue = superUrlList.get(0).getSettingValue();
            // 使用Gson的JsonParser来解析JSON字符串
            JsonElement jsonElement = JsonParser.parseString(jsonSettingValue);
            JsonObject asJsonObject = jsonElement.getAsJsonObject();
            String url = asJsonObject.get("url").getAsString();
            userConfigVO.setSuperUrl(url);
        }
      
        return userConfigVO;
    }
  • 6、controller层

  • java
     @ApiOperation(value = "配置(url)详细信息", notes = "此接口无token可访问")
       @PostMapping(value = "/config")
       public R<UserConfigVO> getUserConfig(@RequestBody PerfUserConfigDTO perfUserConfigDTO) {
           UserConfigVO userConfigInfo = loginUserService.getUserConfigInfo(perfUserConfigDTO.getType());
           return R.ok(userConfigInfo);
       }
  • 7、mapper层

  • java
     public List<PerfConfigEntity> selectPerfPaymentCfgByKeys(@Param("keys") List<String> keys);
  • 8、mapper.xml

  • xml
        <select id="selectPerfPaymentCfgByKeys" resultType="com.perf.data.entity.config.PerfConfigEntity">
             SELECT * FROM perf_config WHERE type IN
             <foreach item="type" index="index" collection="keys" open="(" separator="," close=")">
                 #{type}
             </foreach>
         </select>

测试

测试数据

不用再controller设置,直接跳过了他,可以直接在mxl配置就可以使用

java
public List<PerfConfigEntity> selectPerfPaymentCfgByKeys(@Param("keys") List<String> keys);

实体类

java
package com.perf.data.entity.config;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.ruoyi.common.annotation.Excel;
import com.ruoyi.common.core.domain.BaseEntity;

/**
 * 支付配置对象 perf_payment_cfg
 * 
 * @author bohai
 * @date 2024-03-11
 */
@EqualsAndHashCode(callSuper = true)
@Data
@TableName("perf_config")
public class PerfConfigEntity extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** 主键ID */
    private String id;

    /** 类型 WECHAT_PAYMENT-微信支付 ALIPAY_PAYMENT-支付宝支付 */
    @Excel(name = "类型 WECHAT_PAYMENT-微信支付 ALIPAY_PAYMENT-支付宝支付")
    private String type;

    /** 配置值value */
    @Excel(name = "配置值value")
    private String settingValue;

    /** 删除标志(0代表存在 2代表删除) */
    private String delFlag;
}

13.当前个人信息接口(有逻辑)

原型页面

image-20240409092148636

实现步骤

1.service层

java
 /**
     * 小程序-->当前个人信息详情
     *
     * @return PerfUserInfoVO
     */
    @Override
    public PerfUserInfoVO getUserInfo() {
        PerfUserInfoVO resultVO = new PerfUserInfoVO();
        resultVO.setRemainingUses(0);
        resultVO.setHasVip(true);

        Long userId = SecurityUtils.getUserId();
        PerfUserEntity user = baseMapper.selectPerfUserById(userId);
        BeanUtil.copyProperties(user, resultVO);

        // 用户会员卡信息
        List<PerfUserVipEntity> userVipList = userVipService.selectUserVipListByUserId(userId, UserVipEnum.ACTIVATE);
        Optional.ofNullable(userVipList).ifPresent(list -> {
            if (list.isEmpty()) {
                // 未开通会员
                resultVO.setHasVip(false);
            } else {
                Map<Integer, List<PerfUserVipEntity>> userVipListMap = list.stream().collect(Collectors.groupingBy(PerfUserVipEntity::getServiceType));
                // 保险类会员卡
                userVipListMap.getOrDefault(VipServiceTypeEnum.INSURANCE.getCode(), Collections.emptyList()).stream()
                        .findFirst().ifPresent(userVip -> resultVO.setExpiryDate(userVip.getExpiryDate()));
                // AI类会员卡
                userVipListMap.getOrDefault(VipServiceTypeEnum.AI.getCode(), Collections.emptyList()).stream()
                        .findFirst().ifPresent(userVip -> resultVO.setRemainingUses(userVip.getRemainingUses()));
            }
        });

        // 推荐好友数
        Integer recommendedNumber = iPerfUserInvitedService.selectUserInvitedCount(userId);
        resultVO.setRecommendedNumber(recommendedNumber);

        // 权益次数
        List<UserVipBenefitVO> list = userVipService.selectUserVipBenefitListByUserId(userId);
        resultVO.setUserVipBenefitList(list);
        return resultVO;
    }

2、vo层

java
@Data
@EqualsAndHashCode(callSuper = true)
public class PerfUserInfoVO extends PerfUserEntity {

    @ApiModelProperty(value = "是否开通会员")
    private Boolean hasVip;

    @ApiModelProperty(value = "超级会员到期日")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date expiryDate;

    @ApiModelProperty(value = "AI短视频剩余次数")
    private Integer remainingUses;


    @ApiModelProperty(value = "推荐好友数")
    private Integer recommendedNumber;

    @ApiModelProperty(value = "累计获得x天会员")
    private Integer VIPEarnedDays;

    @ApiModelProperty(value = "用户的会员权益次数")
    private List<UserVipBenefitVO> userVipBenefitList;
}

3、接口的推荐好友数接口(调用)

java
   /**
     * 获取当前用户的推荐人数
     *
     * @return
     */
    @Override
    public Integer selectUserInvitedCount(Long userId) {
        try {
            LambdaQueryWrapper<PerfUserInvitedEntity> lqw = new LambdaQueryWrapper<>();
            lqw.eq(PerfUserInvitedEntity::getInviterId, userId);
            lqw.eq(PerfUserInvitedEntity::getDelFlag, UserStatus.OK.getCode());
            Integer count = baseMapper.selectCount(lqw).intValue();
            return count;
        } catch (Exception se) {
            log.error("查询用户邀请记录失败=>{}", userId);
        }
        return 0;
    }

3-1、实体类

java
@Data
@TableName("perf_user_invited")
public class PerfUserInvitedEntity extends BaseEntity
{
    private static final long serialVersionUID = 1L;

    /** id */
    private Long id;

    /** 邀请人id */
    @Excel(name = "邀请人id")
    private Long inviterId;
    /** 被邀请用户id */
    @Excel(name = "被邀请用户id")
    private Long userId;
    /** 小程序用户的openid */
    @Excel(name = "小程序用户的openid")
    private String openId;

    /** 用户昵称 */
    @Excel(name = "用户昵称")
    private String nickname;

    /** 用户头像url */
    @Excel(name = "用户头像url")
    private String avatarurl;

    /** 性别(0男  1女   2未知) */
    @Excel(name = "性别(0男  1女   2未知)")
    private Long gender;

    /** 生日 */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "生日", width = 30, dateFormat = "yyyy-MM-dd")
    private Date birthday;

    /** 创建/注册时间 */
    @JsonFormat(pattern = "yyyy-MM-dd")
    @Excel(name = "创建/注册时间", width = 30, dateFormat = "yyyy-MM-dd")
    private Date ctime;

    /** 手机号码 */
    @Excel(name = "手机号码")
    private String mobile;

    /** 邮箱 */
    @Excel(name = "邮箱")
    private String email;

    /** 密码 */
    @Excel(name = "密码")
    private String password;

    /** 删除标志(0代表存在 2代表删除) */
    private String delFlag;

}

4、权益次数接口(调用)

java
 /**
     * 保险类会员
     * 没有买,每日免费次数:free_daily_uses。perf_user_vip_daily_usage表记录了每日剩余次数
     * 买了,不限次数
     * AI短视频
     * 没有买,每日免费次数:free_daily_uses。perf_user_vip_daily_usage表记录了每日剩余次数
     * 买了, perf_user_vip.remaining_uses 记录剩余使用次数,max_uses记录最大使用次数
     *
     * @param userId
     * @return
     */
    @Override
    public List<UserVipBenefitVO> selectUserVipBenefitListByUserId(Long userId) {
        List<UserVipBenefitVO> resultList = new ArrayList<>();
        try {
            Date nowDate = DateUtils.getNowDate();
            //获取所有权益及并关联用户购买的权益及次数
            resultList = baseMapper.selectUserVipBenefitListByUserId(userId);
            //获取每日会员卡使用情况
            LambdaQueryWrapper<PerfUserVipDailyUsageEntity> lqw = new LambdaQueryWrapper<>();
            lqw.eq(PerfUserVipDailyUsageEntity::getUserId, userId);
            lqw.eq(PerfUserVipDailyUsageEntity::getUsageDate, DateUtils.getDate());
            lqw.eq(PerfUserVipDailyUsageEntity::getDelFlag, UserStatus.OK.getCode());
            List<PerfUserVipDailyUsageEntity> dailyUsageList = userVipDailyUsageMapper.selectList(lqw);
            //整理剩余次数
            resultList.forEach(item -> {
                PerfUserVipDailyUsageEntity userVipDailyUsageEntity = dailyUsageList.size() > 0 ? dailyUsageList.stream().filter(r -> item.getUserId().equals(r.getUserId()) && item.getId().equals(r.getVipBenefitFeaturesId())).findFirst().orElse(null) : new PerfUserVipDailyUsageEntity();
                //超级会员 最大次数无上限,页面上不再显示剩余次数
                //非超级会员,最大次数为每日限免次数,每日剩余次数用perfUserVipDailyUsageEntity的剩余次数
                if ("1".equals(item.getServiceType().toString())) {
                    if (ObjectUtil.isNotNull(item.getUserId())) {
                        item.setMaxUses("不限次数");
                        item.setNumCount("不限次数");
                    } else {
                        item.setMaxUses(item.getFreeDailyUses().toString());
                        item.setRemainingUses(userVipDailyUsageEntity.getId() == null ? item.getFreeDailyUses() : userVipDailyUsageEntity.getRemainingUses());
                        item.setNumCount("今日:" + (userVipDailyUsageEntity.getId() == null ? item.getFreeDailyUses() + "/" + item.getFreeDailyUses().toString() : userVipDailyUsageEntity.getRemainingUses() + "/" + item.getFreeDailyUses())+" 条");
                    }
                } else if ("2".equals(item.getServiceType().toString())) {
                    //AI短视频会员,最大次数就用会员卡的最大次数,剩余次数用perfUserVip中的剩余次数
                    //非AI短视频会员,最大次数为0,使用每日限免次数,剩余次数用perfUserVipDailyUsageEntity的剩余次数
                    if (ObjectUtil.isNotNull(item.getUserId())) {
                        item.setNumCount("剩余:" + (item.getRemainingUses())+" 条");
                    } else {
                        item.setMaxUses(item.getFreeDailyUses().toString());
                        item.setRemainingUses(userVipDailyUsageEntity.getId() == null ? item.getFreeDailyUses() : userVipDailyUsageEntity.getRemainingUses());
                        item.setNumCount("今日:" + (userVipDailyUsageEntity.getId() == null ? item.getFreeDailyUses() + "/" + item.getFreeDailyUses() : userVipDailyUsageEntity.getRemainingUses() + "/" + item.getFreeDailyUses())+" 条");
                    }
                }
            });

            return resultList;
        } catch (Exception e) {
            log.error("查询会员权益次数异常=>{}", userId);
        }
        return resultList;
    }

14.发送手机验证码(待完成)

实现步骤

1、依赖注入

xml
<!--阿里云短信依赖-->
        <!-- aliyun-java-sdk-core -->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>3.7.1</version>
        </dependency>
        <!-- aliyun-java-sdk-dysmsapi -->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>1.1.0</version>
        </dependency>
<!--        实体类字段不为空注解-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>

<!--        jsonObject依赖包-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.28</version>
        </dependency>

<dependency>
 <!--Redis依赖 -->
 <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <version>3.1.5</version>

<!--连接池依赖 -->
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
    <version>2.8.0</version>
</dependency>

2、工具类

java
	package com.example.usercenter.Utils;

import cn.hutool.core.util.StrUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.ServiceException;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.profile.DefaultProfile;
import com.example.usercenter.model.domain.aliyun.AlibabaCloudSmsConfig;
import lombok.extern.slf4j.Slf4j;

import static com.example.usercenter.constant.UserConstant.OK;

/**
 * @author bohai
 * @date :2024-04-09
 **/
@Slf4j
public class AlibabaCloudSMSSendUtils {

    /**
     * 阿里云短信发送工具类
     * @Author: hanabi
     */

    public static String sendMessage(AlibabaCloudSmsConfig config, String phoneNumbers, String param){
            DefaultProfile profile = DefaultProfile.getProfile(config.getRegionId(), config.getAccessKeyId(), config.getAccessSecret());
            IAcsClient client = new DefaultAcsClient(profile);

            SendSmsRequest request = new SendSmsRequest();
            request.setPhoneNumbers(phoneNumbers);
            request.setSignName(config.getSignName());
            request.setTemplateCode(config.getTemplateCode());
            if (!StrUtil.EMPTY.equals(param)){
                //有占位符数据
                request.setTemplateParam(param);
            }
            try {
                SendSmsResponse response = client.getAcsResponse(request);
                String code = response.getCode();
                if (OK.equals(code)){
                    System.out.println("短信发送成功");
                    log.info(response.toString());
                    //返回回执ID
                    return response.getBizId();
                }
                else {
                    throw new ServiceException("响应码:"+code+" 报错信息:"+response.getMessage());
                }
            }catch (ClientException | com.aliyuncs.exceptions.ClientException e) {
                throw new ServiceException(e.getMessage());
            }
        }
    }

3、配置实体类

java
package com.example.usercenter.model.domain.aliyun;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * @author bohai
 * @date :2024-04-09
 **/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AlibabaCloudSmsConfig {

    /**
     * regionId
     */
    private String regionId;

    /**
     * accessKeyId
     */
    private String accessKeyId;

    /**
     * accessSecret
     */
    private String accessSecret;

    /**
     * 模板code
     */
    private String templateCode;

    /**
     * 签名
     */
    private String signName;


    /**
     * 第三方服务名称
     */
    private String serviceName;

}

4、接口实体类DTO+对应的依赖包

java
package com.example.usercenter.model.DTO;

import io.swagger.annotations.ApiModelProperty;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Pattern;

/**
 * @author bohai
 * @date :2024-04-09
 **/
public class SmsLoginDTO {
    /**
     * 手机号
     */
    @ApiModelProperty(value = "手机号", required = true)
    @NotEmpty(message = "手机号不能为空")
    private String mobile;


    /**
     * 手机验证码
     */
    @ApiModelProperty(value = "手机验证码", required = true, example = "1024")
    @NotEmpty(message = "手机验证码不能为空")
    @Length(min = 4, max = 6, message = "手机验证码长度为 4-6 位")
    @Pattern(regexp = "^[0-9]+$", message = "手机验证码必须都是数字")
    private String code;
}

5、发送短信接口

0-1.sql语句创建

sql
CREATE TABLE `sys_channel_account` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '账号名称',
  `send_channel` tinyint NOT NULL DEFAULT '0' COMMENT '消息发送渠道:10.Email 20.短信 30.钉钉机器人 40.微信服务号 50.push通知栏 60.飞书机器人',
  `account_config` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '账号配置',
  `creator` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'Hanabi' COMMENT '拥有者',
  `created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除:0.不删除 1.删除',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_send_channel` (`send_channel`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='渠道账号信息';

0.用户常量

java
  /**
     * 短信发送成功常量
     */
    String OK = "OK";

    /**
     * 用于提取第三方短信服务名称的JASONObject key
     */
    public static final String SMS_SERVICE_KEY = "serviceName";

0.实体类层

java
package com.example.usercenter.model.entity.message;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

/**
 * @author bohai
 * @date :2024-04-09
 **/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@TableName("sys_channel_account")
public class SysChannelAccount {
    private static final long serialVersionUID = 1L;

    /**
     * 主键
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    /**
     * 账号名称
     */
    private String name;

    /**
     * 消息发送渠道:消息发送渠道:10.Email 20.短信 30.钉钉机器人 40.微信服务号
     */
    private Integer sendChannel;

    /**
     * 账号配置
     */
    private String accountConfig;

    /**
     * 拥有者
     */
    private String creator;

    /**
     * 创建时间
     */
    @JsonFormat( pattern = "yyyy-MM-dd")
    private LocalDateTime created;

    /**
     * 更新时间
     */
    @JsonFormat( pattern = "yyyy-MM-dd")
    private LocalDateTime updated;

    /**
     * 是否删除:0.不删除 1.删除
     */
    private Integer isDeleted;

}

1.接口层

java
  @Override
    public Boolean sendSmsCode(SmsLoginDTO reqVO) {
        SysChannelAccount sysChannelAccount = new SysChannelAccount();
        sysChannelAccount.setSendChannel(20);//短信
        //获取短信模板
        List<SysChannelAccount> sysChannelAccounts = channelAccountService.selectSysChannelAccountList(sysChannelAccount);
        if(ObjectUtil.isEmpty(sysChannelAccounts)){
            log.error("获取短信模板失败,短信模板为空!");
            throw new ServiceException("发送短信失败,请重试!");
        }
        //todo 如果存在多个短信模板,暂时先默认用第一个
        String accountConfig = sysChannelAccounts.get(0).getAccountConfig();
        //生成验证码+发送短信
        sendSms(reqVO,accountConfig);
        return true;
    }

    /**
     * 生成验证码+发送短信
     * @param reqVO
     * @param accountConfig
     */
    private void sendSms(SmsLoginDTO reqVO,String accountConfig) {
        JSONObject jsonObject = null;
        jsonObject = JSONUtil.toBean(accountConfig, JSONObject.class);
        String channelName = (String) jsonObject.get(SMS_SERVICE_KEY);
        if (Objects.isNull(channelName)){
            throw new ServiceException("发送短信失败,请重试!");
        }
        AlibabaCloudSmsConfig config = JSONObject.parseObject(jsonObject.toJSONString(),AlibabaCloudSmsConfig.class);
        //生成验证码
        String code = RandomUtil.randomNumbers(6);
        //将验证码存储到redis中,设置5分钟的过期时间
        redisCache.setCacheObject(CachePrefix.VERIFICATION_KEY+":"+reqVO.getMobile(),code,300000, TimeUnit.MILLISECONDS);
        //发送短信
        AlibabaCloudSMSSendUtils.sendMessage(config,reqVO.getMobile(),"{\"code\":\""+ code +"\"}");
    }

15.分页查询(无传参->直接查)

1.service and serviceimpl

service层

java

package com.perf.common.service.aigc;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.perf.data.dto.aigc.DialogMessageDTO;
import com.perf.data.vo.aigc.DialogContentVO;
import com.ruoyi.common.core.domain.R;

/**
 * 后台ai对话
 * @author bohai
 * @date 2024-4-23
 */
public interface AiDialogService {

    /**
     * 后台ai对话查询
     * @param dialogMessageDTO userid
     * @return ai对话
     */
    IPage<DialogContentVO> getDialogContent(DialogMessageDTO dialogMessageDTO);
}

serviceimpl

java
package com.perf.common.service.aigc.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.perf.common.service.aigc.AiDialogService;
import com.perf.data.dto.aigc.DialogMessageDTO;
import com.perf.data.vo.aigc.DialogContentVO;
import com.perf.mapper.aigc.SysDialogMessagesMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * @author bohai
 * @date :2024-04-23
 * @description ai对话后台管理
 **/
@Service
@Slf4j
public class AiDialogServiceImpl implements AiDialogService {


    @Autowired
    private SysDialogMessagesMapper messagesMapper;

    /**
     *
     * @param dialogMessageDTO 用户id
     * @return ai对话
     * @description 后台获取ai对话(分页)
     */
    @Override
    public IPage<DialogContentVO> getDialogContent(DialogMessageDTO dialogMessageDTO){
        //防止后端没有传参
      int PageNum = dialogMessageDTO.getPageNum() != null ? dialogMessageDTO.getPageNum() : 1;
        int PageSize = dialogMessageDTO.getPageSize() != null ?dialogMessageDTO.getPageSize() :10;
        //进行分页
        Page<DialogContentVO> page = new Page<>(PageNum, PageSize);
        //查询数据
        List<DialogContentVO> dialogHistoryPage = messagesMapper.getDialogHistoryPage(page, dialogMessageDTO);
        //存储当前页的记录列表
        page.setRecords(dialogHistoryPage);
        return page;
    }

}

2.controller

java
package com.perf.controller.aigc;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.perf.common.service.aigc.AiDialogService;
import com.perf.data.dto.aigc.DialogMessageDTO;
import com.perf.data.vo.aigc.DialogContentVO;
import com.ruoyi.common.core.domain.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 *
 * 后台ai管理接口
 * @author bohai
 * @date :2024-04-23
 *
 **/
@Api(tags = "ai对话-接口")
@RestController
@RequestMapping("/system/ai")
@Slf4j
public class AiDialogController {
    @Autowired
    private AiDialogService aiDialogService;


    @PostMapping("/message")
    @ApiOperation(value = "ai对话内容查询")
    public R<IPage<DialogContentVO>> getDialogMessagePage(@RequestBody DialogMessageDTO dialogMessageDTO){
        IPage<DialogContentVO> dialogContent = aiDialogService.getDialogContent(dialogMessageDTO);
        return R.ok(dialogContent);
    }


}

3.DialogMessageDTO and PageDTO

DialogMessageDTO

java
package com.perf.data.dto.aigc;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.perf.data.dto.clause.PageDTO;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.Date;

/**
 * @author bohai
 * @date :2024-04-23-16:50
 * @description: ai对话DTO
 **/
@Data
public class DialogMessageDTO extends PageDTO {


}

PageDTO

java
package com.perf.data.dto.clause;

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import javax.validation.constraints.NotNull;

/**
 * @date: Created in 2024/4/12 23:28
 * @description: 分页参数
 */
@Data
public class PageDTO {
    @ApiModelProperty(value = "页")
    @NotNull(message = "pageNum 页不可为空")
    private Integer pageNum;
    
    @ApiModelProperty(value = " 码")
    @NotNull(message = "pageSize 码不可为空")
    private Integer pageSize;
}

4.mapper and mapper.xml

mapper

java
package com.perf.mapper.aigc;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.perf.data.dto.aigc.DialogMessageDTO;
import com.perf.data.entity.aigc.SysDialogMessagesEntity;
import com.perf.data.vo.aigc.DialogContentVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import java.util.List;

/**
 * @author: Lizyang
 * @date: Created in 2024/4/17 16:36
 * @description: AI对话消息表
 */
@Mapper
public interface SysDialogMessagesMapper extends BaseMapper<SysDialogMessagesEntity> {

    /**
     * 获取ai对话消息列表
     * @param page page
     * @param dialogMessageDTO messageDTO
     * @return List<DialogContentVO>
     */
    List<DialogContentVO> getDialogHistoryPage(Page<DialogContentVO> page, @Param("params")  DialogMessageDTO dialogMessageDTO);

}

mapper.xml

java
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.perf.mapper.aigc.SysDialogMessagesMapper">

    <resultMap id="perfDialogResult" type="com.perf.data.entity.aigc.SysDialogMessagesEntity">
        <result property="sessionId" column="session_id" />
        <result property="userId" column="user_id" />
        <result property="userName" column="user_name" />
        <result property="question" column="question" />
        <result property="message" column="message" />
        <result property="data" column="data" />
        <result property="messageList" column="message_list" />
        <result property="requestId" column="request_id" />
    </resultMap>

    <sql id="selectDialogMessageAll">
    select id,session_id, user_id, user_name,
           question, message, data,message_list,
           request_id, create_time from sys_dialog_messages
    </sql>

    <select id="getDialogHistoryPage" resultType="com.perf.data.vo.aigc.DialogContentVO">
    <include refid="selectDialogMessageAll"/>
    </select>
</mapper>

15-1分页查询,传参查询

mapper and mapper.xml

mapper.xml

xml
<select id="clauseProductList2" resultType="com.perf.data.vo.clause.ClauseProductAppVO">
        <include refid="selectPerfClauseProductVo"/>
        <where>
            <if test="params.name != null and params.name != ''">
                and (ins_name like concat(#{params.name}, '%') or company_organization_name like concat(#{params.name}, '%'))
            </if>
            <if test="params.companyId !=null and params.companyId!=''">
                and company_id=#{params.companyId}
            </if>
            <if test="params.categoryCodeList != null and !params.categoryCodeList.isEmpty()">
                and category_code in
                <foreach item="item" index="index" collection="params.categoryCodeList" open="(" separator="," close=")">
                    #{item}
                </foreach>
            </if>
        </where>
        order by is_dismantle desc
    </select>

mapper

java
   

/**
*
*
*/
List<DialogSessionsVO> getSessionHistoryPage(Page<DialogSessionsVO> page, @Param("params") DialogSessionsDTO sessionsDTO);

全量查询

Last updated: