vue+net使用stripe支付开发流程

news2024/12/19 17:26:06

文章目录

    • 前言
    • 用到的语言和技术
    • 整体流程
    • stripe平台
    • vue前端
    • Net后端
    • 遇到的问题思考
    • 总结

前言

公司最近做到了国外支付功能,最后选型使用stripe进行支付,实现目标:使用stripe支付可以让国外用户自己选择支付方式并订阅支付。

用到的语言和技术

先说下用到的开发技术:
前端:Vue
后端:net6.0
后端需要引入的nuget包:Stripe.net
stripe平台有多种支付方式,目前选择的是在stripe平台生成价格表,然后把价格表的js脚本放在vue前端。

整体流程

要在自己的项目里面实现stripe价格表支付的话
1.首选在stripe平台切换到【测试模式】,在开发环境下,使用测试模式,不要使用正式模式,然后在stripe支付平台添加价格,产品,然后在价格表添加上产品以及产品下面的价格,保存后价格表会生成一个js脚本,把这个脚本放到自己项目前端,用于直接生成固定的价格(注:价格表只能设置几个固定价格让用户去购买,如果自己的项目需求是不同的价格,比如商城里面商品很多,商品型号也很多,这样的需求就不太适合使用价格表来做支付了,最好是在自己的项目后台通过stripe的api生成不同的价格来实现支付,目前这篇文章主要讲解的是价格表支付以及对接stripe的一个思路)
2.拿到stripe平台生成的价格表js脚本,放到vscode前端页面里面,运行项目就会显示一个配置好的价格表,价格表的每个价格都会有一个 支付/订阅 按钮 ,点击按钮会自动进入支付页面,支付页面包含 让用户选择邮箱,支付类型,比如银行卡支付,银行卡号等,stripe平台提供了测试模式下可以支付成功的银行卡号:4242424242424242 在支付填写完支付信息点击提交按钮后,会进入支付回调,回调需要在stripe平台配置回调地址(注:正式的回调地址必须是https开头的协议地址+域名+项目后台回调路径 才会生效),测试模式下在本地开发stripe支付不需要配置https的回调地址,可以在本地电脑安装stripe cli 这个可以自己搜索下如何在本地安装,比较简单。安装完成后使用cmd命令行输入 stripe login 然后校验一下权限,再输入需要监听的本地路径,例如:stripe listen --load-from-webhooks-api --forward-to localhost:9081/api/webhook 输入后会返回一个webhook的测试秘钥,把这个秘钥配置在后台项目里。在回调时候需要用到这个秘钥,只有秘钥验证成功才能继续执行后面的回调事件。
3.配置好stripe前期工作后,在前端vue代码里面放入stripe价格表js脚本,注意:这个价格表里面的每个价格下面的支付/订阅按钮是不能在vue前端触发到点击事件的,因为这是stripe生成的页面。我们能做的是在前端页面的created里面调用后端接口 生成stripe客户会话,把后端接口返回客户会话字段customer-session-client-secret放在前端js脚本里面,会有时效限制,好像是半小时时效。这个customer-session-client-secret字段是后端接口里面通过调用stripe平台api生成的值,customer-session-client-secret客户会话字段的作用:身份验证,会话管理,安全性
4.在后端,我使用的是net开发,首先需要在appsettings.json里面配置stripe平台的一些秘钥信息:

"Stripe": {
  "StripeSecretKey": "sk_test_xxxx这是你的stripe开发秘钥",
  "EndpointSecret": "whsec_8fxxx这是stripe上面Webhook回调的安全密钥,开发期间使用stripe cli监听后可以得到测试秘钥"
}

然后写一个 生成客户支付会话 的接口,在讲解接口前,需要先了解一些前置信息。

