springboot使用aop切面统一处理查询不同接口中接收的请求体,实现多条件分页查询

目录

需求描述

前端ajax请求调用查询接口示例

准备工作

引入相关依赖

实体类

controller层

service层接口

service层实现类

mapper层

mapper.xml中的selectAll复杂动态sql

控制层切面

工具类MyUtils

通用类DataVO

发送请求查看响应结果

ajax请求体

响应内容

 关键——切面增强

感想

致谢


需求描述

在我想要实现的效果中,前端调用查询接口时,请求体中携带以下数据:所查询实体类的查询条件字段(可能有多个条件,也可能没有查询条件)、分页查询的变量:page(当前页码)、limit(每页限制条数),

后端需要接收请求体的内容,可以根据调用接口不同判断是查询哪个实体类,创建出实体类,将对应的实体类和页码、条数传给servic层,service层传实体类给mapper的查询语句(mapper层中实现动态sql),使用mybatis的pagehelper插件实现分页将数据层层返回给前端。

以我自己的项目为例

前端ajax请求调用查询接口示例

$.ajax({url: "http://127.0.0.1:8080/counter/select",method: "POST",headers: {"token": "myToken"},  //由于我的项目拦截器进行了token验证,所以请求头带一个token,没有进行token验证可不用写请求头data:JSON.stringify({"page":1,"limit":5,"id":"A0001"    //要查询的字段}),contentType: "application/json;charset=utf-8",success: function (response) {console.log(response.data)}
});

准备工作

引入相关依赖

maven项目在pom.xml中添加

<!--spring-web依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--spring-aop依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!--mybatis-plus依赖,由于我的项目中需要很多复杂sql,所以依旧是按照mybatis来写的,mp相对于mybatis只做增强不做改变,依旧可以按mybatis用法用-->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version>
</dependency>
<!--mybatis分页插件——pagehelper-->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.6</version>
</dependency>
<!--mysql-->
<dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope>
</dependency>
<!--fastjson2,处理json数据-->
<dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.23</version>
</dependency>
<!--lombok注解-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional>
</dependency>

实体类

package com.cns.coldstoragesys.bean;import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;import java.util.Date;@Data
//@JsonInclude(JsonInclude.Include.NON_NULL)//删除返回前端时为null的字段
public class Counter {private String id;private Integer coldstorageId;private String type;private String state;private String pos;private Integer level;private String goodsId;/*添加临时字段,级联查询返回前端,方便数据表格获取关联数据*/@TableField(exist=false)private String coldstorageName;@TableField(exist = false)private String video;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")//json日期格式转换@TableField(exist = false)private Date startTime;@TableField(exist = false)private String description;@TableField(exist = false)private Integer length;@TableField(exist = false)private Integer width;@TableField(exist = false)private Integer height;
}

controller层

package com.cns.coldstoragesys.controller;import com.cns.coldstoragesys.bean.Counter;
import com.cns.coldstoragesys.common.DataVO;
import com.cns.coldstoragesys.common.SysConstant;
import com.cns.coldstoragesys.service.CounterService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;import java.util.Map;@RestController
@RequestMapping("/counter")
public class CounterController {@Autowiredprivate CounterService counterService;@PostMapping("/select")public DataVO selectAll(@RequestBody Map<String,Object> param){return counterService.selectAll((Counter) param.get(SysConstant.DEFAULT_BEAN_NAME),(Integer) param.get(SysConstant.DEFAULT_PAGE_NAME),(Integer) param.get(SysConstant.DEFAULT_LIMIT_NAME));}
}

service层接口

package com.cns.coldstoragesys.service;import com.cns.coldstoragesys.bean.Counter;
import com.cns.coldstoragesys.common.DataVO;public interface CounterService {DataVO selectAll(Counter counter,Integer page,Integer limit);
}

service层实现类

