6.API验证

# 加密复习
#1.简单的对称加密,token是固定的
客户端请求:
import requests
# 1.自定义token值
token = 'cxiong_token' # token认证都是在请求头里携带
res = requests.get('http://127.0.0.1:8000/getInfo',
headers={'token': token}
)
print(res.text) # 服务端修改:autoserver/views.py
# 修改getInfo函数
def getInfo(requests):
# 数据获取
if requests.method == 'POST':
server_info = json.loads(requests.body)
print(server_info)
# for k,v in server_info.items():
# print(k,v)
return HttpResponse('OK')
# 服务端如何获取请求头里面的数据
# requests.POST\requests.FILES\requests.GET
# HTTP_TOKEN
# print(requests.META)
token = requests.META.get('HTTP_TOKEN')
server_token ='cxiong_token'
if token != server_token:
return HttpResponse('token值错误')
# 链接后台数据库获取主机名列表并返回
return HttpResponse(['c1.com', 'c2.com']) # 2. 加盐处理:动态加盐
一般使用当前时间结合token加密再发送
# client请求
# 自定义token值
token = 'cxiong_token1'
import time client_time = time.time()
tmp = '%s|%s' % (token, client_time)
print(tmp)
# 加密
import hashlib md5 = hashlib.md5() # 使用md5加密
# 传入明文数据:数据必须是bytes类型
md5.update(tmp.encode('utf8'))
# 生成密文数据
res = md5.hexdigest()
print(res)
# 将加密的密文数据发送给服务端,也需要将加密之前的明文数据也发给服务端,让服务端通过相同的方法进行比对
client_md5_token = '%s|%s' % (res, client_time)
# token认证都是请求头里携带
data = requests.get('http://127.0.0.1:8000/getInfo',
headers={'token': client_md5_token}
)
print(data.text) # 服务端处理
token = requests.META.get('HTTP_TOKEN')
server_token = 'cxiong_token'
client_md5_token,client_time = token.split('|')
print(client_md5_token,client_time)
# 加密认证
tmp = '%s|%s'%(server_token,client_time)
#加密
import hashlib
md5 = hashlib.md5()
md5.update(tmp.encode('utf8'))
res = md5.hexdigest()
print(res)
if res != client_md5_token:
return HttpResponse('token值错误') # 3.设置token失效时间
client_md5_token,client_time = token.split('|')
import time
server_time = time.time()
if server_time - client_time > 10:
return HttpResponse('token超时了')
"""大部分API验证做到第三步就可以了"""
# 4.设置token使用次数
"""
黑客可能会在失效时间之前截取并发送请求
思路:
第一次来的时候先去redis中判断是否存在
如果存在则说明token已经使用过了
不存在则添加到redis中(并且设置保存的超时时间)
"""
# 5. 固定token由服务端生成发到客户端再组合的方式(看业务是否重要了)

7.后台目录规划

# django是一款专注于开发app的框架
"""
1.api:接收数据并处理入库(API验证)
2.backend : 后台管理
# 上述两个app都需要使用模型表,那么模型表写在哪个models文件
3.repository:单独存储模型表相关代码
"""

8.模型表设计

"""
#{'board': {'status': 10000, 'data': {'manufacturer': 'Parallels Software International Inc.', 'model': 'Parallels Virtual Platform', 'sn': 'Parallels-1A 1B CB 3B 64 66 4B 13 86 B0 86 FF 7E 2B 20 30'}} 1.设计模型表的时候字段名与收集过来的字典的key保持一致
models.User.objects.create(name='jason',age=18)
d = {'name':'cxiong','age':32}
models.User.objects.create(**d) # **d将数据打散为name=jason这种模式
为了录入数据的时候可以借助于**快速录入
2.模型表设计及表关系判断,根据情况自己关联
"""
如下图

9.模型表数据录入

1.11张表相关统计表
2.数据库迁移命令
makemigrations
migrate
3.django admin后台管理
1.创建超级用户:createsuperuser
2.注册模型表
repository/admin.py
4.手动录入数据
1.业务线表
2.服务器表
对应表:

# repository/models.py
from django.db import models class UserProfile(models.Model):
"""
用户信息
python2 字符串前面需要加u: u姓名
"""
name = models.CharField(u'姓名', max_length=32)
email = models.EmailField(u'邮箱')
phone = models.CharField(u'座机', max_length=32)
mobile = models.CharField(u'手机', max_length=32)
password = models.CharField(u'密码', max_length=64) # 规定django-admin后台管理显示的中文表
class Meta:
verbose_name_plural = "用户表" def __str__(self):
return self.name class UserGroup(models.Model):
"""
用户组
"""
name = models.CharField(max_length=32, unique=True)
users = models.ManyToManyField('UserProfile') class Meta:
verbose_name_plural = "用户组表" def __str__(self):
return self.name class BusinessUnit(models.Model):
"""
业务线
"""
name = models.CharField('业务线', max_length=64, unique=True)
contact = models.ForeignKey('UserGroup', verbose_name='业务联系人', related_name='c')
manager = models.ForeignKey('UserGroup', verbose_name='系统管理员', related_name='m') class Meta:
verbose_name_plural = "业务线表" def __str__(self):
return self.name class IDC(models.Model):
"""
机房信息
"""
name = models.CharField('机房', max_length=32)
floor = models.IntegerField('楼层', default=1) class Meta:
verbose_name_plural = "机房表" def __str__(self):
return self.name class Tag(models.Model):
"""
资产标签
"""
name = models.CharField('标签', max_length=32, unique=True) class Meta:
verbose_name_plural = "标签表" def __str__(self):
return self.name class Server(models.Model):
"""
服务器信息
"""
device_type_choices = (
(1, '服务器'),
(2, '交换机'),
(3, '防火墙'),
)
device_status_choices = (
(1, '上架'),
(2, '在线'),
(3, '离线'),
(4, '下架'),
)
device_type_id = models.IntegerField('服务器类型', choices=device_type_choices, default=1)
device_status_id = models.IntegerField('服务器状态', choices=device_status_choices, default=1) cabinet_num = models.CharField('机柜号', max_length=30, null=True, blank=True)
cabinet_order = models.CharField('机柜中序号', max_length=30, null=True, blank=True) idc = models.ForeignKey('IDC', verbose_name='IDC机房', null=True, blank=True)
business_unit = models.ForeignKey('BusinessUnit', verbose_name='属于的业务线', null=True, blank=True) tag = models.ManyToManyField('Tag') hostname = models.CharField('主机名', max_length=128, unique=True)
sn = models.CharField('SN号', max_length=64, db_index=True)
manufacturer = models.CharField(verbose_name='制造商', max_length=64, null=True, blank=True)
model = models.CharField('型号', max_length=64, null=True, blank=True) manage_ip = models.GenericIPAddressField('管理IP', null=True, blank=True) os_platform = models.CharField('系统', max_length=16, null=True, blank=True)
os_version = models.CharField('系统版本', max_length=16, null=True, blank=True) cpu_count = models.IntegerField('CPU个数', null=True, blank=True)
cpu_physical_count = models.IntegerField('CPU物理个数', null=True, blank=True)
cpu_model = models.CharField('CPU型号', max_length=128, null=True, blank=True) create_at = models.DateTimeField(auto_now_add=True, blank=True) class Meta:
verbose_name_plural = "服务器表" def __str__(self):
return self.hostname class Disk(models.Model):
"""
硬盘信息
"""
slot = models.CharField('插槽位', max_length=8)
model = models.CharField('磁盘型号', max_length=32)
capacity = models.CharField('磁盘容量GB', max_length=32)
pd_type = models.CharField('磁盘类型', max_length=32)
server_obj = models.ForeignKey('Server', related_name='disk') class Meta:
verbose_name_plural = "硬盘表" def __str__(self):
return self.slot class NIC(models.Model):
"""
网卡信息
"""
name = models.CharField('网卡名称', max_length=128)
hwaddr = models.CharField('网卡mac地址', max_length=64)
netmask = models.CharField(max_length=64)
ipaddrs = models.CharField('ip地址', max_length=256)
up = models.BooleanField(default=False)
server_obj = models.ForeignKey('Server', related_name='nic') class Meta:
verbose_name_plural = "网卡表" def __str__(self):
return self.name class Memory(models.Model):
"""
内存信息
"""
slot = models.CharField('插槽位', max_length=32)
manufacturer = models.CharField('制造商', max_length=32, null=True, blank=True)
model = models.CharField('型号', max_length=64)
capacity = models.FloatField('容量', null=True, blank=True)
sn = models.CharField('内存SN号', max_length=64, null=True, blank=True)
speed = models.CharField('速度', max_length=16, null=True, blank=True) server_obj = models.ForeignKey('Server', related_name='memory') class Meta:
verbose_name_plural = "内存表" def __str__(self):
return self.slot class AssetRecord(models.Model):
"""
资产变更记录,creator为空时,表示是资产汇报的数据。
"""
asset_obj = models.ForeignKey('Server', related_name='ar')
content = models.TextField(null=True) # 新增硬盘
creator = models.ForeignKey('UserProfile', null=True, blank=True)
create_at = models.DateTimeField(auto_now_add=True) class Meta:
verbose_name_plural = "资产记录表" def __str__(self):
return "%s-%s-%s" % (self.asset_obj.idc.name, self.asset_obj.cabinet_num, self.asset_obj.cabinet_order) class ErrorLog(models.Model):
"""
错误日志,如:agent采集数据错误 或 运行错误
"""
asset_obj = models.ForeignKey('Server', null=True, blank=True)
title = models.CharField(max_length=16)
content = models.TextField()
create_at = models.DateTimeField(auto_now_add=True) class Meta:
verbose_name_plural = "错误日志表" def __str__(self):
return self.title # 注册:repository/admin.py
# 注册模型表
from django.contrib import admin
from repository import models
# Register your models here. admin.site.register(models.Server)
admin.site.register(models.Disk)
admin.site.register(models.IDC)
admin.site.register(models.NIC)
admin.site.register(models.Tag)
admin.site.register(models.Memory)
admin.site.register(models.ErrorLog)
admin.site.register(models.AssetRecord)
admin.site.register(models.UserProfile)
admin.site.register(models.UserGroup)
admin.site.register(models.BusinessUnit)