首先stripe平台里面是有一个 客户信息页面的,这个页面包含 名称,邮箱 客户ID(StripeCustomId后面会用到),创建时间,购买的产品信息等,所以我们后续是可以在stripe平台看到哪些客户购买了哪些产品以及购买的时间等信息的,当然也可以把这些客户信息以及购买信息在支付回调时候获取到存储到数据库中。
然后在自己的数据库里面创建一些跟stripe支付有关联的表,主要讲:stripe客户信息表,这个表里面主要是把自己项目的userid和stripe的客户id(StripeCustomId)进行关联,在后面的 生成客户支付会话信息接口 会往这个客户信息表存储数据。
下面讲解 生成客户支付会话 接口里面的大概逻辑:

  1. 获取客户信息(查询数据库 stripe客户信息表 ,根据用户ID查询当前用户是否已经关联stripe上面生成的客户信息)
  2. 如果当前用户没有stripe客户信息,说明是第一次进行支付,那么就调用stripe的api生成一个客户ID保存到stripe客户信息表里面,在这一步可以在生成客户信息的时候添加一些元数据到客户信息里,元数据不能是银行卡号等这些敏感数据,可以传输比如userid,userName,roleid,roleName这些业务逻辑数据,元数据主要是用于支付回调时候进行使用。
  3. 根据 查询/生成的客户ID(StripeCustomId)生成一个客户支付会话字段值,并且返回前端。

5.支付回调处理。在到第五步之前,已经是可以进行支付了,在支付完成后,在自己后台写的回调页面里面找到对应的触发事件,我用的是invoice.payment_succeeded支付完成事件来获取支付完成信息进行处理后续逻辑。

下面是具体操作步骤

stripe平台

第一步首先打开stripe平台,然后选择 产品目录-价格表(不是中文的可以在页面右上角设置里面转换一下语言,转成简体中文)
在这里插入图片描述
添加完成价格表后,把价格表生成的脚本代码复制到vue前端

在这里插入图片描述
到这里,stripe平台的工作基本就算是完成了(一些其他配置也很多,但是每个人用到的不一样,比如开发秘钥在哪里等等,用到哪些具体再去找就好了),后面主要是前端和后端接口的使用了。stripe平台的东西很多,但是并不复杂,详细分析的话一篇文章分析完不太现实,主要是提供一种思路。

vue前端

在前端,虽然说是引入js脚本,但是从stripe价格表复制过来后还是做了一些更改:
首选pricing-table-id(价格表ID)做成了动态赋值,这样做的目的是可以根据自己的业务需求来显示不同的价格表,比如检查到用户网站是中文模式就显示中文的价格表,否则就显示英文的价格表(同一个价格表是不能自动转换语言的,所以如果需要多个语言的价格就需要配置多个价格表)publishable-key(前端通信公钥)不需要改动,多个价格表使用的公钥相同。
在这里插入图片描述
然后在mounted里面动态加载价格表:在这里插入图片描述
附上代码:

	mounted() {
		// 动态加载 Stripe 定价表脚本
		const script = document.createElement('script');
		script.src = 'https://js.stripe.com/v3/pricing-table.js';
		script.async = true;
		script.onload = () => {
			this.isScriptLoaded = true; // 脚本加载完成后设置标志
		};
		document.body.appendChild(script);
	},

然后在created里面调用接口:

created() {
		/*前面是其他业务逻辑处理...*/
		//生成客戶会话
		this.createCustomerSession().then((session) => {
			this.customerSessionClientSecret = session.clientSecret;
		})
}
methods: {
		createCustomerSession() {
			return this.http.post('/api/Sys_Customers/createCustomerSession', {
			}).then((res) => {
				if (res) {
					return res; // 返回成功的数据
				}
			}).catch((err) => {
				// 处理错误
				console.error("创建客户会话失败:", err);
				throw err; // 抛出错误以供外部捕获
			});
		},
}

这个时候页面就直接显示出来价格表了,类似这样子:
在这里插入图片描述
·点击价格下面的支付/订阅按钮会跳转到支付页面(不需要自己写代码,stripe已经生成,反过来讲这个页面的样式,输入框等也不能自己使用代码控制,只能在stripe平台去更改配置,并且这个页面不能直接反填一些你自己的配置信息,比如说你想在这个支付页面显示一个新增加的输入框,进入页面的时候输入框里面就显示值:AA,这个是不能实现的,可以在进入页面的时候就显示的值只有email输入框的值,而且显示后是只读的,不能更改)
在这里插入图片描述