package com.cns.coldstoragesys.service.impl;import com.cns.coldstoragesys.bean.Counter;
import com.cns.coldstoragesys.common.DataVO;
import com.cns.coldstoragesys.common.SysConstant;
import com.cns.coldstoragesys.mapper.CounterMapper;
import com.cns.coldstoragesys.service.CounterService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;import java.util.List;
@Slf4j
@Service
@Transactional
public class CounterServiceImpl implements CounterService {@Autowiredprivate CounterMapper counterMapper;@Overridepublic DataVO selectAll(Counter counter,Integer page,Integer limit) {Page<Object> p = PageHelper.startPage(page,limit);try {List<Counter> counters = counterMapper.selectAll(counter);return new DataVO(SysConstant.CODE_SUCCESS,SysConstant.SELECT_SUCCESS,p.getTotal(),counters);} catch (Exception e) {log.error(e.toString());return new DataVO(SysConstant.CODE_ERROR,SysConstant.SELECT_ERROR);}}
}

mapper层

package com.cns.coldstoragesys.mapper;import com.cns.coldstoragesys.bean.Counter;
import org.apache.ibatis.annotations.Mapper;import java.util.List;
@Mapper
public interface CounterMapper {List<Counter> selectAll(Counter counter);
}

mapper.xml中的selectAll复杂动态sql

我写这么复杂是因为前端这个数据表格还需要其他三个表中的部分数据,所以进行了三个连接,同时实体类中用 @TableField(exist=false)注解添加了相应的临时字段,使用sql标签实现动态sql,如果携带的实体类中哪个字段不为空则说明其是查询条件。

<?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.cns.coldstoragesys.mapper.CounterMapper"><resultMap id="BaseResultMap" type="com.cns.coldstoragesys.bean.Counter"><id column="id" jdbcType="CHAR" property="id" /><result column="coldstorage_id" jdbcType="INTEGER" property="coldstorageId" /><result column="coldstorage_name" jdbcType="VARCHAR" property="coldstorageName" /><result column="type" jdbcType="CHAR" property="type" /><result column="state" jdbcType="VARCHAR" property="state" /><result column="pos" jdbcType="VARCHAR" property="pos" /><result column="level" jdbcType="INTEGER" property="level" /><result column="goods_id" jdbcType="CHAR" property="goodsId" /><result column="description" jdbcType="VARCHAR" property="description" /><result column="length" jdbcType="INTEGER" property="length" /><result column="width" jdbcType="INTEGER" property="width" /><result column="height" jdbcType="INTEGER" property="height" /><result column="video" jdbcType="VARCHAR" property="video" /><result column="start_time" jdbcType="TIMESTAMP" property="startTime"/></resultMap><select id="selectAll" parameterType="com.cns.coldstoragesys.bean.Counter" resultMap="BaseResultMap">select counter.id,counter.coldstorage_id,counter.type,counter.state,counter.pos,counter.`level`,counter.goods_id,type.description,type.length,type.width,type.height,record.video,record.start_time,cold.name as coldstorage_namefrom counterleft join coldstorage as cold on cold.id=counter.coldstorage_idleft join counter_type as type on type.id = counter.typeleft join record_access as record on record.start_time=(select MAX(record.start_time)from record_access as recordwhere record.counter_id=counter.idgroup by record.counter_id)<where><if test="null != coldstorageId and '' != coldstorageId">and counter.coldstorage_id=#{coldstorageId}</if><if test="null != coldstorageName and '' != coldstorageName">and counter.coldstorage_id=(select id from coldstorage where name like "%${coldstorageName}%")</if><if test="null != id and '' != id">and counter.`id`= #{id}</if><if test="null != type and '' != type">and counter.`type` = #{type}</if><if test="null != level and '' != level">and counter.`level` = #{level}</if><if test="null != state and '' != state">and counter.`state` = #{state}</if><if test="null != description and '' != description">and counter.type=(select id from counter_type where `description` like "%${description}%")</if></where>order by counter.id asc</select>
</mapper>

控制层切面

