首页 文章 精选 留言 我的

精选列表

搜索[api-],共61篇文章
优秀的个人博客,低调大师

图像抠图-智能抠像-背景分离API-图片前景分割

# 前言 图像抠图可自动识别人体、物品的轮廓范围,将其与背景精准分离,输出带透明通道的抠图结果,适用于拍照背景替换、照片合成、身体特效等场景,帮助开发者快速实现智能抠像能力。 图像抠图可广泛应用在电商设计、影像娱乐、直播短视频等场景,比如: **电商与设计制图**:一键抠取商品、模特主体,快速完成白底图、海报及主图设计,提升制图效率。 **照片合成与特效**:将人像从原背景中分离后合成到新场景,实现证件照换底、创意合影等玩法。 **直播与短视频**:实时分离主体与背景,用于虚拟背景、绿幕替代等场景,丰富内容表现形式。 ![](https://developer.qcloudimg.com/http-save/yehe-12007261/388bc11d4aa42f6d2d6d369d25b4be12.png) # API介绍 ## 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |base64|String|否|图片base64串。base64、file、url 三选其一| |file|File|否|图片文件。base64、file、url 三选其一| |url|String|否|图片文件url。base64、file、url 三选其一| ## 返回样例 ``` { "code": 200, // 返回码,详见返回码说明 "msg": "成功", // 返回码对应描述 "taskNo": "596744245171443788745816", // 本次请求号 "data": { "mattingUrl": "xxx.jpg" // 处理后的图片,有效期30天。建议自行下载保存,避免丢失 } } ```

优秀的个人博客,低调大师

企业报税接口-公司报税API-企业税申报接口介绍

# 前言 企业税申报,是打通企业与各省电子税务局的数据传输通道,支持各企业的所有税种的申报表数据报送到税局并完成申报缴款、PDF下载、完税证明下载等相关业务,负责搭建公司内部企业端与外部系统间的开放桥梁。 ![](https://developer.qcloudimg.com/http-save/yehe-12007261/03fae5c7d3d0754bab9ef6784c7b1eb3.png) # 为什么需要企业税申报API 集成企业税申报API,可以方便的自有的系统中,实现智能报税。 集成企业税支持税齐全,包括: 1. 增值税及附加税(适用于小规模纳税人) 1. 增值税及附加税(适用于一般纳税人) 1. 消费税及附加税费申报表 1. 企业所得税年报(汇算清缴) 1. 印花税按次、按期分开申报 1. 印花税按次和按期合并申报 1. 文化事业建设费 1. 个人所得税代扣代缴 1. 个人所得税生产经营所得税 1. 综合所得个人所得税预扣预缴申报 1. 分类所得个人所得税代扣代缴报告表 1. 非居民个人所得税代扣代缴报告表 1. 残保金 1. 社保费 1. 环境保护税纳税申报表 1. 城镇土地使用税 1. 房产税 1. 城镇土地使用税税源采集 1. 房产税税源采集 1. 资源税 1. 水资源税 1. 水资源税源采集 1. 车辆购置税 1. 车船税 1. 契税 1. 非税收入通用申报表 1. 企业年度关联业务往来报告表 1. 财务报表申报(月季报) 1. 财务报表年度申报(年报) 1. 企业集团合并财务报表(年报) 1. 财产行为税合并纳税申报(房土) 1. 企业所得税(代扣代缴) 1. 代扣代缴、代收代缴税款报告表(仅支持增值税代扣代缴) 1. 重点税源 1. 增值税预缴 1. 土地增值税 1. 定期定额申报表 # 如何使用 ![](https://developer.qcloudimg.com/http-save/12007261/3fc015d010284eaaa4bb8b1efe8a2a89.jpg) 1. 使用者使用之前先向服务商报备需要企业税申报的企业资料,包括:纳税人识别号(统一社会信用代码)、企业名称。 1. 服务商为使用者开通产品使用期限。 1. 根据接口说明对接。

优秀的个人博客,低调大师

Google地图官方API-在地图上绘图(自定义符号)