11张表

9.视图函数

# API/views.py中getinfo函数将获取客户端上传的数据采集并处理
# 资产是否存在
server_info = json.loads(requests.body)
# 通过主机名获取老的数据对应的记录
hostname = server_info['basic']['data']['hostname']
# 去数据库中查询是否有该服务器信息
old_server_info = models.Server.objects.filter(hostname=hostname).first()
# 判断
if not old_server_info:
return HttpResponse("资产不存在")

以硬盘数据为例,进行增删改查

# 先判断status状态码是否正确
# 对数据进行筛选入库
# 以硬盘数据为例
# 1.校验相应状态码
if server_info['disk']['status'] != 10000:
# 2.记录错误日志
models.ErrorLog.objects.create(
asset_obj=old_server_info,
title='%s 采集硬盘数据出错了' % hostname,
content=server_info['disk']['data']
)
# 最新硬盘数据
new_disk_info = server_info['disk']['data']
"""
new_slot_list = [0,2]
old_slot_list = [0,1]
新增:new_slot_list - old_slot_list = 2
删除:old_slot_list - new_slot_list = 1
修改:交集
"""
# 老的硬盘数据
old_disk_info = models.Disk.objects.filter(server_obj=old_server_info).all()
# 先获取新硬盘数据所有slot号
new_slot_list = list(new_disk_info.keys())
# 再获取旧的硬盘数据所有的slot号
old_slot_list = [obj.slot for obj in old_disk_info]
# 利用集合操作获取新增的数据
add_slot_list = set(new_slot_list) - set(old_slot_list)
if add_slot_list:
record_list = []
for slot in add_slot_list:
disk_res = new_disk_info[slot]
# 添加变更记录
tmp = '添加插槽是:{slot},磁盘类型:{pd_type},磁盘容量是:{capacity},磁盘型号:{model}'.format(**disk_res)
# 添加disk记录 将服务器对象直接添加到字典中,之后直接利用**打散
disk_res['server_obj'] = old_server_info
models.Disk.objects.create(**disk_res)
record_list.append(tmp)
# 将变更信息添加到记录表中
record_str = ';'.join(record_list)
models.AssetRecord.objects.create(asset_obj=old_server_info,content=record_str) return HttpResponse('OK')

硬盘增加插槽

# 硬盘数据的删除
del_slot_list = set(old_slot_list) - set(new_slot_list)
if del_slot_list:
# 数据的删除:slot__in多条数据
models.Disk.objects.filter(slot__in=del_slot_list,server_obj=old_server_info).delete()
# 记录变更
record_str = "删除的槽位slot是:%s"%(';'.join(del_slot_list))
models.AssetRecord.objects.create(asset_obj=old_server_info,content=record_str)

删除硬盘槽位