下面就是困扰我时间比较长的问题了,如何去调用后台接口?在我选择支付的时候我肯定需要把userid等用户信息一起带到支付信息里面,不然支付了也不清楚是哪个用户去支付的那么在什么时机去传输用户信息到支付信息里面呢?当时想做的是点击 支付/订阅 按钮的时候去调用接口传输用户信息的,但是价格表是直接生成的页面,所以我们没办法在vue前端去抓取到支付按钮的点击事件。
最后找到的实现方法是:在前端created里面直接添加一个接口,这个接口的实现逻辑为:生成stripe客户会话(上面有讲到这块流程),在生成客户会话的时候创建stripe元数据(键值对格式),把用户信息保存到元数据里面,这样子我们回调时候就能通过获取客户元数据里面的信息来获取到用户信息了。

Net后端

在开发接口前,需要先引入NuGet包:Stripe.net(我使用的版本是47.0.0)

        /// <summary>
        /// 生成客户支付会话
        /// </summary>
        /// <param name="Customer"></param>
        /// <returns></returns>
        [HttpPost, Route("createCustomerSession")]
        public async Task<IActionResult> CreateCustomer([FromBody] CustomerRequest Customer)
        {
            var user = UserContext.Current.UserInfo;
            //获取客户信息
            var CustomerInfo = await _Repository.FindAsyncFirst(x => x.User_Id == user.User_Id);
            string customerId;
            if (CustomerInfo == null)
            {
                //创建客户信息并保存到数据库
                var options = new CustomerCreateOptions
                {
                    Name = user.UserName,
                    //Email = Customer.email,
                    //Metadata里面为元数据
                    Metadata = new Dictionary<string, string>
                    {
                      //{ "email", Customer.email },
                      { "name", user.UserName },
                      { "userId", user.User_Id.ToString() },
                      { "farmId", user.CurrentFarmId.ToString() },
                      { "PayRoleName", user.RoleName },
                      { "PayRoleId", user.CurrentRoleId.ToString() },
                    }
                };
                //通过stripe的api生成客户信息
                var service = new CustomerService();
                var customer = await service.CreateAsync(options);
                customerId = customer.Id;//客户ID
                //保存客户信息到数据库
                Sys_Customer Customer = new Sys_Customer
                {
                    User_Id = user.User_Id,
                    StripeCustomId = customer.Id,
                    StripeName = user.UserName,
                    Creator = user.UserTrueName,
                    CreateDate = DateTime.Now,
                    Modifier = user.UserTrueName,
                    ModifyDate = DateTime.Now
                };
                _Repository.Add(sys_Customer);
                _Repository.SaveChanges();
            }
            else
            {
                customerId = CustomerInfo.StripeCustomId;
            }
            //调用stripe的api生成stripe客户支付会话
            var sessionOptions = new CustomerSessionCreateOptions
            {
                Customer = customerId,
                Components = new CustomerSessionComponentsOptions
                {
                    PricingTable = new CustomerSessionComponentsPricingTableOptions
                    {
                        Enabled = true
                    }
                }

            };
            var sessionService = new CustomerSessionService();
            var session = await sessionService.CreateAsync(sessionOptions);
            return Json(new { status = true, clientSecret = session.ClientSecret });
        }