符号介绍当您想在标记上使用基于矢量的图标或将图像添加到折线时,符号非常有用 。 标记支持栅格图像和矢量图像的使用。请参阅有关自定义标记图标的文档 。A 是可以显示在或对象上的基于矢量的图像 。符号的形状由使用SVG路径符号的路径定义 。虽然是唯一必需的属性,但该 对象支持多种属性,使您可以自定义视觉外观,例如笔触和填充的颜色和粗细。请参阅下面的属性列表。 SymbolMarkerPolylinepathSymbol 通过SymbolPath 该类可以使用几个预定义的符号。请参阅下面的列表。 符号的属性请注意,a的默认行为Symbol取决于它出现在标记还是折线上而略有不同。这些差异在下面的属性列表中描述。 A Symbol支持以下属性: path(必填)是定义符号形状的路径。您可以在其中使用预定义的路径之一,也可以使用 SVG路径符号定义自定义路径 。注意:折线上的矢量路径必须适合22x22px的正方形。如果路径中包括该正方形以外的点,则必须将符号的属性调整为分数值(例如0.2),以使生成的缩放点适合正方形。 google.maps.SymbolPathscaleanchor设置符号相对于标记或折线的位置。符号路径的坐标分别通过锚点的x和y坐标向左和向上转换。默认情况下,符号锚定在(0, 0)。位置用与符号路径相同的坐标系表示。fillColor是符号填充的颜色(即笔触边界的区域)。支持所有CSS3颜色,但扩展的命名颜色除外。对于标记上的符号,默认值为“黑色”。对于折线上的符号,默认值为相应折线的笔触颜色。fillOpacity定义符号填充的相对不透明度(即缺乏透明度)。值的范围是0.0(完全透明)到1.0(完全不透明)。默认值为0.0。rotation是旋转符号的角度,顺时针以度表示。默认情况下,符号标记的旋转度为0,折线上的符号旋转了其所处边缘的角度。在多段线上设置符号的旋转将固定符号的旋转,使其不再跟随直线的曲线。scale设置符号缩放的数量。对于符号标记,默认比例为1。缩放后,符号可以为任意大小。对于折线上的符号,默认比例为折线的笔划粗细。缩放后,符号必须位于以符号锚点为中心的22x22px正方形内。strokeColor是符号轮廓的颜色。支持所有CSS3颜色,但扩展的命名颜色除外。对于标记上的符号,默认值为“黑色”。对于折线上的符号,默认颜色是折线的笔触颜色。strokeOpacity确定符号笔划的相对不透明度(即缺乏透明度)。值的范围是0.0(完全透明)到1.0(完全不透明)。对于符号标记,默认值为1.0。对于折线上的符号,默认值为折线的笔触不透明度。strokeWeight定义符号轮廓的粗细。默认值为scale符号的。预定义符号Maps JavaScript API提供了一些内置符号,您可以通过该类将其添加到标记或折线中 。 SymbolPath 默认符号包括一个圆圈和两种类型的箭头。向前和向后箭头均可用。这对于折线特别有用,因为折线上的符号方向是固定的。正向被认为是在多义线的终点方向。 您可以使用任何默认符号选项来修改预定义符号的笔触或填充。 包括以下预定义符号: 名称 描述 例google.maps.SymbolPath.CIRCLE 一个圆圈。 google.maps.SymbolPath.BACKWARD_CLOSED_ARROW 向后所有方向都闭合的箭头。 google.maps.SymbolPath.FORWARD_CLOSED_ARROW 在所有侧面均闭合的向前箭头。 google.maps.SymbolPath.BACKWARD_OPEN_ARROW 在一侧打开的向后箭头。 google.maps.SymbolPath.FORWARD_OPEN_ARROW 一侧打开的向前箭头。 向标记添加符号要在标记上显示基于矢量的图标 Symbol,请将具有所需路径的对象文字传递 到标记的 icon属性。 下面的示例使用预定义的矢量路径之一创建一个图标。 // This example uses a symbol to add a vector-based icon to a marker.// The symbol uses one of the predefined vector paths ('CIRCLE') supplied by the// Google Maps JavaScript API. function initMap() { var map = new google.maps.Map(document.getElementById('map'), { zoom: 4, center: {lat: -25.363882, lng: 131.044922} }); var marker = new google.maps.Marker({ position: map.getCenter(), icon: { path: google.maps.SymbolPath.CIRCLE, scale: 10 }, draggable: true, map: map }); } 查看示例。 以下示例使用 SVG路径符号 为标记创建自定义图标。 // This example uses SVG path notation to add a vector-based symbol// as the icon for a marker. The resulting icon is a star-shaped symbol// with a pale yellow fill and a thick yellow border. function initMap() { var map = new google.maps.Map(document.getElementById('map'), { zoom: 4, center: {lat: -25.363882, lng: 131.044922} }); var goldStar = { path: 'M 125,5 155,90 245,90 175,145 200,230 125,180 50,230 75,145 5,90 95,90 z', fillColor: 'yellow', fillOpacity: 0.8, scale: 1, strokeColor: 'gold', strokeWeight: 14 }; var marker = new google.maps.Marker({ position: map.getCenter(), icon: goldStar, map: map });} 查看示例。 向折线添加符号要在折线上显示符号,请设置对象的icons[]属性PolylineOptions。该icons[]数组采用一个或多个对象文字,具有以下属性: IconSequence icon(required)是要在线上渲染的符号。offset确定距要渲染图标的行的起点的距离。该距离可以表示为线条长度的百分比(例如,“ 50%”),也可以表示为像素(例如,“ 50px”)。默认值为“ 100%”。repeat确定行中连续图标之间的距离。该距离可以表示为线条长度的百分比(例如,“ 50%”),也可以表示为像素(例如,“ 50px”)。要禁用重复图标,请指定“ 0”。默认值为“ 0”。注意:如果折线是测地线(也就是说,如果其geodesic属性设置为true)offset,repeat 则默认指定为米和米的距离。将像素值offset或 设置为repeat像素值将导致在屏幕上以像素为单位计算距离。结合使用符号和PolylineOptions类,您可以对地图上折线的外观和感觉进行大量控制。以下是您可以应用的一些自定义示例。 箭使用该IconSequence.offset属性可将箭头添加到折线的起点或终点。 // Define a symbol using a predefined path (an arrow)// supplied by the Google Maps JavaScript API.var lineSymbol = { path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW}; // Create the polyline and add the symbol via the 'icons' property.var line = new google.maps.Polyline({ path: [{lat: 22.291, lng: 153.027}, {lat: 18.291, lng: 153.027}], icons: [{ icon: lineSymbol, offset: '100%' }], map: map}); 查看示例。 虚线通过将折线的不透明度设置为0,并以规则的时间间隔在该线上覆盖不透明的符号,可以实现虚线效果。 // Define a symbol using SVG path notation, with an opacity of 1.var lineSymbol = { path: 'M 0,-1 0,1', strokeOpacity: 1, scale: 4}; // Create the polyline, passing the symbol in the 'icons' property.// Give the line an opacity of 0.// Repeat the symbol at intervals of 20 pixels to create the dashed effect.var line = new google.maps.Polyline({ path: [{lat: 22.291, lng: 153.027}, {lat: 18.291, lng: 153.027}], strokeOpacity: 0, icons: [{ icon: lineSymbol, offset: '0', repeat: '20px' }], map: map}); 查看示例。 自定义路径自定义符号允许您向折线添加许多不同的形状。 // Define the custom symbols. All symbols are defined via SVG path notation.// They have varying stroke color, fill color, stroke weight,// opacity and rotation properties. var symbolOne = { path: 'M -2,0 0,-2 2,0 0,2 z', strokeColor: '#F00', fillColor: '#F00', fillOpacity: 1 }; var symbolTwo = { path: 'M -1,0 A 1,1 0 0 0 -3,0 1,1 0 0 0 -1,0M 1,0 A 1,1 0 0 0 3,0 1,1 0 0 0 1,0M -3,3 Q 0,5 3,3', strokeColor: '#00F', rotation: 45 }; var symbolThree = { path: 'M -2,-2 2,2 M 2,-2 -2,2', strokeColor: '#292', strokeWeight: 4 }; // Create the polyline and add the symbols via the 'icons' property. var line = new google.maps.Polyline({ path: [{lat: 22.291, lng: 153.027}, {lat: 18.291, lng: 153.027}], icons: [ { icon: symbolOne, offset: '0%' }, { icon: symbolTwo, offset: '50%' }, { icon: symbolThree, offset: '100%' } ], map: map }); 查看示例。 动画符号您可以通过使用DOM的window.setInterval()功能以固定间隔更改符号的偏移量来沿路径设置符号动画 。 // This example adds an animated symbol to a polyline. function initMap() { var map = new google.maps.Map(document.getElementById('map'), { center: {lat: 20.291, lng: 153.027}, zoom: 6, mapTypeId: 'terrain' }); // Define the symbol, using one of the predefined paths ('CIRCLE') // supplied by the Google Maps JavaScript API. var lineSymbol = { path: google.maps.SymbolPath.CIRCLE, scale: 8, strokeColor: '#393' }; // Create the polyline and add the symbol to it via the 'icons' property. var line = new google.maps.Polyline({ path: [{lat: 22.291, lng: 153.027}, {lat: 18.291, lng: 153.027}], icons: [{ icon: lineSymbol, offset: '100%' }], map: map }); animateCircle(line);} // Use the DOM setInterval() function to change the offset of the symbol// at fixed intervals.function animateCircle(line) { var count = 0; window.setInterval(function() { count = (count + 1) % 200; var icons = line.get('icons'); icons[0].offset = (count / 2) + '%'; line.set('icons', icons); }, 20);} 查看示例。

优秀的个人博客,低调大师

Java高并发秒杀Api-业务分析与DAO层构建2