# API/views.py
# 硬盘数据的修改
up_slot_list = set(new_slot_list) & set(old_slot_list)
if up_slot_list:
record_list = []
for slot in up_slot_list:
new_disk_row = new_disk_info[slot] # 新数据 字典
# 数据库中老数据 对象
old_disk_row = models.Disk.objects.filter(slot=slot,server_obj=old_server_info).first()
# 反射:通过字符串操作对象的属性和方法(出现字符串和对象毫不犹豫使用反射的思路)
for k,new_v in new_disk_row.items():
# 1.利用反射先从老的数据中获取数据
old_v = getattr(old_disk_row,k)
# 2.判断老的数据和新的数据是否相同
if new_v != old_v:
tmp = '槽位:%s,%s由原来的%s变成了%s'%(slot,k,old_v,new_v)
record_list.append(tmp)
# 3.更新数据:将新数据设置到老的数据对象中
setattr(old_disk_row,k,new_v)
# 4.调用对象的save方法,更新数据
old_disk_row.save()
if record_list:
models.AssetRecord.objects.create(asset_obj=old_server_info,content=';'.join(record_list)) return HttpResponse('OK')

硬盘数据的修改

10.前后端分离

to B:to business  面向企业产品
to C:to client 面向客户产品
to C:
前后端分离
讲究美观和用户体验
前端
vue react angular.js
# 前后端分离网站:路飞
to B:(CMDB)
前后端不分离
讲究用途和功能齐全
前端
bootstrap、layui
# 前后端不分离网站:很丑的基本都是

11.前端框架使用layui

https://www.layui.com/demo/admin.html

1.使用CDN模式
https://www.bootcdn.cn/ 2.直接下载文档
使用文档中的css和js
layui/layui.all.js
# <script src="/static/layui/layui.all.js"></script>
layui/css/layui.css
# <link rel="stylesheet" href="/static/layui/css/layui.css">

12. Xadmin使用(后台管理)

  Django-xadmin介绍

    Django是python的重量级web框架,写得少,做得多,非常适合后端开发,它很大的一个亮点是,自带后台管理模块,但它自带的后台管理有点丑,而Xadmin是基于bootstrap开发的一套后台管理框架,界面非常美观,只需几步就可以替换自带的Django_admin
xadmin有很多小bug,使用的时候最好心理准备,可能需要你自己修改源码
基于bootstrap的后台管理
vue-element-admin:最新使用的
基于vue开发的非常酷炫的后台管理

安装步骤:

1.在python 2.x版本中安装方法
pip install xadmin 2.xadmin在python3.6.x时代的安装方法,需要以下插件
pip3 install django-import-export
pip3 install django-reversion
pip3 install django-formtools==2.1
pip3 install future
pip3 install httplib2
pip3 install six
pip3 install django-crispy-forms bootstrap高级模板:https://wrapbootstrap.com/

3.下载xadmin
  https://github.com/sshwsfc/xadmin   只需要zip文件中的xadmin目录

4.在django中的根目录下创建extra_apps(如果不存在此文件夹则创建, 然后鼠标右键extra_app 随后 mark as sources root,用来创建第三方应用
Python Package是带init文件的,跟普通Package不同)
创建完extra_apps,需要在settings中配置一下extra_apps。设置为可搜索的路径
# 1.创建完extra_apps,需要在settings中配置一下extra_apps。设置为可搜索的路径

import sys
sys.path.insert(0, os.path.join(BASE_DIR, 'extra_apps')) # 把extra_apps文件夹添加到搜索目录中 # 2.配置到 INSTALLED_APPS
## 显示中文
LANGUAGE_CODE = 'zh-hans' # TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai'
# xadmin后台界面显示中文
LANGUAGE_CODE = 'zh-hans'

# 新增
INSTALLED_APPS = [
'xadmin',
'crispy_forms', # 注意crispy_forms之间是下划线隔开,不是横线
] # 3. urls.py中把admin换成xadmin
import xadmin
from django.conf.urls import url
from django.contrib import admin urlpatterns = [
# url('admin/', admin.site.urls),
url(r'^xadmin/', xadmin.site.urls),
] # 4.迁移文件:迁移完成之后数据库会多几张xadmin_开头的表
python3 manage.py makemigrations
python3 manage.py migrate """因为版本的原因,有一些报错慢慢解决""" #5.pycharm创建superuser 用户
python3 manage.py 访问:http://127.0.0.1:8000/xadmin/
Xadmin使用