下面webhook回调页面的代码:主要看 if (stripeEvent.Type == EventTypes.InvoicePaymentSucceeded)里面的代码

 [Route("/api/webhook")]
 public class WebhookController : Controller
 {
  //前面根据自己的框架进行配置获取数据上下文,获取appsettings.json里面的秘钥值等等
    [HttpPost]
  public async Task<IActionResult> Index()
  {
      StripeConfiguration.ApiKey = AppSetting.Stripe.StripeSecretKey;
      string endpointSecret = AppSetting.Stripe.EndpointSecret;
      var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
      try
      {
          var stripeEvent = EventUtility.ConstructEvent(json,
              Request.Headers["Stripe-Signature"], endpointSecret);
          //var stripeEvent = EventUtility.ParseEvent(json);

          // 创建订阅时触发
          if (stripeEvent.Type == EventTypes.CustomerSubscriptionCreated)
          {
              Logger.Info($"webhook事件类型:{EventTypes.CustomerSubscriptionCreated},打印stripeEvent:{stripeEvent}");
          }
          // 支付成功,不包含价格编号,发票等信息
          if (stripeEvent.Type == EventTypes.PaymentIntentSucceeded)
          {
              Logger.Info($"webhook事件类型:{EventTypes.PaymentIntentSucceeded},打印stripeEvent:{stripeEvent}");
          }
          //支付成功,包含价格,发票等信息,后续主要用这个事件进行回调处理。
          if (stripeEvent.Type == EventTypes.InvoicePaymentSucceeded)
          {
              Logger.Info($"webhook事件类型:{EventTypes.InvoicePaymentSucceeded},打印stripeEvent:{stripeEvent}");
              var invoice = stripeEvent.Data.Object as Invoice;
              var customerId = invoice.CustomerId; // 获取客户 ID
              var lineItem = invoice.Lines.Data[0]; // 获取第一个行项目(这块是因为我的项目需求每次只能购买一个价格,所以只取第一行,如果多个价格一起支付的话就需要循环获取了)
              var priceId = lineItem.Price.Id; // 获取价格 ID
            
              // 使用 CustomerService 获取客户信息
              var customerService = new CustomerService();
              var customer = await customerService.GetAsync(customerId); // 获取客户
              // 访问客户的元数据
              var metadata = customer.Metadata;
              var userId = metadata.ContainsKey("userId") ? metadata["userId"] : null;
              var userName = metadata.ContainsKey("userName") ? metadata["userName"] : null;
              var farmId = metadata.ContainsKey("farmId") ? metadata["farmId"] : null;
              var PayRoleName = metadata.ContainsKey("PayRoleName") ? metadata["PayRoleName"] : null;
              var PayRoleId = metadata.ContainsKey("PayRoleId") ? metadata["PayRoleId"] : null;
            

              //获取支付意图ID
              var paymentIntentId = invoice.PaymentIntentId.ToString();

              //根据支付意图ID获取支付方式ID
              var paymentIntentService = new PaymentIntentService();
              var paymentIntent = await paymentIntentService.GetAsync(paymentIntentId);
              // 获取支付方式 ID
              var paymentMethodId = paymentIntent.PaymentMethodId;
              // 获取支付方式类型
              var paymentMethodService = new PaymentMethodService();
              var paymentMethod = await paymentMethodService.GetAsync(paymentMethodId);
              var paymentMethodType = paymentMethod.Type;

              // 计算总税费
              decimal totalTax = 0;
              foreach (var taxAmount in invoice.TotalTaxAmounts)
              {
                  totalTax += taxAmount.Amount; // 累加税费
              }

         
           
              var stripeStartTime = lineItem.Period.Start; // 获取开始时间 (UTC)
              var stripeEndTime = lineItem.Period.End;   // 获取结束时间 (UTC)

              // 计算时间间隔
              TimeSpan interval = stripeEndTime - stripeStartTime;
              TimeSpan serverOffset = TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow); // 获取服务器当前时区偏移量

              //转换为服务器时间
              DateTimeOffset serverTimeStart = new DateTimeOffset(stripeStartTime, TimeSpan.Zero).ToOffset(serverOffset);
              DateTimeOffset serverTimeEnd = new DateTimeOffset(stripeEndTime, TimeSpan.Zero).ToOffset(serverOffset);
              var sys_PayLog = new Sys_PayLog();
              //根据自己业务需求添加字段...
              sys_PayLog .PayDate = serverTimeStart.DateTime;
              sys_PayLog .ExpirDate = serverTimeEnd.DateTime;
              sys_PayLog .PayNo = invoice.Id;
              sys_PayLog .User_Id = int.TryParse(userId, out var id) ? id : 0;
              sys_PayLog .RoleName = PayRoleName;
              sys_PayLog .RoleId = int.TryParse(PayRoleId, out var roleId) ? roleId : 0;
        
              // 保存到数据库    
              _Repository.Add(sys_PayLog);
              await _Repository.SaveChangesAsync();
          }
          // 结账完成
          if (stripeEvent.Type == EventTypes.CheckoutSessionCompleted)
          {
              Logger.Info($"webhook事件类型:{EventTypes.CheckoutSessionCompleted},打印stripeEvent:{stripeEvent}");
          }

          // ... handle other event types
          else
          {
              Logger.Info($"webhook事件类型:{stripeEvent.Type},事件不存在");
          }
          return Ok(new { received = true });
      }
      catch (StripeException e)
      {
          Logger.Info($"webhook报异常:{e}");
          return BadRequest();
      }

  }
 }