章节目录 DAO 设计编码 数据库设计与编码 DAO层实体和接口编码 基于mybatis实现DAO理论 基于mybatis实现DAO接口-1 mybatis整合Spring DAO层编码解析 Dao 设计编码 1.pom.xml 引入项目依赖的包 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.seckill</groupId> <artifactId>seckill</artifactId> <packaging>war</packaging> <version>1.0</version> <name>seckill Maven Webapp</name> <url>http://maven.apache.org</url> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <!--使用注解方式运行junit--> <version>4.11</version> <scope>test</scope> </dependency> <!--补全项目依赖--> <!--日志--> <!--1.日志 java日志,slf4j,log4j,logback,common-logging--> <!--2. slf4j 是规范、接口 日志实现 log4j,logback,common-logging 使用slf4j+logback --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.12</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.1.2</version> </dependency> <!--实现slf4j接口并实现,直接通过slf4j来进行日志记录--> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.2</version> </dependency> <!--数据库依赖--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.35</version> <scope>runtime</scope> </dependency> <!--数据库连接池--> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> </dependency> <!--dao层依赖:MyBatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.3.0</version> </dependency> <!--mybatis自身实现的spring依赖--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.3</version> </dependency> <!--servlet web相关依赖--> <dependency> <groupId>taglibs</groupId> <artifactId>standard</artifactId> <version>1.1.2</version> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.5.4</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> </dependency> <!--end java web 相关依赖--> <!--spring 依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--spring dao层依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--spring 声明式事务--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--spring web 相关依赖 servlet容器加载spring ioc spring aop 启动spring 的工厂--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>4.1.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.1.7.RELEASE</version> </dependency> <!--junit相关依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.1.7.RELEASE</version> </dependency> </dependencies> <build> <finalName>seckill</finalName> </build> </project> 2.数据库设计与编码 数据库脚本如下所示: --数据库初始化脚本 --创建数据库 CREATE DATABASE seckill; --使用数据库 use seckill; --创建秒杀库存表 CREATE TABLE seckill( `seckill_id` bigint not null AUTO_INCREMENT COMMENT '商品库存id', `name` VARCHAR (120) not null COMMENT '商品名称', `stock` int not null COMMENT '库存数量', `start_time` TIMESTAMP NOT NULL COMMENT '秒杀开始时间', `end_time` TIMESTAMP NOT NULL COMMENT '秒杀结束时间', `create_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', PRIMARY KEY (seckill_id), key idx_start_time(start_time), key idx_end_time(end_time), key idx_create_time(create_time) ) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀库存表'; --初始化数据 INSERT INTO seckill(name,stock,start_time,end_time) VALUES ('1000元秒杀iphone x','100','2018-05-04 00:00:00','2018-05-05 00:00:00'), ('500元秒杀ipad x','200','2018-05-04 00:00:00','2018-05-05 00:00:00'), ('300元秒杀小米4','300','2018-05-04 00:00:00','2018-05-05 00:00:00'), ('200元秒杀小米note','400','2018-05-04 00:00:00','2018-05-05 00:00:00'); --秒杀成功明细表 CREATE TABLE success_killed( `seckill_id` bigint NOT NULL COMMENT '秒杀商品id', `user_phone` VARCHAR(11) NOT NULL COMMENT '用户手机号', `state` tinyint NOT NULL DEFAULT -1 COMMENT '状态标志:-1:无效 0:成功 1:已付款', `create_time` TIMESTAMP NOT NULL COMMENT '创建时间', PRIMARY KEY (seckill_id,user_phone),/*联合主键*/ key idx_create_time(create_time) ) ENGINE=InnoDB AUTO_INCREMENT=1000 DEFAULT CHARSET=utf8 COMMENT='秒杀成功明细表'; --连接数据库的控制台 mysql -u root -p 3.DAO层实体与接口编码 数据库与实体类映射 3.1MyBatis 实现DAO接口的方式 Mapper自动实现DAO接口 sql直接编写,注解sql(sql更改,原代码需要重新编译),xml方式,单独更新sql,源文件不需要重新编译。 API 编程方式自动实现DAO 接口 3.2 基于mybatis实现DAO接口-1 全局mybatis-conf.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--配置全局属性--> <configuration> <settings> <!-- changes from the defaults for testing --> <setting name="cacheEnabled" value="false" /> <!--使用jdbc 的getGeneratedKeys 获取数据库自增主键值--> <setting name="useGeneratedKeys" value="true" /> <!--使用列别名 替换列名 默认true 复制到对应的entity属性中 select name as tile from table --> <setting name="useColumnLable" value="true"/> <!--开启驼峰命名转化--> <setting name="mapUnderscoreCameCase" value="true"/> <!--REUSE 执行器会重用预处理语句--> <setting name="defaultExecutorType" value="REUSE" /> </settings> </configuration> SecKillDao.xml编写 <?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="org.seckill.dao.SecKillDao"> <!--目的:为DAO方法提供SQL语句配置 parameter-type不用给 #与$的区别 预编译 1.#{} 解析为一个 JDBC 预编译语句(prepared statement)的参数标记符。 会被解析为 ?占位符 动态SQL-会对SQL进行动态解析,解析为一个BoundSql对象 变量替换 2.${} 仅仅为一个纯粹的 string 替换,在动态 SQL 解析阶段将会进行变量替换 3.#{}的变量的替换是在 DBMS 中。 4.${} 在预编译之前已经被变量替换了,这会存在 sql 注入问题 5.表名是不能带''号的,所以使用${} 防止出现变量替换后表名带'' --> <update id="reduceStock"> <!--具体sql--> UPDATE seckill SET stock = stock - 1 WHERE seckill_id = #{secKillId} AND start_time <![CDATA[<=]]> #{killTime} AND end_time >= #{killTime} and stock > 0 </update> <select id="queryById" resultType="SecKill" parameterType="long"> SELECT seckill_id as seckillId,name,stock,start_time,end_time,create_time from seckill where seckill_id = #{secKillId} </select> <select id="queryAll" resultType="SecKill" parameterType="int"> SELECT seckill_id as seckillId,name,stock,start_time,end_time,create_time from seckill order by create_time desc limit #{offset},${limit} </select> </mapper> SuccessKilledDao.xml 编写 <?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="org.seckill.dao.SuccessKilledDao"> <insert id="insertSuccessKilled" > <!--如果出现重复,主键冲突,直接产生一个错误--> INSERT ignore INTO success_killed(seckill_id,user_phone) VALUES (#{secKillId},#{userPhone}) </insert> <select id="queryByIdWithSecKill" resultType="SuccessKilled" > <!--根据id查询SuccessKilled并携带SecKill实体--> <!--如何告诉mybatis 把结果映射到SuccessKilled的同时映射secKill 属性--> <!--可以自由控制SQL 优化等--> SELECT sk.seckill_id, sk.user_phone, sk.create_time, sk.state, sc.seckill_id as "secKill.seckill_id", sc.name as "secKill.name", sc.stock as "secKill.stock", sc.start_time as "secKill.start_time", sc.end_time as "sceKill.end_time", sc.create_time as "secKill.create_time" FROM success_killed sk INNER JOIN seckill sc on sk.seckill_id = sc.seckill_id WHERE sk.seckill_id = #{secKillId} </select> </mapper> entity实体层实现-SecKill、SuccessKilled package org.seckill.domain; import java.util.Date; /** * 秒杀-库存表 entity */ public class SecKill { private long seckillId; //秒杀-商品id private String name; //秒杀-商品名字 private int stock; //秒杀-商品库存 private Date startTime; //秒杀-开始时间 private Date endTime; //秒杀-结束时间 private Date createTime; //秒杀-商品新建时间 public long getSeckillId() { return seckillId; } public void setSeckillId(long seckillId) { this.seckillId = seckillId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getStock() { return stock; } public void setStock(int stock) { this.stock = stock; } public Date getStartTime() { return startTime; } public void setStartTime(Date startTime) { this.startTime = startTime; } public Date getEndTime() { return endTime; } public void setEndTime(Date endTime) { this.endTime = endTime; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } @Override public String toString() { return "SecKill{" + "seckillId=" + seckillId + ", name='" + name + '\'' + ", stock=" + stock + ", startTime=" + startTime + ", endTime=" + endTime + ", createTime=" + createTime + '}'; } } package org.seckill.domain; import java.util.Date; /** * 成功秒杀明细表-SuccessKilled */ public class SuccessKilled { private long secKillId; //秒杀-成功的商品id private String userPhone; //秒杀-成功的用户手机号 private short state; //秒杀-明细状态 private Date createTime; //秒杀-秒杀成功的时间 //one(被秒杀商品)-to-many(秒杀记录) private SecKill secKill; public long getSecKillId() { return secKillId; } public void setSecKillId(long secKillId) { this.secKillId = secKillId; } public String getUserPhone() { return userPhone; } public void setUserPhone(String userPhone) { this.userPhone = userPhone; } public short getState() { return state; } public void setState(short state) { this.state = state; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public SecKill getSecKill() { return secKill; } public void setSecKill(SecKill secKill) { this.secKill = secKill; } @Override public String toString() { return "SuccessKilled{" + "secKillId=" + secKillId + ", userPhone='" + userPhone + '\'' + ", state=" + state + ", createTime=" + createTime + '}'; } } DAO层接口功能声明-SecKillDao、SuccessKilledDao package org.seckill.dao; import org.seckill.domain.SecKill; import java.util.Date; import java.util.List; /** * 常用的操作 */ public interface SecKillDao { /** * 功能:减库存 * @param secKillId * @param killTime create_time? * @return 如果影响行数>1,表示更新的记录行数 */ int reduceStock(long secKillId,Date killTime); /** * 根据秒杀商品id查询秒杀商品详情 * @param secKillId * @return */ SecKill queryById(long secKillId); /** * 根据偏移量查询秒杀商品列表, * @param offset * @param limit * @return */ List<SecKill> queryAll(int offset,int limit); } package org.seckill.dao; import org.seckill.domain.SuccessKilled; public interface SuccessKilledDao { /** * 功能:新增用户秒杀明细 * @param secKillId * @param userPhone * @return 插入的行数,禁止插入表示插入失败 则返回0 */ int insertSuccessKilled(long secKillId,long userPhone); /** * 功能:根据明细id查询具体的秒杀明细并携带秒杀商品对象 * @param secKillId * @return */ SuccessKilled queryByIdWithSecKill(long secKillId); } MyBatis整合Spring 整合的目标: 更少的编码 更少的配置 足够的灵活性1.mybatis优点 更少的编码、只写接口、不写实现 2.DAO层接口声明能显示说明很多事情 轻量级显式接口声明 更少的配置-别名 image.png 使用spring 提供的package-scan 扫描实体包下的所有实体类。可以简写resultType 更少的配置-配置扫描 单独使用mybatis的场景下,配置文件扫描方式 <mapper resource="mapper/SecKillDao.xml">.... 使用spring 整合 mybatis 自动扫描配置文件,采用通配符的方式。 自动实现DAO接口,DAO接口的实现类是自动注入至Spring 容器 足够的灵活性 image.png 5.DAO层接口设计与SQL编写的反思 DAO - data access object 数据访问层的简称 这一层我们主要做的是核心数据操作的接口声明,以及SQL编写,并没有涉及到Service 业务逻辑层代码的编写。这样做的好处是什么呢? 主要有以下三点 代码分层,DAO层只关注于核心数据操作的接口声明&SQL编写 代码和SQL的分离,方便代码review 业务逻辑层主要做事务控制、事务中完成DAO层的代码组合与拼接。每一层都可以做单元测试。 接下来详解mybatis 与 spring 整合编码的过程

优秀的个人博客,低调大师

Java高并发秒杀Api-业务分析与DAO层构建3