package com.cns.coldstoragesys.aspect;import com.alibaba.fastjson2.JSON;
import com.cns.coldstoragesys.common.SysConstant;
import com.cns.coldstoragesys.util.MyUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestBody;import java.util.HashMap;
import java.util.Map;@Aspect
@Component
public class ControllerAspect {//指定切点为controller目录中所有类的selectAll方法并且要求携带的参数是Map<String,Object> param@Pointcut(value = "execution(* com.cns.coldstoragesys.controller..selectAll(..)) && args(param)" ,argNames = "param")public void controllerPoint(Map<String, Object> param){}//指定环绕增强切面的切入点和参数名@Around(value = "controllerPoint(param) && args(..)",argNames= "joinPoint,param")public Object changeParam(ProceedingJoinPoint joinPoint,@RequestBody Map<String, Object> param) throws Throwable {Integer page = (Integer) param.get(SysConstant.DEFAULT_PAGE_NAME);  //获得param中用于分页的page和limit后将其移除,剩余在param中的键值对即为需要查询的条件param.remove(SysConstant.DEFAULT_PAGE_NAME);Integer limit = (Integer) param.get(SysConstant.DEFAULT_LIMIT_NAME);param.remove(SysConstant.DEFAULT_LIMIT_NAME);String className = MyUtils.getClassName(joinPoint.getTarget().getClass().getName());    //工具类获取全限定类名Class<?> clazz = Class.forName(className);  //反射机制创建类Object obj = JSON.parseObject(JSON.toJSONString(param), clazz); //将Map中剩余的键值对转为对应类型的json对象Map<String,Object> params=new HashMap<>();          //重新存放最后需要新返回的参数,procceed方法的参数需要一个Object数组,params.put(SysConstant.DEFAULT_BEAN_NAME,obj);      //但是controller层中的selectAll方法又只有一个参数,params.put(SysConstant.DEFAULT_PAGE_NAME,page);     //如果直接将键值对放到Object数组中将会报参数个数异常,params.put(SysConstant.DEFAULT_LIMIT_NAME,limit);   //所以这里将键值对放到Map中,再将Map放到Object数组中return joinPoint.proceed(new Object[]{params}); //procceed方法的参数需要一个Object数组,}
}

工具类MyUtils

package com.cns.coldstoragesys.util;import com.cns.coldstoragesys.common.SysConstant;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;public class MyUtils {public static String getClassName(String fullClassName) {Pattern pattern = Pattern.compile("\\.(\\w+)Controller$");Matcher matcher = pattern.matcher(fullClassName);if (matcher.find()) {return SysConstant.DEFAULT_BEAN_PATH+matcher.group(1);}return null;}
}

通用类DataVO

package com.cns.coldstoragesys.common;import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Data;import java.util.List;
@Data
@JsonPropertyOrder({"code","msg","count","data"})//指定返回给前端的字段顺序
public class DataVO<T> {private Integer code;private String msg;private Long count;private List<T> data;public DataVO() {}public DataVO(Integer code, String msg) {this.code = code;this.msg = msg;}public DataVO(Integer code, String msg, Long count, List<T> data) {this.code = code;this.msg = msg;this.count = count;this.data = data;}}

系统常量SysConstant

package com.cns.coldstoragesys.common;public interface SysConstant {Integer CODE_SUCCESS=0;     //操作成功Integer CODE_ERROR=1;       //操作失败String DEFAULT_BEAN_PATH="com.cns.coldstoragesys.bean.";String DEFAULT_PAGE_NAME="page";    //默认传递指定页数的变量名称,因为前端传来的数据是放在请求体中的,controller层接口通过Map<String,Object>接收,需要通过key取值Integer DEFAULT_PAGE=1;     //默认页数String DEFAULT_LIMIT_NAME="limit";Integer DEFAULT_LIMIT=10;   //默认条数String DEFAULT_BEAN_NAME="bean";Long REDIS_OVERDUE_TIME=30*24*60*60L;String DEFAULT_TOKEN_ISSUER="Yan";String DEFAULT_TOKEN_AUDIENCE="Huang";String SELECT_SUCCESS="查询成功";String SELECT_ERROR="查询失败";String ADD_SUCCESS="添加成功";String ADD_ERROR="添加失败";String DELETE_SUCCESS="删除成功";String DELETE_ERROR="删除失败";String UPDATE_SUCCESS="修改成功";String UPDATE_ERROR="修改失败";String NULL_VALUE="主键不存在";String REPEAT_VALUE="主键重复";String LOGIN_SUCCESS="登陆成功";String LOGIN_ERROR="登陆失败";String UNKNOW_ERROR="未知错误";
}

发送请求查看响应结果

ajax请求体

响应内容

 关键——切面增强

在这个切面中,使用@Around注解,其切点定义为controller包下所有不同实体类的controller层类中的selectAll方法,并且这个方法要带有一个Map<String,Object>类型的参数param,即如切点表达式