注意:这个里面当初我根据stripe官网得到的代码是 if (stripeEvent.Type == Event.InvoicePaymentSucceeded),但是会报错,检查了半天没发现问题,并且在国内国外都没搜到解决办法,最后还是自己反编译引入的Nuget包Stripe.net发现47.0.0版本的Event已经改成了EventTypes的方式了,但是官网demo还是使用的之前的Event.方式,真的有点无语了。不管怎么样,代码改成if (stripeEvent.Type == EventTypes.InvoicePaymentSucceeded)就没问题了,如果你也遇到类似的问题,也可以试着反编译你引用的包查看最正确的用法。

遇到的问题思考

1.stripe上面订阅和支付的区别?
答:订阅相当于购买产品后自动续费,如果订阅的是月付包,那么到了一个月这个时间就是自动扣款续费。支付是一次性的,不会自动续费。
2.一个客户在订阅了产品A以后,隔了一分钟这个客户是否还能订阅产品A?
答:在stripe平台设置里面可以设置-产品设置-付款 里面可以设置客户是否可以多个订阅。
3.stripe支付页面是否可以自动反填一些跟自己项目业务相关的值?
答:不可以,stripe支付页面除了email,其他的值只能让客户自己去填写。
4.email反填到支付页后,还能够让客户自己更改吗?
答:不可以,如果email设置为反填的话,在支付页面就显示为只读了。
5.一个用户如果多次购买产品,如何只生成一个stripe客户信息?
答:在后台接口把用户id和stripe客户id关联上,一个用户始终使用同一个stripe客户ID去生成客户支付会话。
6.在本地开发stipe支付的时候,我使用了stripe cli命令行进行模拟webhook回调地址,那么如何确认回调地址是否被成功监听到呢?
答:本地运行后台项目,并且在回调地址上打上断点,本地运行前端包含价格表的项目,打开cmd运行stripe cli监听后台webhook地址路径,前端通过价格表正常支付,这个时候后台就会进入回调断点。

总结

stripe的几种支付方式,实现逻辑其实差距不大,使用价格表主要是不需要调用stripe的api去生成价格了,可以直接通过嵌入价格表到前端的方式直接使用stripe支付,但是局限性也会有,如果是很多不同的价格,比如商城商品,那么就不适合使用价格表,看个人的业务需求去使用。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

什么?Flutter 可能会被 SwiftUI/ArkUI 化?全新的 Flutter Roadmap

在刚刚过去的 FlutterInProduction 活动里&#xff0c;Flutter 官方除了介绍「历史进程」和「用户案例」之外&#xff0c;也着重提及了未来相关的 roadmap &#xff0c;其中就有 3.27 里的 Swift Package Manager 、 Widget 实时预览 和 Dart 与 native 平台原生语言直接互操作…

随机森林算法原理