章节目录 mybatis与spring整合过程 spring-dao.xml 配置 junit4 单元测试 1.mybatis与spring整合过程 1.1 spring-dao.xml 配置 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!--配置整合mybatis--> <!--1:配置数据库相关参数--> <!-- 1:配置数据库相关参数properties的属性:${url} --> <context:property-placeholder location="classpath:jdbc.properties"/> <!--2.数据库连接池配置--> <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"> <!-- 配置连接池属性--> <property name="driverClass" value="${driver}" /> <property name="jdbcUrl" value="${url}" /> <property name="user" value="${username}" /> <property name="password" value="${password}" /> <!-- c3p0连接池的私有属性--> <property name="maxPoolSize" value="30" /> <property name="minPoolSize" value="10" /> <!-- 关闭连接后不自动commit--> <property name="autoCommitOnClose" value="false" /> <!-- 获取连接超时时间 30个连接用完--> <property name="checkoutTimeout" value="1000" /> <!-- 当获取连接失败重试次数--> <property name="acquireRetryAttempts" value="2" /> </bean> <!-- 约定大于配置--> <!-- 3:配置SqlSessionFactory对象--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!-- 注入数据库连接池--> <property name="dataSource" ref="dataSource" /> <!-- 配置MyBatis全局配置文件:mybatis-config.xml--> <property name="configLocation" value="classpath:mybatis-config.xml" /> <!-- 扫描entity包 使用别名--> <property name="typeAliasesPackage" value="org.seckill.domain" /> <!-- 扫描sql配置文件;mapper需要的xml文件--> <property name="mapperLocations" value="classpath:mapper/*.xml" /> </bean> <!--配置扫描Dao接口包,动态实现Dao接口 mapper 代理,并注入到Spring容器中 MapperScannerConfigurer--> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <!--注入sqlSessionFactory,懒加载,用到的时候才加载--> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property> <!--给出扫描DAO接口包--> <property name="basePackage" value="org.seckill.dao"></property> </bean> </beans> 2.junit 单元测试 SecKillDaoTest.java - 测试秒杀 package org.seckill.dao; import org.junit.Test; import org.junit.runner.RunWith; import org.seckill.domain.SecKill; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.Date; import java.util.List; /** * 配置spring 与 junit 启动时加载Spring IOC容器 * spring-test,junit整合 * 告诉junit Spring配置文件位置 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration({"classpath:spring/spring-dao.xml"}) public class SecKillDaoTest { //注入Dao类实现 @Autowired private SecKillDao secKillDao; @Test public void queryById() throws Exception { long id = 1000; SecKill secKill = secKillDao.queryById(id); System.out.println(secKill.getName()); System.out.println(secKill); /** * 1000元秒杀iphone x SecKill{seckillId=1000, name='1000元秒杀iphone x', stock=100, startTime=Fri May 04 00:00:00 CST 2018, endTime=Sat May 05 00:00:00 CST 2018, createTime=Sat May 05 00:05:03 CST 2018 } */ } /** * @throws Exception */ @Test public void queryAll() throws Exception { // queryAll(offset,limit)->queryAll(arg0,arg1) List<SecKill> secKillList = secKillDao.queryAll(0, 100); for (SecKill secKill : secKillList) { System.out.println(secKill); } /** * SecKill{seckillId=1000, name='1000元秒杀iphone x', stock=100, startTime=Fri May 04 00:00:00 CST 2018, endTime=Sat May 05 00:00:00 CST 2018, createTime=Sat May 05 00:05:03 CST 2018} SecKill{seckillId=1001, name='500元秒杀ipad x', stock=200, startTime=Fri May 04 00:00:00 CST 2018, endTime=Sat May 05 00:00:00 CST 2018, createTime=Sat May 05 00:05:03 CST 2018} SecKill{seckillId=1002, name='300元秒杀小米4', stock=300, startTime=Fri May 04 00:00:00 CST 2018, endTime=Sat May 05 00:00:00 CST 2018, createTime=Sat May 05 00:05:03 CST 2018} SecKill{seckillId=1003, name='200元秒杀小米note', stock=400, startTime=Fri May 04 00:00:00 CST 2018, endTime=Sat May 05 00:00:00 CST 2018, createTime=Sat May 05 00:05:03 CST 2018} */ } @Test public void reduceStock() throws Exception { Date killTime = new Date(); int updateCount = secKillDao.reduceStock(1000L, killTime); System.out.println(updateCount); } } 测试秒杀明细 package org.seckill.dao; import org.junit.Test; import org.junit.runner.RunWith; import org.seckill.domain.SuccessKilled; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:spring/spring-dao.xml") public class SuccessKilledDaoTest { @Autowired private SuccessKilledDao successKilledDao; @Test public void insertSuccessKilled() throws Exception { int successCount = successKilledDao.insertSuccessKilled(1001L, "15300815999"); System.out.println("insertCount=" + successCount); } @Test public void queryByIdWithSecKill() throws Exception { //1个用户只能查询到属于自己的一个秒杀明细 long id = 1001L; String phone = "15300815999"; SuccessKilled successKilled = successKilledDao.queryByIdWithSecKill(id, phone); System.out.println(successKilled); } } 接下来是service 层的构建

优秀的个人博客,低调大师

Java高并发秒杀Api-业务分析与DAO层构建1

章节目录 1.为什么使用Spring+Spring MVC+Mybatis 2.秒杀业务特性 3.秒杀分析过程、优化思路 4.相关技术介绍 5.基于Maven创建项目 6.秒杀业务分析 7.秒杀事务的难点分析 8.实现秒杀的哪些功能 1.为什么使用Spring+Spring MVC+Mybatis 框架易于使用、轻量级 对业务代码侵入性低 成熟的社区与资料 2.秒杀业务特性 秒杀业务场景具有典型的"事务"特性 秒杀、红包类需求越来越常见,对竞争资源的访问 面试常问的问题 3.相关技术介绍 MySQL 表设计 SQL技巧 事务、行级锁 MyBatis DAO层设计与开发 MyBatis 合理使用 MyBatis 与 Spring整合 Spring Spring Ioc 整合Service Spring 声明式事务使用 Spring MVC Restful 接口设计与使用 框架运作流程 Controller 设计技巧 前端 交互设计 BootStrap Jquery 高并发 高并发点和高并发分析 优化思路并实现 4.基于Maven创建项目 1.maven命令创建web项目骨架 mvn archetype:generate -DgroupId=org.seckill -DartifactId=seckill - DarchetypeArtifactId=maven-archetype-webapp 5.秒杀业务分析 如下图所示: 秒杀业务系统流程分析 所以秒杀业务的核心是对库存的处理。 用户针对库存处理的业务分析用户的秒杀过程 需要减库存->记录购买明细->组成完整事务->数据持久化 如下图所示: 用户秒杀过程 用户购买行为 记录谁购买成功了->成功的时间及有效期->付款、发货信息 为什么需要事务 事务不完整导致的数据一致性问题 如上图所示 1.减了库存,但是没有用户的购买明细,那么就会出现50个商品,但是购买明 细小于50个,到时候发货会发现有些许商品滞留在仓库中,此种情况属于少卖 的情况。 2.记录了明细但是没有减库存,就会发现订单量比商品量要多,出现超卖的情 况,还有一种情况也会导致超卖,多个用户并发修改库存,加入库存量为1,这 个时候多个用户同时去减库存(经过库存量>0的验证),会导致库存为负数, 多个用户抢到同一个商品,这种情况下也会导致超卖发生。 数据落地方案 MySQL VS NoSQL NoSQL非关系型数据库在事务的支持上并没有关系型数据库可靠。 所以归根结底事务机制依然是目前最可靠的落地方案。 6.秒杀事务难点分析 6.1 难点问题-竞争 竞争问题-并发冲突修改 解决方案是采用数据库innodb引擎提供的 事务及 行级锁。 用户减库存的事务流程: begin; update 库存数量; insert 购买明细; commit; 行级锁 如下如所示: 行级锁 情景分析: 在事务执行过程中,mysql默认的repeateable read 隔离级别会在写操作发生的 行上加上行级锁(非记录加锁,而是在对应索引上加锁,上图中的update加锁 发生在id上),多个写请求并发更新同一行记录会产生如下问题: 因为行级锁在事务结束之后才能释放锁,可能会导致锁等待的发生。数据库吞吐率(事务处理能力)会降低。 所以秒杀的难点是什么?秒杀的难点是如何高效的处理竞争-如何在保证数据一致性的情况下高效的处理竞争 8.实现秒杀的哪些功能 1.秒杀接口暴露 浏览器插件获取秒杀接口,通过脚本去秒杀,保护秒杀接口的一种手段。 2.执行秒杀的操作 执行秒杀的业务逻辑 3.秒杀查询,商品详情页查询。 其实市面上最主要的几个秒杀思路是: 1.直接在数据库层面做秒杀 2.缓存中存储库存,用户秒杀请求是将缓存中库存与数据库中库存数据同时进行减库存的过程,保证数据一致性是一个难点。 3.缓存中存储有效的减库存操作,队列减库存的的请求依次顺序执行数据库减库存、生成订单明细事务(操作),如小米。

优秀的个人博客,低调大师

网站域名查询-域名资产管理API-域名Whois查询API接口介绍

# 前言 WHOIS 域名查询是指企业开展品牌风控、站点核验、域名资产运维工作时,如需掌握域名权属与生命周期信息,可将目标域名提交接口查询,获取域名注册商、创建时间、更新时间、到期时间、域名状态、DNS 服务器等完整 WHOIS 档案,接口同时支持一级域名与二级域名直接检索。 ![](https://developer.qcloudimg.com/http-save/yehe-12007261/c9d3650035e7486e4d2d7d6072255eda.png) # 特性 兼容**WHOIS、RDAP 两套查询协议**,各类域名信息统一整理;**多通道稳定查询**,避免被限制拦截;统一规整域名数据、标注**隐私保护状态**,无需维护双协议逻辑和复杂文本解析。 # API介绍 ## 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |website|String|是|网站地址| ## 返回样例 ``` { "code": 200, // 返回码,详见返回码说明 "msg": "成功", // 返回码对应描述 "taskNo": "65605503936940344488", // 本次请求号 "data": { "registrationTime": "2021-02-07 13:04:11",//注册时间 "country": "",//国家代码 "registrar": "Alibaba Cloud Computing (Beijing) Co.,Ltd.",//注册商 "city": "",//城市 "registrarEmail": "DomainAbuse@service.aliyun.com",//注册商邮箱 "domainStatus": "ok",//域名状态 "whoisServer": "grs-whois.hichina.com",//whois服务器 "updatedDate": "2023-12-25 01:48:34",//更新时间 "eMail": "",//联系人邮箱(有隐私保护) "province": "",//省份 "phone": "",//联系人电话(有隐私保护) "expirationTime": "2027-02-07 13:04:11",//到期时间 "domain": "jumdata.com",//域名 "nameServer": "dns7.hichina.com,dns8.hichina.com",//DNS服务器 "company": "",//公司 "openProtect": 0,//开启隐私保护(0未开,1开启) "registrarPhone": "xxxx",//注册商电话 "contacts": "" //联系人(有隐私保护) } } ```

优秀的个人博客,低调大师

技术开发记录:创客API-声音克隆开发API接口接入

# 声音克隆 API V3 接入教程 > 接口地址:`https://api.hihookeji.com/api/tts/clonevoicev3` > 返回格式:`application/json` > 请求方式:`HTTP POST` --- ## 一、接口概述 本接口根据一段 **5~14 秒** 的参考音频(建议 MP3)与指定文案,克隆音色并合成目标文本语音。 | 项目 | 说明 | |------|------| | 接口地址 | `https://api.hihookeji.com/api/tts/clonevoicev3` | | 认证方式 | 请求体字段 `key`(你的 API 密钥,与业务参数一并提交) | | 请求示例 | `https://api.hihookeji.com/api/tts/clonevoicev3` | | Content-Type | `application/json`(推荐)或 `application/x-www-form-urlencoded` | | 业务判断 | 统一用响应字段 **`code`**:`200` 成功 | | HTTP 状态码 | 请以响应体 `code` 为准 | --- ## 二、请求参数 | 参数名 | 类型 | 必填 | 描述 | 示例 | |--------|------|------|------|------| | key | string | 是 | API 密钥,放在请求体中与其它参数一起提交 | `你的密钥` | | source_audio_url | string | 是 | 参考音频公网 URL,须为标准 **mp3 / wav**,且 URL **以 `.mp3` 或 `.wav` 结尾**;时长 **5~14 秒**;音频中读的文案必须为下方指定文案 | `https://a.b.c/1.mp3` | | target_text | string | 是 | 要合成的目标文本,单次不超过 **2000** 字;超过 **1000** 字建议用异步(type=1) | `我是克隆的文字` | | notify_url | string | 是 | 合成结果通知地址(异步回调接收地址,需公网可达) | 见下方「回调测试」 | | ref_text | string | 是 | 参考音频文案,必须为下方两种之一 | 见下方说明 | | speed | float | 否 | 语速,默认 `1.0` | `1` | | pitch | float | 否 | 语调,默认 `1.0` | `1` | | type | int | 否 | 结果返回类型,默认异步:`1` 异步文件回调;`2` 同步返回 Base64 音频 | `1` | | mtype | int | 否 | `1` 单人;`2` 多角色 | `1` | **回调测试**:联调异步回调时,建议先打开 [https://webhook.site/](https://webhook.site/) 获取自己的唯一 URL,将该地址填入 `notify_url`。提交任务后可在网页上直接查看服务端推送的表单字段与 `target_file` 文件,无需先部署自己的回调服务。 --- ## 三、参考音频文案(必须严格一致) `source_audio_url` 对应音频中念出的内容,以及参数 `ref_text`,**必须**与下列文案完全一致,否则克隆会失败: 1. 中文:`我的声音将用于平台克隆,并合法使用,为自己的行为负责` 2. 英文:`My voice will be used for platform cloning , and I take responsibility for my actions` --- ## 四、文本标签能力 可在 `target_text` 中使用下列标签。 ### 4.1 停顿标签 支持秒(s)与毫秒(ms),单次停顿 **最大不超过 10 秒**,否则失败。 ```text 如果每天都是全新的一天。 表示停顿50毫秒 ``` ### 4.2 多音字 / 发音矫正 ```text 每天早起照镜子都崩溃。 多音字:重要处理。 ``` > `ph` 中音标与声调之间有空格,如 `wan 3`、`gan 4`、`chong 3`。 ### 4.3 读法标签(say-as) | interpret-as | 含义 | |--------------|------| | number | 数字读法 | | time | 时间读法 | | date | 日期读法 | | value | 数值读法 | | telephone | 手机号 / 电话读法 | ### 4.4 完整示例 ```text 金额123436511.562254, 数值是-1204455, 停顿5秒, 多音字:重要处理。 现在的时间是:14:05, Today is 25-02-01. 我的电话: (888) 555-1212 ``` --- ## 五、返回结果说明 ### 5.1 异步提交成功(type=1) 立即返回任务受理结果;`data.audio_base64` 为 `null`,音频通过 `notify_url` 回调。 | 字段 | 说明 | |------|------| | code | 业务状态码,`200` 成功 | | msg | 说明信息 | | data.taskid | 任务 ID,回调关联用 | | data.consume | 消耗点数 | | data.audio_base64 | 异步为 `null` | | exec_time | 接口耗时(秒) | | ip | 请求方 IP | ```json { "code": 200, "msg": "success", "data": { "taskid": "1784017039696108", "consume": 20, "audio_base64": null }, "exec_time": 0.097585, "ip": "14.19.93.116" } ``` ### 5.2 同步返回(type=2) 同步成功时 `data.audio_base64` 为音频 Base64 字符串。**必须先 Base64 解码再保存为文件。** ```json { "code": 200, "msg": "success", "data": { "taskid": "1784017039913816", "consume": 20, "audio_base64": "SUQzBAAAAAALFVRTo......=" }, "exec_time": 0.936593, "ip": "14.19.93.116" } ``` ### 5.3 异步回调 Notify(type=1) 服务端合成完成后,以 **表单 POST** 向 `notify_url` 推送(成功带文件时为 `multipart/form-data`,失败多为 `application/x-www-form-urlencoded`),**不是** JSON Body。 | 字段 | 说明 | |------|------| | errcode | **`0` 成功**;**`1001` 失败**(与接口提交响应的 `code: 200` 不同,回调请按此处判断) | | taskid | 任务 ID | | msg | 说明文案 | | target_file | 音频文件(mp3/wav),PHP 可用 `$_FILES['target_file']` 接收;失败时无此字段 | --- ## 六、接入流程建议 1. 准备合规参考音频(5~14 秒,MP3),念上述指定文案之一,公网可访问 URL。 2. 申请 API Key,作为请求体字段 `key` 与其它参数一起提交。 3. 字数 ≤1000 且需立即拿结果:可用 `type=2` 同步;字数较多或需稳定投递:用 `type=1` 异步。联调回调时可先用 [webhook.site](https://webhook.site/) 获取唯一接收地址填入 `notify_url`。 4. 异步场景下用 `data.taskid` 关联提交与回调结果。 5. 同步场景对 `data.audio_base64` 解码后落盘。 --- ## 七、Python 完整代码 依赖:`requests`(`pip install requests`) ```python # -*- coding: utf-8 -*- """ 声音克隆 API V3 - Python 完整示例 接口: https://api.hihookeji.com/api/tts/clonevoicev3 """ import os import base64 import json from datetime import datetime import requests API_URL = "https://api.hihookeji.com/api/tts/clonevoicev3" API_KEY = "你的密钥" # 替换为真实密钥 # 参考音频文案(必须与音频内容一致,二选一) REF_TEXT_CN = "我的声音将用于平台克隆,并合法使用,为自己的行为负责" REF_TEXT_EN = ( "My voice will be used for platform cloning , " "and I take responsibility for my actions" ) def clone_voice( source_audio_url, target_text, notify_url, ref_text=REF_TEXT_CN, speed=1.0, pitch=1.0, result_type=1, mtype=1, timeout=120, ): """ 调用声音克隆 V3 接口。 :param source_audio_url: 参考音频公网 URL(mp3,5~14 秒) :param target_text: 目标合成文本(≤2000 字) :param notify_url: 异步回调地址(type=1 必填且需可公网访问) :param ref_text: 参考音频文案(必须为指定文案) :param speed: 语速,默认 1.0 :param pitch: 语调,默认 1.0 :param result_type: 1=异步回调,2=同步 Base64 :param mtype: 1=单人,2=多角色 :param timeout: HTTP 超时秒数(同步建议调大) :return: 接口 JSON 字典 """ url = API_URL payload = { "key": API_KEY, "source_audio_url": source_audio_url, "target_text": target_text, "notify_url": notify_url, "ref_text": ref_text, "speed": speed, "pitch": pitch, "type": result_type, "mtype": mtype, } resp = requests.post( url, json=payload, headers={"Content-Type": "application/json"}, timeout=timeout, ) resp.raise_for_status() return resp.json() def save_base64_audio(result, save_dir="./output"): """ 将 type=2 同步返回的 Base64 音频解码并保存为 mp3。 """ if int(result.get("code") or 0) != 200: raise RuntimeError(f"合成失败: {result.get('msg')}") data = result.get("data") or {} audio_b64 = data.get("audio_base64") if not audio_b64: raise RuntimeError("响应中缺少 data.audio_base64") os.makedirs(save_dir, exist_ok=True) audio_bytes = base64.b64decode(audio_b64) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") taskid = data.get("taskid") or timestamp save_path = os.path.join(save_dir, f"{taskid}.mp3") with open(save_path, "wb") as f: f.write(audio_bytes) return save_path # --------------------------------------------------------------------------- # Flask 异步回调接收示例(可选,部署到公网后作为 notify_url) # 安装: pip install flask # --------------------------------------------------------------------------- def create_notify_app(save_dir="./callback_audio"): from flask import Flask, request, jsonify app = Flask(__name__) os.makedirs(save_dir, exist_ok=True) @app.route("/Notify", methods=["POST"]) def notify(): # 回调为表单字段(有文件时是 multipart),不是 JSON Body taskid = request.form.get("taskid") msg = request.form.get("msg") errcode = request.form.get("errcode") # 成功 0,失败 1001 if "target_file" in request.files: f = request.files["target_file"] if f and f.filename: ext = os.path.splitext(f.filename)[1] or ".mp3" path = os.path.join(save_dir, f"{taskid}{ext}") f.save(path) return jsonify({"ok": True, "taskid": taskid, "saved": path, "errcode": errcode}) return jsonify({"ok": False, "taskid": taskid, "msg": msg, "errcode": errcode}) return app if __name__ == "__main__": # ---------- 示例 1:异步提交(type=1)---------- async_result = clone_voice( source_audio_url="https://a.b.c/1.mp3", target_text='你好,这是克隆语音。今天天气不错。', notify_url="https://webhook.site/你的唯一ID", ref_text=REF_TEXT_CN, result_type=1, mtype=1, ) print("异步提交:", json.dumps(async_result, ensure_ascii=False, indent=2)) # 成功时 code==200,记录 data.taskid;回调可在 webhook.site 页面查看 # ---------- 示例 2:同步返回 Base64(type=2)---------- sync_result = clone_voice( source_audio_url="https://a.b.c/1.mp3", target_text="我是克隆的文字", notify_url="https://webhook.site/你的唯一ID", # 同步模式仍按文档传 notify_url ref_text=REF_TEXT_CN, result_type=2, timeout=180, ) print("同步响应 code:", sync_result.get("code"), "type:", type(sync_result.get("code")), "msg:", sync_result.get("msg")) print("data keys:", list((sync_result.get("data") or {}).keys())) print("audio_base64 是否有值:", bool((sync_result.get("data") or {}).get("audio_base64"))) if int(sync_result.get("code") or 0) == 200: try: path = save_base64_audio(sync_result) print("音频已保存:", path) except Exception as e: print("保存失败:", repr(e)) print("完整 data:", json.dumps(sync_result.get("data"), ensure_ascii=False)[:500]) # ---------- 启动回调服务(需要时取消注释)---------- # app = create_notify_app() # app.run(host="0.0.0.0", port=8080) ``` --- ## 八、PHP 完整代码 需开启 `curl` 扩展。回调示例可直接作为独立 `notify.php` 部署。 ```php API_KEY, 'source_audio_url' => $params['source_audio_url'], 'target_text' => $params['target_text'], 'notify_url' => $params['notify_url'], 'ref_text' => isset($params['ref_text']) ? $params['ref_text'] : REF_TEXT_CN, 'speed' => isset($params['speed']) ? $params['speed'] : 1.0, 'pitch' => isset($params['pitch']) ? $params['pitch'] : 1.0, 'type' => isset($params['type']) ? (int)$params['type'] : 1, 'mtype' => isset($params['mtype']) ? (int)$params['mtype'] : 1, ]; $ch = curl_init($url); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE), CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => $timeout, CURLOPT_SSL_VERIFYPEER => true, ]); $body = curl_exec($ch); if ($body === false) { $err = curl_error($ch); curl_close($ch); throw new Exception('请求失败: ' . $err); } $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $result = json_decode($body, true); if (!is_array($result)) { throw new Exception("响应非 JSON,HTTP={$httpCode}, body={$body}"); } return $result; } /** * 保存 type=2 返回的 Base64 音频 * * @param array $result 接口响应 * @param string $saveDir 目录 * @return string 文件路径 * @throws Exception */ function saveBase64Audio(array $result, $saveDir = './output') { if (!isset($result['code']) || (int)$result['code'] !== 200) { $msg = isset($result['msg']) ? $result['msg'] : 'unknown'; throw new Exception('合成失败: ' . $msg); } if (empty($result['data']['audio_base64'])) { throw new Exception('响应中缺少 data.audio_base64'); } if (!is_dir($saveDir) && !mkdir($saveDir, 0755, true)) { throw new Exception('无法创建目录: ' . $saveDir); } $bytes = base64_decode($result['data']['audio_base64'], true); if ($bytes === false) { throw new Exception('Base64 解码失败'); } $name = !empty($result['data']['taskid']) ? $result['data']['taskid'] : date('Ymd_His'); $path = rtrim($saveDir, '/\\') . DIRECTORY_SEPARATOR . $name . '.mp3'; if (file_put_contents($path, $bytes) === false) { throw new Exception('写入文件失败: ' . $path); } return $path; } // ======================== 调用示例 ======================== try { // 示例 1:异步提交 type=1 $asyncResult = cloneVoice([ 'source_audio_url' => 'https://a.b.c/1.mp3', 'target_text' => '你好,这是克隆语音。今天天气不错。', 'notify_url' => 'https://webhook.site/你的唯一ID', 'ref_text' => REF_TEXT_CN, 'type' => 1, 'mtype' => 1, ]); echo "异步提交:\n" . json_encode($asyncResult, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n"; // 示例 2:同步 Base64 type=2 $syncResult = cloneVoice([ 'source_audio_url' => 'https://a.b.c/1.mp3', 'target_text' => '我是克隆的文字', 'notify_url' => 'https://webhook.site/你的唯一ID', 'ref_text' => REF_TEXT_CN, 'type' => 2, 'mtype' => 1, ], 180); echo "同步 code: " . $syncResult['code'] . ", msg: " . $syncResult['msg'] . "\n"; if ((int)$syncResult['code'] === 200) { $path = saveBase64Audio($syncResult); echo "音频已保存: {$path}\n"; } } catch (Exception $e) { echo '错误: ' . $e->getMessage() . "\n"; } /* ======================== notify.php 异步回调接收 ======================== 将下方代码单独保存为 notify.php,部署到公网,地址填入 notify_url。 true, 'taskid' => $taskid, 'saved' => $path, 'errcode' => $errcode], JSON_UNESCAPED_UNICODE); exit; } echo json_encode([ 'ok' => false, 'taskid' => $taskid, 'msg' => $msg, 'errcode' => $errcode, ], JSON_UNESCAPED_UNICODE); */ ``` --- ## 九、Java 完整代码 依赖:JDK 8+,使用内置 `HttpURLConnection`,无需第三方库。 若使用 Maven,也可自行替换为 OkHttp / Apache HttpClient。 ```java import java.io.*; import java.net.HttpURLConnection; import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Base64; /** * 声音克隆 API V3 - Java 完整示例 * 接口: https://api.hihookeji.com/api/tts/clonevoicev3 * * 说明:为减少依赖,本示例用字符串拼 JSON,并用简单方式解析 code / audio_base64。 * 生产环境建议改用 Jackson / Gson 等库。 */ public class CloneVoiceV3Demo { private static final String API_URL = "https://api.hihookeji.com/api/tts/clonevoicev3"; private static final String API_KEY = "你的密钥"; // 替换为真实密钥 public static final String REF_TEXT_CN = "我的声音将用于平台克隆,并合法使用,为自己的行为负责"; public static final String REF_TEXT_EN = "My voice will be used for platform cloning , and I take responsibility for my actions"; public static class CloneRequest { public String sourceAudioUrl; public String targetText; public String notifyUrl; public String refText = REF_TEXT_CN; public double speed = 1.0; public double pitch = 1.0; public int type = 1; // 1 异步,2 同步 Base64 public int mtype = 1; // 1 单人,2 多角色 } /** * 调用声音克隆 V3 */ public static String cloneVoice(CloneRequest req, int timeoutMs) throws IOException { String urlStr = API_URL; URL url = new URL(urlStr); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); conn.setConnectTimeout(15000); conn.setReadTimeout(timeoutMs); conn.setDoOutput(true); conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); conn.setRequestProperty("Accept", "application/json"); String json = toJson(req); byte[] body = json.getBytes(StandardCharsets.UTF_8); conn.setRequestProperty("Content-Length", String.valueOf(body.length)); try (OutputStream os = conn.getOutputStream()) { os.write(body); } int code = conn.getResponseCode(); InputStream is = (code >= 200 && code = json.length()) { return null; } if (json.charAt(start) == '"') { int end = start + 1; StringBuilder sb = new StringBuilder(); while (end 今天天气不错。"; asyncReq.notifyUrl = "https://webhook.site/你的唯一ID"; asyncReq.refText = REF_TEXT_CN; asyncReq.type = 1; asyncReq.mtype = 1; String asyncResp = cloneVoice(asyncReq, 60000); System.out.println("异步提交: " + asyncResp); // 示例 2:同步 type=2 CloneRequest syncReq = new CloneRequest(); syncReq.sourceAudioUrl = "https://a.b.c/1.mp3"; syncReq.targetText = "我是克隆的文字"; syncReq.notifyUrl = "https://webhook.site/你的唯一ID"; syncReq.refText = REF_TEXT_CN; syncReq.type = 2; syncReq.mtype = 1; String syncResp = cloneVoice(syncReq, 180000); System.out.println("同步响应: " + syncResp); if (extractInt(syncResp, "code") == 200) { Path path = saveBase64Audio(syncResp, "./output"); System.out.println("音频已保存: " + path.toAbsolutePath()); } } } ``` --- ## 十、常见问题 | 问题 | 处理建议 | |------|----------| | 克隆效果异常 / 失败 | 检查参考音频是否念指定文案,且 `ref_text` 与音频内容完全一致 | | 参考音频格式不对 | `source_audio_url` 必须是标准 **mp3** 或 **wav**;提示失败时可先用 ffmpeg 转码后再上传,例如:`ffmpeg -i 输入文件 -acodec libmp3lame -ar 44100 输出.mp3` | | 参考音频链接后缀不对 | URL 必须以 **`.mp3` 或 `.wav` 结尾**;常见 OSS 带鉴权参数的长链接(不以 `.mp3`/`.wav` 结尾)会失败,需换成可访问且后缀正确的直链,或转存后再提交 | | 参考音频时长 | 必须在 5~14 秒;URL 需公网可访问 | | 长文本 | 超过 1000 字建议 `type=1` 异步;单次最多 2000 字 | | 停顿过长 | `` 单次最大 10 秒 | | 只看 HTTP 200 | 不够,必须判断响应体 `code == 200` | | 同步音频无法播放 | 对 `data.audio_base64` **解码后再写文件**,不要直接把 Base64 字符串当二进制保存 | | 异步收不到回调 | 确认 `notify_url` 公网可达、支持 POST;联调可先用 [webhook.site](https://webhook.site/) 唯一地址验证是否已推送;正式环境再换成自己的接收服务 | | 回调当成 JSON 解析失败 | 回调是表单 POST,用 form/`$_POST` 取值,不要按 JSON Body 解析 | | 回调成功却判失败 | 回调成功 `errcode` 为 **`0`**(不是提交接口的 `code: 200`),失败为 `1001` | | 旧接口标签无效 | 请改用本教程新地址 `.../clonevoicev3` | --- ## 十一、快速对照 | 模式 | type | 立即响应 | 音频获取方式 | |------|------|----------|--------------| | 异步 | 1(默认) | `code:200`,`data.taskid`,`audio_base64: null` | `notify_url` 回调 multipart 文件 `target_file` | | 同步 | 2 | `code:200`,`data.audio_base64` 有值 | 本地 Base64 解码落盘 |

优秀的个人博客,低调大师

滑块登陆验证API-滑块验证接口-手机滑块验证-文字点选-图片点选验证

# 前言 滑块验证码是一种“行为式验证码”,它要求用户通过拖拽滑块到指定位置来完成验证,其核心目的是区分操作者是真人还是自动化程序(机器人)。 滑块验证码凭借其高安全性和对用户体验的友好性,已广泛应用于各类需要防范机器恶意攻击的场景: 1. 账号登录与注册防护 防暴力破解与撞库: 在用户登录时,滑块验证能有效阻止黑客利用自动化工具高频尝试密码或进行撞库攻击。 防批量注册: 在注册环节,通过拦截机器脚本,防止黑产批量生成虚假账号(如“水军”或“羊毛党”),保障平台的真实用户生态。 1. 核心业务接口与表单防护 支付与交易验证: 在电商下单、支付确认或账户信息修改等高风险操作中,增加滑块验证作为安全屏障,防止恶意程序篡改订单或盗刷资金。 防恶意刷单与爬虫: 在抢票、抢购秒杀或评论发帖场景中,滑块验证能有效拦截自动化抢单脚本和恶意爬虫,保护业务数据不被滥用,维护公平的交易环境。 1. 营销活动与内容社区防刷 活动防刷: 在抽奖、优惠券领取、投票或问卷调查中,防止机器批量刷取奖励或操纵投票结果,保障营销预算精准触达真实用户。 防垃圾内容: 在论坛发帖、文章评论或留言板上,有效阻断垃圾广告机器人和恶意引流脚本的自动发布。 1. 账号安全风控 密码多次输错、异地登录、风险访问时弹出滑块验证,加强账号安全。 ![](https://developer.qcloudimg.com/http-save/yehe-12007261/bd443f6e1ddd63a04fd69e4456d67535.png) # API介绍 本验证码接口接入包括**客户端(浏览器)接入**和**服务端接入**,支持**滑动拼图**、**文字点选**和**图形点选**三种验证方式: 1. **滑动拼图:** 轻轻一滑即刻完成验证,适用于追求用户体验的业务场景 1. **文字点选:** 顺序点击图中文字即可完成验证,提高机器识别难度的同时,保证真实用户可读 1. **图形点选:** 依次点击图中图形,对抗空间大,安全系数极高 ## 客户端接口 1. 客户端完成滑块动作,滑块正确则返回ticket; 1. 客户端完成验证之后,再用客户端返回的ticket,调用服务端ticket接口完成验证。 Web 页面需引入验证码的js,在业务需要验证时,唤起验证码进行验证。 ``` ``` ### 服务端接入 客户端验证成功之后,需要由接入者的服务端再调用服务商的服务端接口完成验证,本接口即完成该功能 #### 请求参数 |名称|类型|必须|说明| | --- | --- | --- | --- | |captchaAppId|String|是|验证码 captchaAppId ,和客户端一致。联系服务商申请获取| |ticket|String|是|验证码返回给用户的票据| |randstr|String|是|验证票据需要的随机字符串| |captchaSign|String|是|验证码签名,详见验证码签名说明| |ip|String|否|用户操作来源的外网IP| #### 验证成功返回样例 ``` { "code": 200,//返回码,详见返回码说明 "msg": "成功",//code对应的描述 "taskNo": "987272522132093927999832" //code对应的描述 } ``` ```

优秀的个人博客,低调大师

图片相似度检索-以图搜图-相似图片搜索-图片去重API-图库检索

# 前言 图片相似度检索,可在图库中找到与检索图片语义相似的图片集合,并给出相似度打分,综合参考图片类型、颜色、内容、布局等特征,适用于各种网络图片、实拍图等场景。 图片相似度检索可广泛应用在设计素材、美术作品、UGC内容管理等场景,比如: **设计素材与美术作品**:在海量素材库中快速查找风格、内容相似的图片,提升素材查找与推荐效率。 **内容平台与UGC管理**:对用户上传的内容进行相似图片检测,辅助完成内容去重、聚合与版权保护。 **电商与商品图片管理**:检索相似商品图或货源图片,辅助商品同款识别、图片去重与图库治理。 ![](https://developer.qcloudimg.com/http-save/yehe-12007261/f38df211fdd8d6ee00de80c0ab52d24d.png) # API介绍 ## 图片入库 ### 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |url|String|否|入库图片的URL| |base64|String|否|入库图片的base64| |description|String|否|入库图片的描述,不能超过64字节| ### 返回样例 ``` { "code": 200, // 返回码,详见返回码说明 "msg": "成功", // 返回码对应描述 "taskNo": "043439882226367117195632", // 本次请求号 "data": { "contSign": "MzA0OTUzNjk1MCwyMjUzNTU3Mjc" // 入库后的图片签名 } } ``` ## 图片检索 ### 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |url|String|否|检索图片的URL| |base64|String|否|检索图片的base64| |start|String|否|分页功能,起始位置。接口返回数量最大限制1000条,例如:起始位置为800,截取条数500条,接口也只返回第800-1000条的结果,共计200条。默认返回0-100| |end|String|否|分页功能,截取条数,例:250| ### 返回样例 ``` { "code": 200, // 返回码,详见返回码说明 "msg": "成功", // 返回码对应描述 "taskNo": "043439882226367117195632", // 本次请求号 "data": { "result": [ { "brief": "{\"describe\":\"测试1\", \"taskNo\":\"123\"}", // 入库时添加的描述信息 "score": 1, // 图片相关性,取值范围0-1,越接近1代表越相似 "contSign": "MzA0MzM0ODQ1NSwxNjIwMzA3MTk" // 图片签名信息,可以用来删除图片或定位问题 } ], "resultNum": "8", // 检索结果数 "hasMore": "false" // 是否还有下一页,返回值:true、false;如果不分页,不用关注该字段 } } ``` ## 删除图片 ### 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |contSign|String|是|图片签名信息,多个以逗号隔开,例:MzA0MzM0ODQ1NSwxNjIwMzA3JLk3,NDAzMDk3MFP0NCwxOTU0MDY0OTA1| ### 返回样例 ``` { "msg": "成功", // 返回码对应描述 "code": 200, // 返回码,详见返回码说明 "taskNo": "991824559141136645118228", // 本次请求号 "data": {} } ```

优秀的个人博客,低调大师

智能视觉人流量统计API-多人客流监测分析-全域人数统计接口功能介绍

# 前言 依托高精度人体视觉识别技术,可对监控画面进行实时人体检测与精准人流量统计,支持多人重叠、人体遮挡、高低俯拍、多角度人脸、大幅动作等复杂实景场景。系统可实时输出画面内人体数量、人员坐标位置等数据,有效解决传统人流量统计漏检、误检、抗干扰差的问题,具备强适配性与高稳定性,可全天候自动完成人员进出、区域驻留人数统计,为各场景客流监管、数据分析、智能运维提供精准的数据支撑。 在下面的这些场景中,使用接口简化人力行为,自动识别流量。 1. 商业商场、门店客流统计与运营分析 1. 园区、写字楼楼宇人员进出管控 1. 校园、场馆人员容量监测管理 1. 地铁、车站交通枢纽客流监测 1. 景区、展馆文旅场所客流管控 1. 城市公共区域安全客流监测预警 ![](https://developer.qcloudimg.com/http-save/yehe-12007261/eeb3ae5a04e9cd0f2233fcc74b01e4d0.png) # 静态版API介绍 ## 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |image|String|否|图片base64| |url|String|否|图片地址| |area|String|否|特定区域坐标选择,支持多个多边形区域。如输入超过10个区域,截取前10个区域进行识别。例如:x1,y1,x2,y2,x3,y3…xn,yn同一个区域内的坐标使用英文逗号“,”分隔,默认尾点和首点相连做闭合,例:”1,1,620,1,620,620,1,620”多个区域使用英文分号“;”分隔。例如:1,1,1,720,100,720…100,1;200,1,200,720…300,720,300,1;…注意:area区域坐标从(1,1)开始,坐标大于或等于原图大小,以及x、y坐标未成对出现都是错误的注意:设置了多个区域时,任意一个坐标设置错误,则认为area参数错误、失效。注意:若此参数为空或无此参数、或area参数设置错误时,默认识别整张图片中的人数。| |show|String|否|是否输出渲染的图片,默认false不返回,选true时返回渲染后的图片(base64)。若此字段为无效值或为空则默认false| ## 返回样例 ``` { "code": 200, // 返回码,详见返回码说明 "msg": "成功", // 返回码对应描述 "taskNo": "65605503936940344488", // 本次请求号 "charge": false, // 计费标志 "data": { "areaCounts": [ //每一个区域框中人数,仅当area参数有效时才会返回,否则该字段不返回。 1, 5, 8 ], "personNum":"1", //识别出的人体数目;若多个区域之间有重合,则是所有区域的并集人体数目。 "image":"/aj/4MHo...FS2P/9=" //渲染后的图片base64字符串,不包含图片头 } } ``` # 动态版API介绍 ## 请求说明 |名称|类型|必须|说明| | --- | --- | --- | --- | |image|String|否|图片base64| |url|String|否|图片地址| |area|String|否|特定区域坐标选择,支持多个多边形区域。如输入超过10个区域,截取前10个区域进行识别。例如:x1,y1,x2,y2,x3,y3…xn,yn同一个区域内的坐标使用英文逗号“,”分隔,默认尾点和首点相连做闭合,例:”1,1,620,1,620,620,1,620”多个区域使用英文分号“;”分隔。例如:1,1,1,720,100,720…100,1;200,1,200,720…300,720,300,1;…注意:area区域坐标从(1,1)开始,坐标大于或等于原图大小,以及x、y坐标未成对出现都是错误的注意:设置了多个区域时,任意一个坐标设置错误,则认为area参数错误、失效。注意:若此参数为空或无此参数、或area参数设置错误时,默认识别整张图片中的人数。| |show|String|否|是否输出渲染的图片,默认false不返回,选true时返回渲染后的图片(base64)。若此字段为无效值或为空则默认false| ## 返回样例 ``` { "code": 200, // 返回码,详见返回码说明 "msg": "成功", // 返回码对应描述 "taskNo": "65605503936940344488", // 本次请求号 "charge": false, // 计费标志 "data": { "personNum": "1", //识别出的人体数目 "personInfo"://每个框的具体信息 [ { "ID":3,//人体的ID编号,ID的取值逻辑为:每个case从1开始,不同人体向上递增但不一定连续 "location"://跟踪到的人体框位置 { "left": 100,//人体框左坐标 "top": 200,//人体框顶坐标 "width": 200,//人体框宽度 "height": 400//人体框高度 } }, { "ID": 5, "location": { "left": 400, "top": 200, "width": 200, "height": 400 } } ], "personCount"://进出区域的人流统计 { "in":0,//当前帧进入区域的瞬时人数;如需计算一段时间内进入区域的累计人数,可根据连续帧图片的返回结果计算。 "out":1//当前帧离开区域的瞬时人数;如需计算一段时间内进入区域的累计人数,可根据连续帧图片的返回结果计算。 } } } ```

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册