@Pointcut(value = "execution(* com.cns.coldstoragesys.controller..selectAll(..)) && args(param)" ,argNames = "param")

@Aspect
@Component
public class ControllerAspect {//指定切点为controller目录中所有类的selectAll方法并且要求携带的参数是Map<String,Object> param@Pointcut(value = "execution(* com.cns.coldstoragesys.controller..selectAll(..)) && args(param)" ,argNames = "param")public void controllerPoint(Map<String, Object> param){}//指定环绕增强切面的切入点和参数名@Around(value = "controllerPoint(param) && args(..)",argNames= "joinPoint,param")public Object changeParam(ProceedingJoinPoint joinPoint,@RequestBody Map<String, Object> param) throws Throwable {Integer page = (Integer) param.get(SysConstant.DEFAULT_PAGE_NAME);  //获得param中用于分页的page和limit后将其移除,剩余在param中的键值对即为需要查询的条件param.remove(SysConstant.DEFAULT_PAGE_NAME);Integer limit = (Integer) param.get(SysConstant.DEFAULT_LIMIT_NAME);param.remove(SysConstant.DEFAULT_LIMIT_NAME);String className = MyUtils.getClassName(joinPoint.getTarget().getClass().getName());    //工具类获取全限定类名Class<?> clazz = Class.forName(className);  //反射机制创建类Object obj = JSON.parseObject(JSON.toJSONString(param), clazz); //将Map中剩余的键值对转为对应类型的json对象Map<String,Object> params=new HashMap<>();          //重新存放最后需要新返回的参数,procceed方法的参数需要一个Object数组,params.put(SysConstant.DEFAULT_BEAN_NAME,obj);      //但是controller层中的selectAll方法又只有一个参数,params.put(SysConstant.DEFAULT_PAGE_NAME,page);     //如果直接将键值对放到Object数组中将会报参数个数异常,params.put(SysConstant.DEFAULT_LIMIT_NAME,limit);   //所以这里将键值对放到Map中,再将Map放到Object数组中return joinPoint.proceed(new Object[]{params}); //procceed方法的参数需要一个Object数组,}
}

这里用到的MyUtil工具类获取全限定类名方法,用正则表达式提取出xxxController对应的实体类名,加上系统常量中定义的Bean包位置,即就是全限定类名

public static String getClassName(String fullClassName) {Pattern pattern = Pattern.compile("\\.(\\w+)Controller$");Matcher matcher = pattern.matcher(fullClassName);if (matcher.find()) {return SysConstant.DEFAULT_BEAN_PATH+matcher.group(1);}return null;
}

在changeParam这个环绕增强方法中,将param中分页相关的两个变量page和limit拿到,将其从map移除,那么map中就只剩下我们需要查询的字段了,

可以通过joinPoint获取当前加强目标方法的controller层类名,比如当前对counter进行查询,那就是CounterController,如果是查询User,那就是UserController,因为我们结构规范,那么我们可以通过这个类名获取实体类的全限定名称,比如com.cns.bean.

获取到全限定类名后,通过反射机制创建类,

通过com.alibaba.fastjson2.JSON的方法将map转为对应实体类,因为这里的具体类我们不能确定,所以用Object接收,由controller层再强制转换成所需实体类,就实现效果了

因为是通用的切面,所以这个切面可以对多个controller进行增强,我有很多实体类的controller需要这样的加强

假如接口需要查询其他实体类,只需要换个接口一样能实现增强。

感想

虽然可以直接在Controller层中对传来的requestBody进行处理实现动态sql分页查询,但是这样很多个controller都需要多一段这样的重复性质代码,所以我就想放到切面中减少代码重复,这样controller还是只需要写一行,优雅美观

致谢

感谢ChatGPT,我有实现这个效果的想法,但其实自己并不能很好进行具体实现,很多bug是由询问ChatGPT才恍然大悟的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/9789.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

chatgpt赋能python:Python如何阻止弹窗

Python如何阻止弹窗 Python是一种高级编程语言&#xff0c;它具有广泛的应用和丰富的库。它还可以被用于开发自动化程序&#xff0c;包括阻止弹窗。在本文中&#xff0c;我们将介绍如何使用Python阻止弹出窗口&#xff0c;并探讨防止弹窗的原因。 为什么要防止弹窗&#xff1…