随机森林算法原理 算法流程随机森林的生成随机森林的预测 算法总结随机森林的优点随机森林的缺点 算法流程 随机森林的生成 输入训练数据 D&#xff0c;样本个数为 m &#xff0c;待学习的决策树数量为 T。 对于 t 1,2,…,T&#xff0c;从 D 中有放回地采样 m 次&#xff0c…

游戏AI实现-寻路算法(Dijkstra)

戴克斯特拉算法&#xff08;英语&#xff1a;Dijkstras algorithm&#xff09;&#xff0c;又称迪杰斯特拉算法、Dijkstra算法&#xff0c;是由荷兰计算机科学家艾兹赫尔戴克斯特拉在1956年发现的算法。 算法过程&#xff1a; 1.首先设置开始节点的成本值为0&#xff0c;并将…

基于MNE的EEGNet 神经网络的脑电信号分类实战(附完整源码)

利用MNE中的EEG数据&#xff0c;进行EEGNet神经网络的脑电信号分类实现&#xff1a; 代码&#xff1a; 代码主要包括一下几个步骤&#xff1a; 1&#xff09;从MNE中加载脑电信号&#xff0c;并进行相应的预处理操作&#xff0c;得到训练集、验证集以及测试集&#xff0c;每个…

Element@2.15.14-tree checkStrictly 状态实现父项联动子项,实现节点自定义编辑、新增、删除功能

背景&#xff1a;现在有一个新需求&#xff0c;需要借助树结构来实现词库的分类管理&#xff0c;树的节点是不同的分类&#xff0c;不同的分类可以有自己的词库&#xff0c;所以父子节点是互不影响的&#xff1b;同样为了选择的方便性&#xff0c;提出了新需求&#xff0c;选择…

SAP-ABAP开发学习-面向对象开发ooalv(2)

SAP-ABAP开发学习-面向对象OOALV&#xff08;1&#xff09;-CSDN博客 本文目录 一、类的继承 多态性类继承的实现 二、抽象类 三、最终类 四、接口 五、定义全局对象 一、类的继承 继承的本质是代码重用。当我们要构造一个新类时&#xff0c;无需从零开始&#xff0c;可…

典型案例 | 旧PC新蜕变!东北师范大学依托麒麟信安云“旧物焕新生”

东北师范大学始建于1946年&#xff0c;坐落于吉林省长春市&#xff0c;是中国共产党在东北地区创建的第一所综合性大学。作为国家“双一流”建设高校&#xff0c;学校高度重视教学改革和科技创新&#xff0c;校园信息化建设工作始终走在前列。基于麒麟信安云&#xff0c;东北师…

Linux脚本语言学习--上

1.shell概述 1.1 shell是什么&#xff1f; Shell是一个命令行解释器&#xff0c;他为用户提供了一个向Linux内核发送请求以便运行程序的界面系统级程序&#xff0c;用户可以使用Shell来启动&#xff0c;挂起&#xff0c;停止甚至是编写一些程序。 Shell还是一个功能相当强大…

2024年底-Sre面试问题总结-持续更新

