当前位置: 首页 > news >正文

AndroidTV端:酒店扫码认证投屏DLNA

被老板叼了几次了,最近实在忍不了,准备离职;

但是担心离职后长时间没有办法找到工作

就想贡献一套平时琢磨出来的程序,请各位有能力的话带我熬过这凛冽的寒冬。

目前写出来的,有三个端:安卓TV端,安卓手机端,Windows-Qt端

这篇是TV端的部分代码,当然要理解一下,我并不能粘贴出完整的代码,请见谅;

想要获取完整源码可以直接私信我,万分感激

那下面就开始粘贴部分源码:

1.SSDP的发送

DLNA是通过监听网络当中的广播进行解析的,也就是在循环发送几个消息

这些消息,有的是关于设备信息的,有些是关于控制命令的,总之得根据自己的来

public String toString(int i) {//都是些固定格式,NT和USN是两辆相关StringBuilder content = new StringBuilder();content.append("NOTIFY * HTTP/1.1").append("\r\n");content.append("SERVER: Linux/3.14.29 UPnP/1.0 Cling/2.0").append("\r\n");content.append("CACHE-CONTROL: max-age=1800").append("\r\n");//指定通知消息存活时间,如果超过此时间间隔,控制点可以认为设备不存在content.append("LOCATION: "+_LOCATION).append("\r\n");//乐播投屏加了下面三行,加了就搜索不到了
//        content.append("OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01; ds=0").append("\r\n");
//        content.append("01-NLS: 0dcebdf8-1dd2-11b2-8b72-f07170d94291").append("\r\n");
//        content.append("X-User-Agent: redsonic").append("\r\n");content.append("NTS: ssdp:alive").append("\r\n");if(i==0){content.append("NT: upnp:rootdevice").append("\r\n");content.append("USN: uuid:"+ MainActivity._UUID+"::upnp:rootdevice").append("\r\n");}else if(i==1){content.append("NT: uuid:"+MainActivity._UUID).append("\r\n");content.append("USN: uuid:"+MainActivity._UUID).append("\r\n");} else if(i==2){content.append("NT: urn:schemas-upnp-org:device:MediaRenderer:1").append("\r\n");content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:device:MediaRenderer:1").append("\r\n");}else if(i==3){content.append("NT: urn:schemas-upnp-org:service:RenderingControl:1").append("\r\n");content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:service:RenderingControl:1").append("\r\n");}else if(i==4){content.append("NT: urn:schemas-upnp-org:service:ConnectionManager:1").append("\r\n");content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:service:ConnectionManager:1").append("\r\n");}else if(i==5){content.append("NT: urn:schemas-upnp-org:service:AVTransport:1").append("\r\n");content.append("USN: uuid:"+MainActivity._UUID+"::urn:schemas-upnp-org:service:AVTransport:1").append("\r\n");}content.append("HOST: 239.255.255.250:1900").append("\r\n");content.append("\r\n");//Log.e("SSDPSendMsg",MainActivity._UUID+"\r\n\r\n");return content.toString();//https://blog.csdn.net/thebestleo/article/details/50800781}

安卓端,发送成功后,其实并不能在各大平台的设备上正常显示

这时候就需要在安卓端搭建一个web服务,web服务里面指向一个描述的xml,有关这个投屏设备的一些可以使用的信息,比如酒店某个“房间号”,“品牌+房间号”都可以动态设置,比如我就设置的MyDLNA

2.搭建一个web服务

//noinspection GradleDependency
api 'org.eclipse.jetty:jetty-server:8.1.21.v20160908'
//noinspection GradleDependency
api 'org.eclipse.jetty:jetty-servlet:8.1.21.v20160908'
//noinspection GradleDependency
api 'org.eclipse.jetty:jetty-client:8.1.21.v20160908'
api 'org.nanohttpd:nanohttpd:2.3.1'

我这边就不粘贴很多

粘贴一下认证部分的内容,认证说起来也简单,就是电视端给一个链接让手机端可以访问,电视端把这个链接转换成二维码;

手机要先连接和电视机端同样的网络下面,手机扫码后直接就请求电视机了

扫码后请求电视机会拿到请求的头部,也就请求来的手机ip地址,把这个ip缓存一下,做一下判断,只给这个手机ip发送广播,这样就可以实现扫码后单一的投屏,就不会在同一个酒店下面显示所有房间的电视机了

public class ControlHandler extends org.eclipse.jetty.server.handler.DefaultHandler {private Activity myactivity;private String PlayerURL = "";private String DesiredVolume = "";private String PlayTarget = "";private String PlayState = "NO_MEDIA_PRESENT";private String PlayerURLTemp = "xxx";public ControlHandler(Activity activity) {super();this.myactivity = activity;}@Overridepublic void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {if (response.isCommitted() || baseRequest.isHandled()) {return;}Log.e("RequestShare", request.getRequestURI());//认证过就返回if (request.getRequestURI().contains("/certification")) {Constants.IPDEVICE = request.getRemoteAddr();Log.e("RequestShare", "正在认证设备");baseRequest.setHandled(true);ServletMsg.certificationMsg(response);}//这个是读取认证的设备的(可以使得投屏设备具有唯一性)if (request.getRequestURI().contains("/upnp/dev/" + MainActivity._UUID + "/desc")) {//验证可以通过对比IP实现指定设备投屏if (Constants.ALLDEVICES) {Log.e("RequestShare", "所有设备都可以投屏");baseRequest.setHandled(true);ServletMsg.defaultMsg(response);} else {if (request.getRemoteAddr().contains(Constants.IPDEVICE)) {//验证可以通过对比IP实现指定设备投屏Log.e("RequestShare", "指定:" + request.getRemoteAddr() + "设备符合投屏条件");baseRequest.setHandled(true);response.addHeader("Content-type: ", "text/xml");response.addHeader("SERVER: ", "Linux/3.14.29 UPnP/1.0 Cling/2.0");response.addHeader("Cache-control: ", "max-age=1800");
//                    response.addHeader("X-User-Agent: ","redsonic");ServletMsg.defaultMsg(response);}}}//请求分析if (Constants.ALLDEVICES) {getHeader(request, baseRequest, response); //头部执行播放暂停之类的简单操作getBody(request, baseRequest, response);//这个主要是主要处理有携带参数的数据,response还是加了一下,避免以后自己做控制端//爱奇艺和腾讯会请求里面链接,单纯回复可能不管用,加的一项ServletRequest(request, baseRequest, response);} else {if (request.getRemoteAddr().contains(Constants.IPDEVICE)) {//验证可以通过对比IP实现指定设备投屏Log.e("RequestShare1", request.getRemoteAddr());//IP地址Log.e("RequestShare2", request.getRequestURI());//请求的路径getHeader(request, baseRequest, response); //头部执行播放暂停之类的简单操作getBody(request, baseRequest, response);//这个主要是主要处理有携带参数的数据,response还是加了一下,避免以后自己做控制端//爱奇艺和腾讯会请求里面链接,单纯回复可能不管用,加的一项ServletRequest(request, baseRequest, response);}}}

3.投射视频

讲到投射视频,就很简单了,可以理解为手机向电视机发送了一个请求,只不过这个请求头请求体都需要根据特定格式来

例如:

<?xml version="1.0" encoding="UTF-8"?><s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><u:SetAVTransportURI xmlns:u="urn:schemas-upnp-org:service:AVTransport:1"><InstanceID>0</InstanceID><CurrentURI>http://183.131.147.16/upgcxcode/09/78/1255627809/1255627809-1-192.mp4?e=ig8euxZM2rNcNbNBhWdVhwdlhbU1hwdVhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J_aug859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IMvNC8xNbLEkF6MuwLStj8fqJ0EkX1ftx7Sqr_aio8_&amp;ua=tvproj&amp;uipk=5&amp;nbs=1&amp;deadline=1693797974&amp;gen=playurlv2&amp;os=bcache&amp;oi=3728755326&amp;trid=00002c92df64c42a43fa9d53a3d4ca9263eaT&amp;mid=434537794&amp;upsig=a446d61c8f79924e141b2973d94a0715&amp;uparams=e%2Cua%2Cuipk%2Cnbs%2Cdeadline%2Cgen%2Cos%2Coi%2Ctrid%2Cmid&amp;cdnid=22222&amp;bvc=vod&amp;nettype=0&amp;bw=251536&amp;orderid=0%2C3&amp;buvid=Y947C4998063ABA44C5595C15C70401D62DF&amp;build=70100100&amp;mobi_app=iphone&amp;f=T_0_0&amp;logo=80000000&amp;_nva_ext_=</CurrentURI><CurrentURIMetaData>&lt;DIDL-Lite xmlns="urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:upnp="urn:schemas-upnp-org:metadata-1-0/upnp/"&gt;&lt;item id="0" parentID="-1" restricted="1"&gt;&lt;dc:title&gt;我花1万块重庆打车到上海,却被全国网友说成老赖?&lt;/dc:title&gt;&lt;upnp:storageMedium&gt;UNKNOWN&lt;/upnp:storageMedium&gt;&lt;upnp:writeStatus&gt;UNKNOWN&lt;/upnp:writeStatus&gt;&lt;upnp:longDescription&gt;8ZPKtX3aKq2ata7O0mJBw8X-jfpm6X7rvBzgi6zEJLdfXlUYjO_zomV6xIkLrNS8Y97j6EBY4i-BmrgA2WWIqDn9Gd6p83nZCVDTdbhDOos4x_QGAByL3FXnYPdYvFYaHluTvVQ5MKCckyKCQIKKxdYatoPoEqsgeVHUPN4WtaAChNi5UaHQfwtwRNaZO_SSa9jnMEYFDhN4kpzC4A0TmmqKUbUQvdIR9fh7acHLpKz3Rt7XmnEu-o9jfV7km3nwBpPT8il6Ycxp5Gw0k-kEa192ccOzzA0V11Bg7EntZzm7aJKn2QVwqg5t8hFPxpdS32W6yzM1W-7usjfm2SOESC4guqmfBlaOxdVL8hnG3mdl_BGU3kmcVhKz8u0u79UsDTWEElD1088XFetEb--WevzgLqcQF53VlmwPzCF1hoIXID5L4UaTmLu6cSzi3dKW&lt;/upnp:longDescription&gt;&lt;res protocolInfo="http-get:*:video/mp4:DLNA.ORG_PN=MPEG4_P2_SP_AAC;DLNA.ORG_OP=01;DLNA.ORG_CI=0;DLNA.ORG_FLAGS=01500000000000000000000000000000"&gt;http://183.131.147.16/upgcxcode/09/78/1255627809/1255627809-1-192.mp4?e=ig8euxZM2rNcNbNBhWdVhwdlhbU1hwdVhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J_aug859r1qXg8gNEVE5XREto8z5JZC2X2gkX5L5F1eTX1jkXlsTXHeux_f2o859IMvNC8xNbLEkF6MuwLStj8fqJ0EkX1ftx7Sqr_aio8_&amp;amp;ua=tvproj&amp;amp;uipk=5&amp;amp;nbs=1&amp;amp;deadline=1693797974&amp;amp;gen=playurlv2&amp;amp;os=bcache&amp;amp;oi=3728755326&amp;amp;trid=00002c92df64c42a43fa9d53a3d4ca9263eaT&amp;amp;mid=434537794&amp;amp;upsig=a446d61c8f79924e141b2973d94a0715&amp;amp;uparams=e%2Cua%2Cuipk%2Cnbs%2Cdeadline%2Cgen%2Cos%2Coi%2Ctrid%2Cmid&amp;amp;cdnid=22222&amp;amp;bvc=vod&amp;amp;nettype=0&amp;amp;bw=251536&amp;amp;orderid=0%2C3&amp;amp;buvid=Y947C4998063ABA44C5595C15C70401D62DF&amp;amp;build=70100100&amp;amp;mobi_app=iphone&amp;amp;f=T_0_0&amp;amp;logo=80000000&amp;amp;_nva_ext_=&lt;/res&gt;&lt;upnp:class&gt;object.item.videoItem&lt;/upnp:class&gt;&lt;/item&gt;&lt;/DIDL-Lite&gt;</CurrentURIMetaData></u:SetAVTransportURI></s:Body></s:Envelope>

 这就是发过来的视频连接,适当解析后就可以正常播放了

4.监听播放与控制进度

手机端会不断给电视端发送实时数据,比如音量啊,进度啊,这些;手机监听到系统音量后,会给DLNA也发一条关于音量的数据,电视机拿到就处理一下即可

大部分视频厂家都是可以正常上报,优酷和芒果倒是加了些机制,需要电视机端给设备发消息,说我已经开始播放了,然后优酷才会继续播,不然播了两秒,优酷没有收到电视机的消息,会立刻把链接关掉云云..

 //获取body信息private void getBody(HttpServletRequest request, Request baseRequest, HttpServletResponse response) throws IOException {Log.e("getBody", "------getBody分割线start------");BufferedReader br = request.getReader();String str, wholeStr = "";while ((str = br.readLine()) != null) {wholeStr += str;}Log.e("getBody", wholeStr);//拿到播放的链接if (wholeStr.contains("u:SetAVTransportURI")) {Log.e("55m5m53", wholeStr);String temp = wholeStr.substring(0, wholeStr.indexOf("</CurrentURI>"));PlayerURL = temp.substring(temp.indexOf("<CurrentURI>")).replace("<CurrentURI>", "").replace("&amp;", "&").replace("&quot;", "\"").replace("&lt;", "<").replace("&gt;", "<").replace("&nbsp;", " ").replace("&apos;", "'");Log.e("55m5m54", PlayerURL);PlayState = "PLAYING";//Response响应一下baseRequest.setHandled(true);ServletMsg.SetAVTransportURIResponseMsg(response);//发送并打开页面给播放器
//            Intent intent = new Intent(myactivity, PlayerActivity.class);
//            intent.putExtra("PlayerURL", PlayerURL);
//            myactivity.startActivity(intent);//芒果TV一次性发了5个
//            if (!PlayerURL.equals(PlayerURLTemp)) {
//                PlayerURLTemp = PlayerURL;countDownTimer.start();
//                intent.putExtra("PlayerURL", PlayerURL);
//                myactivity.startActivity(intent);
//            }} else if (wholeStr.contains("u:SetVolume")) {//摘取出要我设置的音量String Volume = wholeStr.substring(0, wholeStr.indexOf("</DesiredVolume>"));DesiredVolume = Volume.substring(Volume.indexOf("<DesiredVolume>")).replace("<DesiredVolume>", "");Log.e("SetVolume", DesiredVolume);//Response响应一下baseRequest.setHandled(true);ServletMsg.SetVolumeResponseMsg(response);//到这边的意思就是和播放器说要控制一下设备的音频SendBroadcast("Volume#" + DesiredVolume);} else if (wholeStr.contains("u:Seek")) {//摘取出要我设置的进度String Target = wholeStr.substring(0, wholeStr.indexOf("</Target>"));PlayTarget = Target.substring(Target.indexOf("<Target>")).replace("<Target>", "");Log.e("PlayTarget", PlayTarget);SendBroadcast("Seek#" + PlayTarget);//告诉播放器,我要设置进度了} else if (wholeStr.contains("u:GetTransportInfo")) {//告诉控制端,我还活着Log.e("u:GetTransportInfo", "电视系统的State:" + PlayState);baseRequest.setHandled(true);ServletMsg.GetTransportInfoMsg(response, PlayState, "OK");} else if (wholeStr.contains("u:GetVolume")) {//腾讯bilibili会要求获取一下音量,就给呗//bilibili给了后,会一直索要~好奇怪Log.e("u:GetVolume", "电视系统的Volume:" + PlayerActivity.playerData.get_volume());baseRequest.setHandled(true);ServletMsg.GetVolumeMsg(response, PlayerActivity.playerData.get_volume());} else if (wholeStr.contains("u:GetPositionInfo")) {//告诉控制端我当前的播放状态GetPositionInfo(response, baseRequest);} else if (wholeStr.contains("u:GetMediaInfo")) {//爱奇艺单独列了这个~,尝试发了,但没反应,找了几个数据,应该是正确的Log.e("getBody", "我拿到GetMediaInfo了");GetMediaInfo(response, baseRequest);} else if (wholeStr.contains("u:Stop")) {//有些东西需要响应的,就响应一下Log.e("getBody", "我拿到Stop了");baseRequest.setHandled(true);ServletMsg.StopResponseMsg(response);}Log.e("getBody", "------getBody分割线end------");}private CountDownTimer countDownTimer = new CountDownTimer(1000, 1000) {@Overridepublic void onTick(long millisUntilFinished) {}@Overridepublic void onFinish() {//PlayerURLTemp="Fowlet";if(isTopActivity(Constants.PLAYACTIVITY)){SendBroadcast("State#Stop");}Intent intent = new Intent(myactivity, PlayerActivity.class);//Intent intent = new Intent(myactivity, Player2Activity.class);intent.putExtra("PlayerURL", PlayerURL);myactivity.startActivity(intent);}};//获取的头部信息private void getHeader(HttpServletRequest request, Request baseRequest, HttpServletResponse response) {Log.e("getHeader", "------getHeader分割线start------");Enumeration<String> em = request.getHeaderNames();while (em.hasMoreElements()) {String name = (String) em.nextElement();String value = request.getHeader(name);//System.out.println(name + "=" + value);Log.e("getHeader", name + "=" + value);if (name.equals("SOAPAction") | name.equals("SOAPACTION")) {String[] split = value.split("#");String result = split[1];//获取头部的一些快捷操作,如果说有一些要参数要回调的话,还是要走bodyString Transport = result.substring(0, result.indexOf("\""));Log.e("yongge", Transport);if (Transport.equals("Play")) {//播放PlayState = "PLAYING";Log.e("Header__", "我拿到Play了:" + PlayState);//开始播放了就让播放器开始收集信息//SendBroadcast("GetInfo#"+Transport);//暂停播放也会用的到SendBroadcast("State#" + Transport);} else if (Transport.equals("Stop")) {//停止PlayState = "STOPPED";Log.e("Header", "我拿到Stop了:" + PlayState);SendBroadcast("State#" + Transport);} else if (Transport.equals("Pause")) {//暂停PlayState = "PAUSED_PLAYBACK";Log.e("Header__", "我拿到Pause了:" + PlayState);SendBroadcast("State#" + Transport);} else if (Transport.equals("SetVolume")) {//设置音量Log.e("Header", "我拿到SetVolume了" + DesiredVolume);} else if (Transport.equals("GetPositionInfo")) {SendBroadcast("GetInfo#" + Transport);//告诉播放器,你要开始收集播放器信息了//获取进度Log.e("Header", "我拿到GetPositionInfo了");} else if (Transport.equals("GetTransportInfo")) {//获取传输状态//安卓端的腾讯是拿的GetTransportInfoLog.e("Header", "我拿到GetTransportInfo了");} else if (Transport.equals("SetMute")) {//静音/取消Log.e("Header", "我拿到SetMute了");} else if (Transport.equals("Seek")) {//设置进度Log.e("Header", "我拿到Seek了" + PlayerActivity.playerData.get_seek());} else if (Transport.equals("GetMediaInfo")) {//爱奇艺单独列了这个~Log.e("Header", "我拿到GetMediaInfo了");} else if (Transport.equals("GetVolume")) {Log.e("Header", "我拿到GetVolume了");}}//优酷是要走订阅if (name.equals("CALLBACK")) {String callbackUrl = value.replace("<", "").replace(">", "");Log.e("callbackUrl", callbackUrl);String str[] = callbackUrl.replace("/","").split(":");try {Socket socket = new Socket(str[1], Integer.parseInt(str[2]));Log.e("callbackUrl2", str[1]+"---"+str[2]);BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));bw.write("HTTP/1.1 200 OK\r\n");bw.write("Server: Linux/3.14.29 UPnP/1.0 Cling/2.0\r\n");bw.write("SID: uuid:"+MainActivity._UUID+"\r\n");bw.write("Content-Type: text/html; charset=\"utf-8\""+"\r\n");bw.write("TIMEOUT: Second-3600"+"\r\n\r\n");bw.flush();BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));while(true) {String readLine = null;if ((readLine = br.readLine()) != null) {Log.e("socket", readLine);} else {break;}}} catch (IOException e) {e.printStackTrace();}
//                YoukuMsg youkuMsg = new YoukuMsg();
//                //要返回一下订阅成功
//                baseRequest.setHandled(true);
//                ServletMsg.VendorMsg(response, youkuMsg.toString());//                SubResponse subRespons = new SubResponse();
//                subRespons.getTest(callbackUrl,MainActivity._UUID);//                onUnicastSend("172.16.0.121", 49152);//源IP和源端口
//
//                response.setStatus(SC_OK);
//                response.addHeader("Server: ","Linux/3.14.29 UPnP/1.0 Cling/2.0");
//                response.addHeader("SID: ","uuid:"+MainActivity._UUID);
//                //response.addHeader("Content-Type: ","text/html; charset=\"utf-8\"");
//                response.addHeader("TIMEOUT: ","Second-3600");
//                response.setContentType("text/html; charset=\"utf-8\"");
//
//                baseRequest.setHandled(true);
//                ServletMsg.VendorMsg(response, TencentMsg.RenderingControl_desc);
//                baseRequest.setHandled(true);
//                ServletMsg.VendorMsg(response, "");//                response.setHeader();}}Log.e("getHeader", "------getHeader分割线end:" + PlayState + "------");}

处理返回的进度

相关文章:

AndroidTV端:酒店扫码认证投屏DLNA

被老板叼了几次了&#xff0c;最近实在忍不了&#xff0c;准备离职&#xff1b; 但是担心离职后长时间没有办法找到工作 就想贡献一套平时琢磨出来的程序&#xff0c;请各位有能力的话带我熬过这凛冽的寒冬。 目前写出来的&#xff0c;有三个端&#xff1a;安卓TV端&#xf…...

基于PyTorch的交通标志目标检测系统

一、开发环境 Windows 10PyCharm 2021.3.2Python 3.7PyTorch 1.7.0 二、制作交通标志数据集&#xff0c;如下图 三、配置好数据集的地址&#xff0c;然后开始训练 python train.py --data traffic_data.yaml --cfg traffic_yolov5s.yaml --weights pretrained/yolov5s.pt --e…...

feign调用失败 feign.RetryableException: xxx-service executing GET http://xxx/test

一。 问题引入 升级springcloud的版本后 突然发现 以前正常的feign调用也报错了 升级后的各组件版本如下 spring cloud 2021.0.5 spring cloud alibaba 2021.0.5.0 spring boot 2.6.13 错误日志如下 feign.RetryableException: xxx-service executing GET http://xxx-servic…...

mysql 用户管理

目录 用户 创建用户 删除用户 修改密码 权限管理 赋权 查看权限 插销权限 总结 用户 mysql 的用户都存在于系统数据库 mysql 的user 表中 mysql> show tables; --------------------------- | Tables_in_mysql | --------------------------- | column…...

pyinstaller打包exe运行闪退

这里写自定义目录标题 前言问题描述解决过程 前言 闪退原因可能有很多&#xff0c;这里记录下我遇到的问题&#xff0c;简单来说是dll调用错误导致的闪退&#xff0c;因为我的python用的是32位的&#xff0c;但是pyinstaller却是64位的&#xff0c;属于用conda的时候没注意。 …...

ARM 汇编基础知识

1.为什么学习汇编&#xff1f; 我们在进行嵌入式 Linux 开发的时候是绝对要掌握基本的 ARM 汇编&#xff0c;因为 Cortex-A 芯片一 上电 SP 指针还没初始化&#xff0c; C 环境还没准备好&#xff0c;所以肯定不能运行 C 代码&#xff0c;必须先用汇编语言设置好 C 环境…...

CRM 自动化如何改善销售和客户服务?

许多 B2B 和 B2C 公司都使用 CRM 系统来组织业务流程&#xff0c;使复杂的任务更容易完成。企业可以使用 CRM 自动化来自动化工作流程&#xff0c;让团队有更多的时间来执行高价值的任务&#xff0c;而不是陷于一堆琐碎事情中。 什么是CRM自动化&#xff1f; CRM 自动化是指 C…...

Bean 的六种作用域

目录 一、作用域是什么&#xff1f; 1、singleton&#xff08;单例作用域&#xff09; 2、prototype&#xff08;原型作用域&#xff09; 3、request&#xff08;请求作用域&#xff09; 4、session&#xff08;回话作用域&#xff09; 5、application&#xff08;全局作用域&a…...

go语言--锁

锁的基础&#xff0c;go的锁是构建在原子操作和信号锁之上的 原子锁 原子包实现协程的对同一个数据的操作&#xff0c;可以实现原子操作&#xff0c;只能用于简单变量的简单操作&#xff0c;可以把多个操作变成一个操作 sema锁 也叫信号量锁/信号锁 核心是一个uint32值&#…...

再见,CSDN

从我2018年1月31日加入CSDN&#xff0c;到现在已经5年多的时间了。在这5年里&#xff0c;陆陆续续在CSDN上发布了很多论文阅读笔记、教程、技术文章等等&#xff0c;记录了我从大四到研究生再到工作这段时间的学习和成长轨迹。 我一直有备份个人资料的习惯&#xff0c;尤其是耗…...

MySQL总复习

目录 登录 显示数据库 创建数据库 删除数据库 使用数据库 创建表 添加数据表数据 查询表 添加数据表多条数据 查询表中某数据 增insert 删delete 改update 查select ​ where like ​编辑 范围查找 order by 聚合函数 count max min sum avg g…...

桌面平台层安全随手记录

声明 本文是学习桌面云安全技术要求. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 桌面平台层安全 桌面接入安全 用户标识 一般要求 本项要求包括&#xff1a; a) 系统应为用户提供唯一的身份标识&#xff0c;同时将用户的身份标识与该用户的所…...

【Docker】 08-Dockerfile

什么是Dockerfile Dockerfile可以认为是Docker镜像的描述文件&#xff0c;是由一系列命令和参数构成的教程&#xff0c;主要作用是用来构建docker镜像的构建文件。 Dockerfile解析过程 Dockerfile的保留命令 保留字作用FROM当前镜像是基于哪个镜像的 第一个指令必须是FROMMA…...

【二等奖方案】大规模金融图数据中异常风险行为模式挖掘赛题「Aries」解题思路

第十届CCF大数据与计算智能大赛&#xff08;2022 CCF BDCI&#xff09;已圆满结束&#xff0c;大赛官方竞赛平台DataFountain&#xff08;简称DF平台&#xff09;正在陆续释出各赛题获奖队伍的方案思路&#xff0c;欢迎广大数据科学家交流讨论。 本方案为【大规模金融图数据中…...

Github 下载指定文件夹(git sparse-checkout)

比如要下载这里的 data_utils 步骤 1、新建空文件夹&#xff0c;并进入新建的空文件夹。 2、git init 初始化 3、git remote add origin 添加远程仓库 4、git config core.sparsecheckout true 允许稀疏检出 5、git sparse-checkout set 设置需要拉取的文件夹&#xff08;可…...

蚂蚁集团SQLess 开源,与内部版有何区别?

当我们使用关系型数据库时&#xff0c;SQL 是联系起用户和数据库的一座桥梁。 SQL 是一种高度非过程化的语言&#xff0c;当我们在编写SQL 时&#xff0c;表达的是想要什么数据&#xff0c;而不是怎么获取数据。因此&#xff0c;我们往往更关心SQL 有没有满足业务逻辑&#xff…...

An Efficient Memory-Augmented Transformer for Knowledge-Intensive NLP Tasks

本文是LLM系列文章&#xff0c;针对《An Efficient Memory-Augmented Transformer for Knowledge 一种用于知识密集型NLP任务的高效内存增强转换器 摘要1 引言2 相关工作3 高效内存增强Transformer4 EMAT的训练流程5 实验6 分析7 结论局限性 摘要 获取外部知识对于许多自然语言…...

Java项目中jar war pom包的区别

1、pom&#xff1a;用在父级工程或聚合工程中&#xff0c;用来做jar包的版本控制&#xff0c;必须指明这个聚合工程的打包方式为pom。 <project ...> <modelVersion>4.0.0</modelVersion> <groupId>com.wong.tech</groupId> <artifactI…...

整理mongodb文档:分页

个人博客 整理mongodb文档:分页 个人博客&#xff0c;求关注&#xff0c;如果文章不够清晰&#xff0c;麻烦指出。 文章概叙 本文主要讲下在聚合以及crud的find方法中如何使用limit还有skip进行排序。 分页的情况很经常出现&#xff0c;这也是这篇博客诞生的理由。 数据准备…...

社区团购新玩法,生鲜蔬菜配货发货小程序商城

在当前的电商市场中&#xff0c;生鲜市场具有巨大的潜力和发展空间。为了满足消费者的需求&#xff0c;许多生鲜店正在寻找创新的方法来提高销售和客户满意度。其中&#xff0c;制作一个个性且功能强大的生鲜小程序商城是一个非常有效的策略。以下是在乔拓云平台上制作生鲜小程…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)

更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...

反射获取方法和属性

Java反射获取方法 在Java中&#xff0c;反射&#xff08;Reflection&#xff09;是一种强大的机制&#xff0c;允许程序在运行时访问和操作类的内部属性和方法。通过反射&#xff0c;可以动态地创建对象、调用方法、改变属性值&#xff0c;这在很多Java框架中如Spring和Hiberna…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

JAVA后端开发——多租户

数据隔离是多租户系统中的核心概念&#xff0c;确保一个租户&#xff08;在这个系统中可能是一个公司或一个独立的客户&#xff09;的数据对其他租户是不可见的。在 RuoYi 框架&#xff08;您当前项目所使用的基础框架&#xff09;中&#xff0c;这通常是通过在数据表中增加一个…...

CSS | transition 和 transform的用处和区别

省流总结&#xff1a; transform用于变换/变形&#xff0c;transition是动画控制器 transform 用来对元素进行变形&#xff0c;常见的操作如下&#xff0c;它是立即生效的样式变形属性。 旋转 rotate(角度deg)、平移 translateX(像素px)、缩放 scale(倍数)、倾斜 skewX(角度…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)

题目 做法 启动靶机&#xff0c;点进去 点进去 查看URL&#xff0c;有 ?fileflag.php说明存在文件包含&#xff0c;原理是php://filter 协议 当它与包含函数结合时&#xff0c;php://filter流会被当作php文件执行。 用php://filter加编码&#xff0c;能让PHP把文件内容…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...