MySQL - 各种超时时间 - 学习与探究

1.应用场景 主要用于学习与探究MySQL各种超时时间&#xff0c;应用在合适的场景下. 2.学习/操作 1.文档阅读 https://wen.geekr.dev/ chatgpt & 官方文档 & 其他资料 2.整理输出 2.1 是什么 MySQL中有多个超时时间&#xff0c;以下是其中的几个&#xff1a; connect_…

大语言模型 AI 辅助编码使用过程体验报告(Github Copilot、Cursor)

编码感受和评估 在过去一周多的时间里&#xff0c;我在 ChatGPT 的协助下&#xff0c;生成了做一个简单编辑器的产品文档、技术方案文档&#xff0c;然后在这个基础上&#xff0c;进行程序的编码。 使用的工具纪要 为了更全面地感受 AI IDE 对研发过程的影响&#xff0c;我特…

产品设计师使用ChatGPT的十大妙招

掌握ChatGPT 提示列表,将大大提高产品设计师的效率。 微信搜索关注《Java学研大本营》&#xff0c;加入读者群&#xff0c;分享更多精彩 OpenAI 的 ChatGPT 无处不在&#xff0c;人们将其用于各种各样的事情&#xff0c;从作业作弊到构建产品。最近开始使用 ChatGPT 来摆脱空白…

postman接口报文返回:系统异常

场景&#xff1a;在做python脚本参数化时&#xff0c;同样的请求报文&#xff0c;在postman里可以发送成功&#xff0c;但是发给同事做自动化跑接口时&#xff0c;却返回"系统异常"。 今天在做Jmeter时&#xff0c;发现又出现了同样的问题。 原因&#xff1a;因为没…

postman能请求到后端接口,.HttpMessageNotReadableException: I/O error while reading input message; nested exce

postman能请求到后端接口,.但是前端发送请求&#xff0c;怎么请求&#xff0c;后端接口都没响应.... 前端项目是vue-element-admin 报错信息&#xff1a; HttpMessageNotReadableException: I/O error while reading input message; nested exception is org.apache.catalina…

关于POST发送数据过大,发送请求发生错误问题的原因及办法。

问题来自于生产的一个批量处理提交操作&#xff0c;当POST请求提交的数据量过大时&#xff0c;就会产生错误&#xff0c;发生例如&#xff1a;超时、504等等现象。惊讶之余&#xff0c;并不着急解决BUG&#xff0c;更想弄清楚为什么POST请求会出现这种情况&#xff0c;第一反应…

使用postman发送post请求,却报错不支持get请求的原因

场景复现 可以看到我们postman发出的确实是post请求&#xff0c;message却报错这个接口不支持get请求&#xff0c;说明服务器实际上收到的是一个get请求。 产生原因分析 如果我们访问的是线上的接口&#xff0c;线上的nginx一般都会对http访问做一个302重定向&#xff0c;跳转…

postman,浏览器测试接口正常,HttpClient 调用就报错

一次奔溃的经历 事情是这样的&#xff1a;第三方提供了一个接口需要对接&#xff0c;我就对接了&#xff0c;测试环节的时候怎么都调不通&#xff0c;各种排查&#xff0c;各方人员都动员了起来&#xff0c;就是没有找到问题&#xff0c;下面把问题报错的原因呈上&#xff1a; …

基于Sanic(Python)和ChatGPT的超级简易聊天应用

文章目录 一、项目简介&#xff1a;二、代码结构&#xff1a;三、具体代码&#xff1a;main.pytemplates/index.htmlstatic/css/custom.cssstatic/js/custom.js 四、使用方法:1. 安装依赖&#xff1a;2. 在main.py中替换自己的openai_key3. 运行项目&#xff1a;4. 打开浏览器&…

如何评价OpenAI的超级对话模型ChatGPT?

Trinkle回答&#xff1a; 有幸参与ChatGPT训练的全过程。直接上想法&#xff1a; RLHF会改变现在的research现状&#xff0c;个人认为一些很promising的方向&#xff1a;在LM上重新走一遍RL的路&#xff1b;如何更高效去训练RM和RL policy&#xff1b;写一个highly optimized R…