这几个缩写 贴一下是因为真的会有人问:( SRE “Site Reliability Engineer” 站点可靠性工程师 SLA “Service Level Agreement” 服务可用性协议 CICD “Continuos Integration Continous Deployment” 持续集成 持续部署 3个高频问题 K8s生产环境中处理过哪些复杂 or 印象…

【硬件接口】I2C总线接口

本文章是笔者整理的备忘笔记。希望在帮助自己温习避免遗忘的同时&#xff0c;也能帮助其他需要参考的朋友。如有谬误&#xff0c;欢迎大家进行指正。 一、概述 I2C总线是一种非常常用的总线&#xff0c;其多用于一个主机&#xff08;或多个&#xff09;与单个或多个从设备通讯…

OkHttp源码分析:分发器任务调配,拦截器责任链设计,连接池socket复用

目录 一&#xff0c;分发器和拦截器 二&#xff0c;分发器处理异步请求 1.分发器处理入口 2.分发器工作流程 3.分发器中的线程池设计 三&#xff0c;分发器处理同步请求 四&#xff0c;拦截器处理请求 1.责任链设计模式 2.拦截器工作原理 3.OkHttp五大拦截器 一&#…

SAP:如何修改已释放的请求

SAP:如何修改已释放的请求 QQ出了一个新功能&#xff0c;把10年前的旧日志推给自己。这个10年前的日志&#xff0c;是用户反映在SE10中把请求释放后发现漏了内容&#xff0c;想修改已释放的请求。经调查写了一个小程序&#xff0c;实现用户的需求。 *&-------------------…

python怎么循环嵌套

嵌套循环&#xff1a; 概念&#xff1a;循环中再定义循环&#xff0c;称为嵌套循环&#xff1b; 【注意】嵌套循环可能有多层&#xff0c;但是一般我们实际开发最多两层就可以搞定了(99%的情况) 格式&#xff1a; 1、while中套while常用 2、while中套for in 3、for in中套…

前端优雅(装逼)写法(updating····)

1.>>右位移运算符取整数 它将一个数字的二进制位向右移动指定的位数&#xff0c;并在左侧填充符号位&#xff08;即负数用1填充&#xff0c;正数用0填充&#xff09;。 比如 2.99934 >> 0&#xff1a;取整结果是2&#xff0c;此处取整并非四舍五入 2.99934 会先…

MySQL -- 库的相关操作

目录 查看数据库 创建数据库 直接创建&#xff1a; 加约束条件 if not exists 字符集和校对规则 什么是字符集 什么是校对规则 校对规则的主要功能 校对规则的特性 查看指定的数据库使用的字符集和校对规则&#xff1a; 比较是否区分大小写字母差异 显示创建语句 …

Moretl开箱即用日志采集

永久免费: 至Gitee下载 使用教程: Moretl使用说明 使用咨询: 用途 定时全量或增量采集工控机,电脑文件或日志. 优势 开箱即用: 解压直接运行.不需额外下载.管理设备: 后台统一管理客户端.无人值守: 客户端自启动,自更新.稳定安全: 架构简单,兼容性好,通过授权控制访问. 架…

分享一次接口性能摸底测试过程

接口性能测试是用于验证应用程序中的接口是否可以满足系统的性能要求的一种测试方法。确定应用程序在各种负载条件下的性能指标&#xff0c;例如响应时间、吞吐量、并发性能等&#xff0c;以便提高系统的性能和可靠性。本文主要讲述接口性能测试从前期准备、方案设计到环境搭建…

【机器学习】机器学习的基本分类-无监督学习-t-SNE(t-分布随机邻域嵌入)

t-SNE&#xff08;t-分布随机邻域嵌入&#xff09; t-SNE&#xff08;t-distributed Stochastic Neighbor Embedding&#xff09;是一种用于降维的非线性技术&#xff0c;常用于高维数据的可视化。它特别适合展示高维数据在二维或三维空间中的分布结构&#xff0c;同时能够很好…

【教学类-83-03】20241218立体书盘旋蛇3.0——圆点蛇1(蚊香形)

背景需求&#xff1a; 制作儿童简易立体书贺卡 【教学类-83-01】20241215立体书三角嘴1.0——小鸡&#xff08;正菱形嘴&#xff09;-CSDN博客文章浏览阅读1k次&#xff0c;点赞24次&#xff0c;收藏18次。【教学类-83-01】20241215立体书三角嘴1.0——小鸡&#xff08;正菱形…

监控视频汇聚融合云平台一站式解决视频资源管理痛点

随着5G技术的广泛应用&#xff0c;各领域都在通信技术加持下通过海量终端设备收集了大量视频、图像等物联网数据&#xff0c;并通过人工智能、大数据、视频监控等技术方式来让我们的世界更安全、更高效。然而&#xff0c;随着数字化建设和生产经营管理活动的长期开展&#xff0…