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

站长工具是干嘛的免费网站建设阿里云

站长工具是干嘛的,免费网站建设阿里云,平台型网站如何推广,郑州网站设计 公司目录 一、效果演示 二、后端滑块验证码生成思路 三、原理解析 四、核心代码拿走 滑块验证码react前端实现,见我的这篇博客:前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客 一、效果演示 生成的案例…

目录

一、效果演示

二、后端滑块验证码生成思路

三、原理解析

四、核心代码拿走


滑块验证码react前端实现,见我的这篇博客:前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客

一、效果演示

生成的案例图片:

视频演示:

滑块验证码演示

二、后端滑块验证码生成思路

1、后端需要生成对应的两个图片(拼图图片和拼图背景图片,图片内存尽量小一点)和对应位置(x和y, 等高拼图只需要记录x即可);

2、验证码生成服务,生成唯一的标识uuid(可考虑雪花算法生成),将生成图片生成后得到的位置信息即x(非登高拼图x和y)记录到缓存中,建议使用redis存储,即使分布式也能使用;

3、将验证码数据返回给前端,格式参考如下:

三、原理解析

要想生成拼图形状的拼图,我们需要运用到一些数学知识,核心代码如下:

通过 圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆,>=的在外侧,<的内侧。

简单来看就是这样的一个模型:

 /*** 随机生成拼图图轮廓数据** @param randomR1 圆点距离随机值* @return 0和1,其中0表示没有颜色,1有颜色*/private static int[][] createTemplateData(int randomR1) {// 拼图轮廓数据int[][] data = new int[puzzleWidth][puzzleHeight];// 拼图去掉凹凸的白色距离int xBlank = puzzleWidth - distance;int yBlank = puzzleHeight - distance;// 记录圆心的位置值int topOrBottomX = puzzleWidth / 2;int leftOrRightY = puzzleHeight / 2;// 凹时对应的位置int topYOrLeftX = distance - randomR1 + radius;int rightX = puzzleWidth - topYOrLeftX;int bottomY = puzzleHeight - topYOrLeftX;// 凸时对应的位置int topYOrLeftXR = distance + randomR1 - radius;int rightXR = puzzleWidth - topYOrLeftXR;int bottomYR = puzzleHeight - topYOrLeftXR;double rPow = Math.pow(radius, 2);/* 随机获取判断条件 */Random random = new Random();Integer[] randomCondition = new Integer[]{random.nextInt(3),random.nextInt(3),random.nextInt(3),random.nextInt(3)};/*计算需要的拼图轮廓(方块和凹凸),用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆*/for (int i = 0; i < puzzleWidth; i++) {for (int j = 0; j < puzzleHeight; j++) {/* 凹时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftX)double top = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftX, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftX)double bottom = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomY, 2);// 左侧的圆心位置为(topYOrLeftX, puzzleHeight / 2)double left = Math.pow(i - topYOrLeftX, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftX, puzzleHeight / 2)double right = Math.pow(i - rightX, 2) + Math.pow(j - leftOrRightY, 2);/* 凸时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftXR)double topR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftXR, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftXR)double bottomR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomYR, 2);// 左侧的圆心位置为(topYOrLeftXR, puzzleHeight / 2)double leftR = Math.pow(i - topYOrLeftXR, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftXR, puzzleHeight / 2)double rightR = Math.pow(i - rightXR, 2) + Math.pow(j - leftOrRightY, 2);/* 随机获取条件 */Boolean[][] conditions = new Boolean[][]{new Boolean[]{(j <= distance && topR >= rPow),(j <= distance || top <= rPow),(j <= distance)},new Boolean[]{(j >= yBlank && bottomR >= rPow),(j >= yBlank || bottom <= rPow),(j >= yBlank)},new Boolean[]{(i <= distance && leftR >= rPow),(i <= distance || left <= rPow),(i <= distance)},new Boolean[]{(i >= xBlank && rightR >= rPow),(i >= xBlank || right <= rPow),(i >= xBlank)}};boolean hide = false;for (int c = 0; c < randomCondition.length; c++) {if (conditions[c][randomCondition[c]]) {hide = true;break;}}if (hide) {// 不显示的像素data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}

绘制好需要截取的数据位置后,再来进行剪切:

 /*** 裁剪拼图** @param bgImg             - 原图规范大小之后的大图* @param puzzleImg         - 小图* @param slideTemplateData - 拼图轮廓数据* @param x                 - 坐标x* @param y                 - 坐标y*/private static void cutByTemplate(BufferedImage bgImg, BufferedImage puzzleImg, int[][] slideTemplateData, int x, int y) {int[][] matrix = new int[3][3];int[] values = new int[9];// 虚假的x坐标int fakeX = getRandomFakeX(x);// 创建shape区域,即原图抠图区域模糊和抠出小图/*遍历小图轮廓数据,创建shape区域。即原图抠图处模糊和抠出小图*/for (int i = 0; i < puzzleImg.getWidth(); i++) {for (int j = 0; j < puzzleImg.getHeight(); j++) {// 获取大图中对应位置变色int rgb_ori = bgImg.getRGB(x + i, y + j);// 0和1,其中0表示没有颜色,1有颜色int rgb = slideTemplateData[i][j];if (rgb == 1) {// 设置小图中对应位置变色puzzleImg.setRGB(i, j, rgb_ori);// 大图抠图区域高斯模糊readPixel(bgImg, x + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(x + i, y + j, avgMatrix(matrix));// 抠虚假图readPixel(bgImg, fakeX + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(fakeX + i, y + j, avgMatrix(matrix));} else {// 这里把背景设为透明puzzleImg.setRGB(i, j, rgb_ori & 0x00ffffff);}}}}

四、核心代码拿走

SliderCaptchaUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.SliderCaptchaConfig;
import com.xloda.common.tool.captcha.pojo.SliderCaptcha;import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;/*** @author Dragon Wu* @since 2025/04/23 10:52* 滑块验证码生成器*/public class SliderCaptchaUtil implements SliderCaptchaConfig {/*** 生成滑块验证码** @param bgImg     1. 传入随机背景图* @param accordant 是否生成登高拼图* @return SliderCaptcha 验证码结果* @throws IOException IO异常*/public static SliderCaptcha generateCaptcha(BufferedImage bgImg, boolean accordant) throws IOException {// 2. 随机生成离左上角的(X,Y)坐标,上限为 [width-puzzleWidth, height-puzzleHeight]。最好离大图左边远一点,上限不要紧挨着大图边界Random random = new Random();// X范围:[puzzleWidth, width - puzzleWidth)int x = random.nextInt(width - 2 * puzzleWidth) + puzzleWidth;// Y范围:[puzzleHeight, height - puzzleHeight)int y = random.nextInt(height - 2 * puzzleHeight) + puzzleHeight;// 3. 创建拼图图像BufferedImage puzzleImg = new BufferedImage(puzzleWidth, puzzleHeight, BufferedImage.TYPE_4BYTE_ABGR);// 4. 随机获取位置数据int randomR1 = getRandomR1();// 5. 随机生成拼图轮廓数据int[][] slideTemplateData = createTemplateData(randomR1);// 6. 从大图中裁剪拼图。抠原图,裁剪拼图cutByTemplate(bgImg, puzzleImg, slideTemplateData, x, y);// 7. 给拼图加边框puzzleImg = ImageUtil.addBorderWithOutline(puzzleImg, borderSize, Color.white);// 8. 判断是否为登高拼图if (accordant) {puzzleImg = reshapeAccordant(puzzleImg, y);return new SliderCaptcha(ImageUtil.toBase64(bgImg),ImageUtil.toBase64(puzzleImg), x);}// 非登高拼图,记录x和yreturn new SliderCaptcha(ImageUtil.toBase64(bgImg),ImageUtil.toBase64(puzzleImg), x, y);}// 随机获取小圆距离点private static int getRandomR1() {Integer[] r1List = new Integer[]{radius * 3 / 2,radius,radius / 2,};int index = new Random().nextInt(r1List.length);return r1List[index];}/*** 随机生成拼图图轮廓数据** @param randomR1 圆点距离随机值* @return 0和1,其中0表示没有颜色,1有颜色*/private static int[][] createTemplateData(int randomR1) {// 拼图轮廓数据int[][] data = new int[puzzleWidth][puzzleHeight];// 拼图去掉凹凸的白色距离int xBlank = puzzleWidth - distance;int yBlank = puzzleHeight - distance;// 记录圆心的位置值int topOrBottomX = puzzleWidth / 2;int leftOrRightY = puzzleHeight / 2;// 凹时对应的位置int topYOrLeftX = distance - randomR1 + radius;int rightX = puzzleWidth - topYOrLeftX;int bottomY = puzzleHeight - topYOrLeftX;// 凸时对应的位置int topYOrLeftXR = distance + randomR1 - radius;int rightXR = puzzleWidth - topYOrLeftXR;int bottomYR = puzzleHeight - topYOrLeftXR;double rPow = Math.pow(radius, 2);/* 随机获取判断条件 */Random random = new Random();Integer[] randomCondition = new Integer[]{random.nextInt(3),random.nextInt(3),random.nextInt(3),random.nextInt(3)};/*计算需要的拼图轮廓(方块和凹凸),用二维数组来表示,二维数组有两张值,0和1,其中0表示没有颜色,1有颜色圆的标准方程 (x-a)²+(y-b)²=r²,标识圆心(a,b),半径为r的圆*/for (int i = 0; i < puzzleWidth; i++) {for (int j = 0; j < puzzleHeight; j++) {/* 凹时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftX)double top = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftX, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftX)double bottom = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomY, 2);// 左侧的圆心位置为(topYOrLeftX, puzzleHeight / 2)double left = Math.pow(i - topYOrLeftX, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftX, puzzleHeight / 2)double right = Math.pow(i - rightX, 2) + Math.pow(j - leftOrRightY, 2);/* 凸时对应的圆点 */// 顶部的圆心位置为(puzzleWidth / 2, topYOrLeftXR)double topR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - topYOrLeftXR, 2);// 底部的圆心位置为(puzzleWidth / 2, puzzleHeight - topYOrLeftXR)double bottomR = Math.pow(i - topOrBottomX, 2) + Math.pow(j - bottomYR, 2);// 左侧的圆心位置为(topYOrLeftXR, puzzleHeight / 2)double leftR = Math.pow(i - topYOrLeftXR, 2) + Math.pow(j - leftOrRightY, 2);// 右侧的圆心位置为(puzzleWidth - topYOrLeftXR, puzzleHeight / 2)double rightR = Math.pow(i - rightXR, 2) + Math.pow(j - leftOrRightY, 2);/* 随机获取条件 */Boolean[][] conditions = new Boolean[][]{new Boolean[]{(j <= distance && topR >= rPow),(j <= distance || top <= rPow),(j <= distance)},new Boolean[]{(j >= yBlank && bottomR >= rPow),(j >= yBlank || bottom <= rPow),(j >= yBlank)},new Boolean[]{(i <= distance && leftR >= rPow),(i <= distance || left <= rPow),(i <= distance)},new Boolean[]{(i >= xBlank && rightR >= rPow),(i >= xBlank || right <= rPow),(i >= xBlank)}};boolean hide = false;for (int c = 0; c < randomCondition.length; c++) {if (conditions[c][randomCondition[c]]) {hide = true;break;}}if (hide) {// 不显示的像素data[i][j] = 0;} else {data[i][j] = 1;}}}return data;}/*** 裁剪拼图** @param bgImg             - 原图规范大小之后的大图* @param puzzleImg         - 小图* @param slideTemplateData - 拼图轮廓数据* @param x                 - 坐标x* @param y                 - 坐标y*/private static void cutByTemplate(BufferedImage bgImg, BufferedImage puzzleImg, int[][] slideTemplateData, int x, int y) {int[][] matrix = new int[3][3];int[] values = new int[9];// 虚假的x坐标int fakeX = getRandomFakeX(x);// 创建shape区域,即原图抠图区域模糊和抠出小图/*遍历小图轮廓数据,创建shape区域。即原图抠图处模糊和抠出小图*/for (int i = 0; i < puzzleImg.getWidth(); i++) {for (int j = 0; j < puzzleImg.getHeight(); j++) {// 获取大图中对应位置变色int rgb_ori = bgImg.getRGB(x + i, y + j);// 0和1,其中0表示没有颜色,1有颜色int rgb = slideTemplateData[i][j];if (rgb == 1) {// 设置小图中对应位置变色puzzleImg.setRGB(i, j, rgb_ori);// 大图抠图区域高斯模糊readPixel(bgImg, x + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(x + i, y + j, avgMatrix(matrix));// 抠虚假图readPixel(bgImg, fakeX + i, y + j, values);fillMatrix(matrix, values);bgImg.setRGB(fakeX + i, y + j, avgMatrix(matrix));} else {// 这里把背景设为透明puzzleImg.setRGB(i, j, rgb_ori & 0x00ffffff);}}}}/*** 随机获取虚假x坐标的值** @param x 真正的x坐标* @return fakeX*/private static int getRandomFakeX(int x) {int puzzleRealWidth = puzzleWidth + 2 * borderSize + 2;Random random = new Random();int fakeX = random.nextInt(width - 2 * puzzleRealWidth) + puzzleRealWidth;if (Math.abs(fakeX - x) <= puzzleRealWidth) {fakeX = width - x;}return fakeX;}/*** 通过拼图图片生成登高拼图图片** @param puzzleImg 拼图图片* @param offsetY   随机生成的y* @return 登高拼图图片*/private static BufferedImage reshapeAccordant(BufferedImage puzzleImg, int offsetY) {BufferedImage puzzleBlankImg = new BufferedImage(puzzleWidth + 2 * borderSize + 2, height, BufferedImage.TYPE_4BYTE_ABGR);Graphics2D graphicsPuzzle = puzzleBlankImg.createGraphics();graphicsPuzzle.drawImage(puzzleImg, 1, offsetY, null);graphicsPuzzle.dispose();return puzzleBlankImg;}private static void readPixel(BufferedImage img, int x, int y, int[] pixels) {int xStart = x - 1;int yStart = y - 1;int current = 0;for (int i = xStart; i < 3 + xStart; i++) {for (int j = yStart; j < 3 + yStart; j++) {int tx = i;if (tx < 0) {tx = -tx;} else if (tx >= img.getWidth()) {tx = x;}int ty = j;if (ty < 0) {ty = -ty;} else if (ty >= img.getHeight()) {ty = y;}pixels[current++] = img.getRGB(tx, ty);}}}private static int avgMatrix(int[][] matrix) {int r = 0;int g = 0;int b = 0;for (int[] x : matrix) {for (int j = 0; j < x.length; j++) {if (j == 1) {continue;}Color c = new Color(x[j]);r += c.getRed();g += c.getGreen();b += c.getBlue();}}return new Color(r / 8, g / 8, b / 8).getRGB();}private static void fillMatrix(int[][] matrix, int[] values) {int filled = 0;for (int[] x : matrix) {for (int j = 0; j < x.length; j++) {x[j] = values[filled++];}}}
}

RandomUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.CaptchaConstants;import java.util.Random;/*** @author Dragon Wu* @since 2025/04/23 18:07* 随机生成器工具*/public class RandomUtil {// 随机获取背景图路径public static String randomBgImgPath() {int index = new Random().nextInt(CaptchaConstants.BG_IMAGES.length);return CaptchaConstants.BG_IMAGES[index];}
}

ImageUtil:

package com.xloda.common.tool.captcha.util;import com.xloda.common.tool.captcha.constant.CaptchaConstants;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;/*** @author Dragon Wu* @since 2025/04/25 10:18* 图片处理工具*/public class ImageUtil {public static BufferedImage addBorderWithOutline(BufferedImage image, int borderWidth, Color borderColor) {// 创建新图像,尺寸扩大以容纳边框BufferedImage result = new BufferedImage(image.getWidth() + borderWidth * 2,image.getHeight() + borderWidth * 2,BufferedImage.TYPE_INT_ARGB);Graphics2D g2d = result.createGraphics();g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);// 获取图像的非透明区域Area area = new Area();for (int y = 0; y < image.getHeight(); y++) {for (int x = 0; x < image.getWidth(); x++) {if ((image.getRGB(x, y) >> 24) != 0x00) {area.add(new Area(new Rectangle(x, y, 1, 1)));}}}// 绘制边框g2d.setColor(borderColor);g2d.setStroke(new BasicStroke(borderWidth * 2));g2d.translate(borderWidth, borderWidth);g2d.draw(area);// 绘制原始图像g2d.drawImage(image, 0, 0, null);g2d.dispose();return result;}// 图片转Base64public static String toBase64(BufferedImage image) throws IOException {// 创建一个字节数组输出流ByteArrayOutputStream os = new ByteArrayOutputStream();// 将BufferedImage写入到输出流中,这里指定图片格式为"png"或"jpg"等ImageIO.write(image, CaptchaConstants.IMG_FORMAT, os);// 将输出流的字节数组转换为Base64编码的字符串String imageBase64 = Base64.getEncoder().encodeToString(os.toByteArray());// 关闭输出流os.close();return CaptchaConstants.BASE64_PREFIX + imageBase64;}
}

SliderCaptcha:

package com.xloda.common.tool.captcha.pojo;import lombok.*;/*** @author Dragon Wu* @since 2025/04/23 10:49* 滑块验证码*/
@AllArgsConstructor
@Getter
@ToString
public class SliderCaptcha {// 验证码背景图private String bgImg;// 验证码滑块private String puzzleImg;// 验证码正确的x位置(此值需自行存入缓存,用于验证码判断)private int x;// 等高拼图时,返回0(非登高拼图,此值需自行存入缓存,用于验证码判断)private int y;public SliderCaptcha(String bgImg, String puzzleImg, int x) {this.bgImg = bgImg;this.puzzleImg = puzzleImg;this.x = x;}
}

CaptchaConstants:

package com.xloda.common.tool.captcha.constant;/*** @author Dragon Wu* @since 2025/04/23 10:53*/
public interface CaptchaConstants {// 图片格式String IMG_FORMAT = "png";// base64前缀String BASE64_PREFIX = "data:image/" + IMG_FORMAT + ";base64,";// 图片存储的目录String FOLDER = "/static/img/captcha/";// 背景图列表(引入依赖后,记得在项目资源目录的该路径下添加对应图片)String[] BG_IMAGES = new String[]{FOLDER + "bg01.png",FOLDER + "bg01.png"};
}

SliderCaptchaConfig

package com.xloda.common.tool.captcha.constant;/*** @author Dragon Wu* @since 2025/04/23 11:09* 滑块验证码的配置*/public interface SliderCaptchaConfig {// 大图宽度(原图裁剪拼图后的背景图)int width = 280;// 大图高度int height = 173;// 小图宽度(滑块拼图),前端拼图的实际宽度:puzzleWidth + 2 * borderSize + 2int puzzleWidth = 66;// 小图高度,前端拼图的实际高度:puzzleHeight + 2 * borderSize + 2int puzzleHeight = 66;// 边框厚度int borderSize = 1;// 小圆半径,即拼图上的凹凸轮廓半径int radius = 8;// 图片一周预留的距离,randomR1最大值不能超过radius * 3 / 2int distance = radius * 3 / 2;
}

接下来继续手搓旋转验证码前后端。

本节,总结到此,学点数学挺有用的!


文章转载自:

http://0WesKV15.bmyrL.cn
http://C2qe6ABg.bmyrL.cn
http://fzOMMnpZ.bmyrL.cn
http://ucZACI70.bmyrL.cn
http://dXhtqunZ.bmyrL.cn
http://IAykvRkX.bmyrL.cn
http://E2LnaEmA.bmyrL.cn
http://c3FAdRHM.bmyrL.cn
http://3X5VL1AE.bmyrL.cn
http://w1vaVXbw.bmyrL.cn
http://3C2dCQxl.bmyrL.cn
http://m7fCnqZf.bmyrL.cn
http://rW2b6E2f.bmyrL.cn
http://R2oPzEwM.bmyrL.cn
http://Ji6IdQBi.bmyrL.cn
http://2L4IFmNn.bmyrL.cn
http://kXKNzI6A.bmyrL.cn
http://nXAfqbEh.bmyrL.cn
http://Lg2v4gUu.bmyrL.cn
http://lQ6J8TvI.bmyrL.cn
http://EDnjbSnH.bmyrL.cn
http://vQLLf1H2.bmyrL.cn
http://X14Yv7zT.bmyrL.cn
http://j5uT7V3M.bmyrL.cn
http://GKYSKqKy.bmyrL.cn
http://vNrsD0IB.bmyrL.cn
http://V1l2kpE2.bmyrL.cn
http://5D6nouKP.bmyrL.cn
http://YQV3y7LG.bmyrL.cn
http://P5EFKznk.bmyrL.cn
http://www.dtcms.com/wzjs/701827.html

相关文章:

  • 网站如何seo视觉设计专业学什么
  • 工程建设资质单位网站南通做网站找谁
  • 浙江省建设工程造价协会网站网站知名度
  • 天津建设工程注册中心网站2021年资料员报名入口官网
  • 苏州模板建站定制免费海外网络连接器
  • 软件下载免费大全网站织梦网站地图怎么做xml
  • 阿里云建设网站的步骤推广引流黑科技
  • 找人帮你做ppt的网站吗网站开发相关专业
  • 为女朋友做网站百度推广计划
  • 网站配置系统站优化
  • 网站资源建设的步骤企业微网站与手机微信号
  • 郑州企业网站制作公司建盏公司哪几家
  • 商业空间设计网站大全怎么样做问卷网站
  • 建立网站 英语现在那个网站做推广效果会好点
  • 做网站公司如何赚钱网站的营销推广
  • 手机网站建设找哪家好数字展厅制作公司
  • 网站域名管理网站的详情页面设计
  • 嘉兴网络项目建站公司域名 和网站有什么区别
  • 南昌哪家网站开发公司好低价网站建设制作费用
  • 国外开源代码网站精品课程网站建设步骤
  • 几度设计网站WordPress首页打开慢
  • 做网站需要icp电商网站建设怎么样
  • 切图做网站中国建筑出国招聘网
  • 深圳品牌网站制作公司哪家好找小程序开发公司
  • iis架设jsp网站光明网站开发
  • 长白山开发建设集团网站项目管理平台
  • 手机工信部网站备案查询上海高端网站定制开发
  • 谢岗东莞网站建设西安工程造价信息网
  • 网站快速排名服务传媒公司网站建设策划
  • asp access 做网站seo网课培训