#1.需要在后台管理app中创建adminx.py文件
import xadmin
from repository import models
from xadmin import views class UserProfileAdmin(object):
# 1.显示的字段名称
list_display = ['id','name' ,'email','phone','mobile'] # 2.搜索时可输入的字段内容
search_fields = ['id', 'name', 'email', 'phone'] # 3.点击id可进入详细界面进行编辑(默认的)
list_display_links = ('id',) # 4.可编辑的列名
list_editable = ['name' ,'email','phone','mobile']
# list_filter = ['name' ,'email','phone','mobile'] # 5.每页显示多少条
list_per_page = 20 #6.根据id排序
ordering = ('id',) 
 
# 7.设置只读字段 
readonly_fields = ('id',) #8.显示本条数据的所有信息
show_detail_fields = ['asset_name'] # data_charts = {
# "user_count": {'title': u"用户分布", "x-field": "name", "y-field": ("id",),},
# # "avg_count": {'title': u"Avg Report", "x-field": "date", "y-field": ('avg_count',), "order": ('date',)}
# } xadmin.site.register(models.UserProfile,UserProfileAdmin) # 2.是否设置书签名
默认是开启书签的
show_bookmarks 属性: 设置是否开启书签功能, 默认为 True
list_bookmarks 属性: 设置默认的书签. 用户可以在列表页面添加自己的书签, 你也可以实现设定好一些书签 list_bookmarks = [{
"title": "存在邮箱", # 书签的名称, 显示在书签菜单中
"query": {"user_email__contains": '@'}, 过滤参数, 是标准的 queryset 过滤
"order": ("-user_name",), # 排序参数
"cols": ('user_name', 'user_email', 'user_mobile'),# 显示的列
}] # 3.数据导出
"""
如果想要导出Excel数据,需要安装xlwt。 默认情况下,xadmin会提供Excel,CSV,XML,json四种格式的数据导出,可以通过设置OptionClass的list_export属性来指定使用哪些导出格式(四种格式分别用xls,csv,xml,json表示)或是将list_export设置为None来禁用数据导出功能
"""
list_export = ('xls', 'xml', 'json')
list_export_fields = ('id', 'name', 'title') # 4. 设置全局配置
#如下的代码可以在任何的app中
import xadmin
from repository import models from xadmin import views
# 全局修改,固定写法
class GlobalSettings(object):
# 修改title
site_title = 'xxx后台管理界面'
# 修改footer
site_footer = 'xxx的公司'
# 收起菜单
menu_style = 'accordion' # 设置 models图标
# https://v3.bootcss.com/components/
global_search_models = [models.Disk, models.Server]
global_models_icon = {
# Server: "glyphicon glyphicon-tree-conifer", Pool: "fa fa-cloud"
models.Server: "fa fa-linux",
models.Disk: "fa fa-cloud"
} # 将title和footer信息进行注册
xadmin.site.register(views.CommAdminView,GlobalSettings) # 创建xadmin的最基本管理器配置,并与view绑定
class BaseSetting(object):
# 开启主题功能
enable_themes = True
use_bootswatch = True # 将基本配置管理与view绑定
xadmin.site.register(views.BaseAdminView,BaseSetting) #5.图表显示(不好看不用)
data_charts = {
"host_idc_counts": {
'title': '机房统计',
'x-field': "idc",
'y-field': ("idc",),
'option': {
"series": {"bars": {"align": "center", "barWidth": 0.3, "show": True}},
"xaxis": {"aggregate": "count", "mode": "categories"}
}
}

xadmin使用

13. 数据可视化

highcharts
echarts
antv

CMDB开发(三)的更多相关文章

  1. Python CMDB开发

    Python CMDB开发   运维自动化路线: cmdb的开发需要包含三部分功能: 采集硬件数据 API 页面管理 执行流程:服务器的客户端采集硬件数据,然后将硬件信息发送到API,API负责将获取 ...

  2. App开发三种模式

    APP开发三种模式 现在App开发的模式包含以下三种: Native App 原生开发AppWeb App 网页AppHybrid App 混合原生和Web技术开发的App 详细介绍: http:// ...

  3. iOS开发三步搞定百度推送

    iOS开发三步搞定百度推送   百度推送很简单,准备工作:在百度云推送平台注册应用,上传证书. 步骤一: 百度云推送平台 http://push.baidu.com/sdk/push_client_s ...

  4. 基于Spring MVC的Web应用开发(三) - Resources

    基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...

  5. Java Web高性能开发(三)

    今日要闻: Clarifai:可识别视频中物体 最近几年,得益于深度学习技术的发展,谷歌和Facebook等企业的研究人员在图形识别软件领域取得了重大突破.现在,一家名为Clarifai的创业公司则提 ...

  6. C#的百度地图开发(三)依据坐标获取位置、商圈及周边信息

    原文:C#的百度地图开发(三)依据坐标获取位置.商圈及周边信息 我们得到了百度坐标,现在依据这一坐标来获取相应的信息.下面是相应的代码 public class BaiduMap { /// < ...

  7. Qt计算器开发(三):执行效果及项目总结

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/guodongxiaren/article/details/26046543 执行效果 project ...

  8. GitHub 多人协作开发 三种方式:

    GitHub 多人协作开发 三种方式: 一.Fork 方式 网上介绍比较多的方式(比较大型的开源项目,比如cocos2d-x) 开发者 fork 自己生成一个独立的分支,跟主分支完全独立,pull代码 ...

  9. 从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件

    标题:从零开始实现ASP.NET Core MVC的插件式开发(三) - 如何在运行时启用组件 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/112 ...

  10. Python服务器开发三:Socket

    Python服务器开发三:Socket   socket是操作系统中I/O的延续,它可以使进程和机器之间的通信成为可能.socket可以看成一个标准的文件描述符.不同的是文件需要用open()函数打开 ...

随机推荐

  1. 记录--uniapp上如何实现安卓app微信登录功能(操作流程总结)

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 uniapp上如何实现安卓app微信登录功能?下面本篇文章给大家分享一下uniapp上实现安卓app微信登录的权限申请.开发的具体操作流程 ...

  2. SMASH:经典One-Shot神经网络搜索,仅需单卡 | ICLR 2018

    SMASH方法使用辅助网络生成次优权重来支持网络的快速测试,从结果来看,生成的权重与正常训练的权重在准确率上存在关联性,整体搜索速度很快,仅需要单卡进行搜索,提供了一个很好的新思路.   来源:晓飞的 ...

  3. KingbaseES 串行化隔离级别引起的阻塞分析

    前言 这是实际生产环境中遇到的一个问题,前端业务有如下报错: could not serialize access due to read/write dependencies among trans ...

  4. Java字符数组char和字符串String互相转化

    字符串转换为数组 1 String str = "123abc"; 2 char[] arr = str.toCharArray(); // char数组 3 for (int i ...

  5. 测试开发之网络篇-OSI七层协议

    今天,我们来了解一下OSI(Open System Interconnect)开放式系统互连.它是ISO组织在1985年发布的网络互连模型,该标准定义了网络互连的七层框架.其内容简述如下: 我们办公室 ...

  6. #二分图匹配#UVA1194 Machine Schedule

    题目 有两台机器 \(A,B\) 分别有 \(n,m\) 种模式. 现在有 \(k\) 个任务.对于每个任务 \(i\) ,给定两个整数 \(a_i\) 和 \(b_i\)​, 表示如果该任务在 \( ...

  7. #计数#A 古老谜题

    From NOIP2020 模拟赛 B 组 Day4 题目 给定一个长度为\(n\)的01序列\(a\), 问有多少个三元组\((l,p,r),1\leq l<p<r\leq n\) 满足 ...

  8. 华为Push用户增长服务:精准触达,加速增长

    速戳了解华为Push用户增长服务:通过精细化运营,助力开发者高效实现用户增长,提升用户活跃度和粘性! 合作咨询请点此链接 了解更多详情>> 访问华为开发者联盟官网 获取开发指导文档 华为移 ...

  9. 华为终端云服务牵手Likee,助力其用户与变现双增长

    如今,社交媒体越来越深入人们的生活,改变了人们沟通方式的同时,也塑造着全新的人际关系和品牌形象.为了迎合用户多样化的需求和提升用户体验, 社交媒体行业的新老企业不断追逐着新技术和新功能.据调查机构Da ...

  10. Windows系统编译libhv带SSL,开启WITH_OPENSSL

    需要开发一个https的服务,使用libhv来做,需要重新编译libhv,需要开启 WITH_OPENSSL,前面编译一直很顺利,但是打开VS生成动态库的时候,报错,找不到ssl相关的文件,看了官方的 ...