2019美研计算机录取,2019美研录取更新 | 春节OFFER大集锦,没有比OFFER更好的新年礼物了!...

原标题&#xff1a;2019美研录取更新 | 春节OFFER大集锦&#xff0c;没有比OFFER更好的新年礼物了! 嗨&#xff0c;春节有比收到红包更让人激动的事儿吗&#xff1f; 有&#xff01;比如收到OFFER&#xff01; 继二月初惊喜地收获两枚斯坦福大学的硕士录取后&#xff0c; 过去的…

美研计算机案例,[04.23]公开课丨美研计算机专业分享,让你进军米国IT届

【4.23公开课】美研计算机专业分享&#xff0c;让你进军米国IT届 活动类型:线上活动 开始时间:2015-4-23 20:00 活动地点:天道公开课交流群274304450 性别要求:不限 如今申请赴美读研的人越来越多&#xff0c;一方面美国著名大学的研究生院占据了世界高校专业排名前列的半壁江山…

2019美研计算机录取,2019美研录取更新 | 哥伦比亚大学、芝加哥大学OFFER携手来袭...

原标题&#xff1a;2019美研录取更新 | 哥伦比亚大学、芝加哥大学OFFER携手来袭 福 临近春节 集五福活动又双叒叕来了 福气满满的日子里 连OFFER雨都密了起来!!!!!!!! 今天第一个要恭喜的是来自上海交通大学的L同学 跨学科申请收获哥伦比亚大学公共卫生专业及芝加哥大学生物医学…

2019美研计算机录取,2019美研录取更新 | 又到周五,是时候晒OFFER了!

原标题&#xff1a;2019美研录取更新 | 又到周五&#xff0c;是时候晒OFFER了&#xff01; 又到周五&#xff01; 捂了几天的OFFER们 是时候挑一些拿出来晒晒了。 【今日OFFER雨重点预告】 宾夕法尼亚大学博士全奖录取一枚 佐治亚理工学院博士全奖录取一枚 约翰霍普金斯大学硕士…

商汤版ChatGPT「商量」来了,开放API!

国产ChatGPT之战&#xff0c;已然是大步迈进白热化阶段。 就在刚刚&#xff0c;商汤正式发布自研类ChatGPT产品&#xff0c;名曰商量&#xff08;SenseChat&#xff09;。 单单是这个名字&#xff0c;便值得说道一番。 商量的“商”&#xff0c;不仅体现了它是商汤自家“商字辈…

一加(oneplus)7 pro刷nethunter与Linux 下刷新版Android 9(P)手机(root)

在三年前我买了op3就为了刷nh&#xff0c;三年后我又买了一加&#xff0c;然而以为nh不怎么更新了就没想着要去刷。昨天看私信就突然去看了一眼&#xff0c;发现居然有op7/7p的刷机包&#xff0c;结果果断刷了&#xff08;不能白瞎这12GB的内存啊&#xff09;。 总体上来说nh的…

ChatGPT露馅了,它明明就是人

让人工智能理解句子成分和语义&#xff0c;这看起来是件不可能的事&#xff0c;看过流浪地球的都知道&#xff0c;那里面的人工智能哪怕发展到2057年&#xff0c;也听不懂比喻和反问。 那最近大火的chatGPT能不能听懂冷笑话呢&#xff1f;它不仅能写代码、论文&#xff0c;居然…

ChatGPT是有点中文在身上的:鲁迅、脱口秀甚至世界杯…都被玩宕机了

各大社交平台&#xff0c;最近突然掀起了一股晒聊天记录的热潮。 对方是个有求必应的角色&#xff0c;让它扮演虚拟女友、写论文、编请假理由&#xff0c;通通满足要求。 这中文能力、沟通技巧、知识水平……直接引得网友一水儿“牛X、无敌”。甚至有人说&#xff0c;强得令人…

短视频文案怎么写?优质短视频文案写作技巧

抖音短视频的质量仔细推敲起来确实会涉及到非常多的因素&#xff0c;但真正决定你视频曝光的关键就是视频文案&#xff0c;从剧情的铺垫、冲突、反转&#xff0c;这在一定程度上能够吸引不少的观众。 又或者小说开篇要么叙述宏达的故事背景&#xff0c;要么制造悬念。否则很难让…