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

北京工程质量建设协会网站巩义网络推广公司

北京工程质量建设协会网站,巩义网络推广公司,安徽省建设工程招投标信息网,推荐一些做网站网络公司前言 最近有个想法想整理一个内容比较完整springboot项目初始化Demo。 连接Oracle数据库集成mybatis-plus&#xff0c;自定义WrapperFactory。配置代码生成器 一、引入jar包 <!--oracle驱动 --><dependency><groupId>org.springframework.boot</groupI…

前言

最近有个想法想整理一个内容比较完整springboot项目初始化Demo。

连接Oracle数据库集成mybatis-plus,自定义WrapperFactory。配置代码生成器

一、引入jar包

 <!--oracle驱动 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>com.oracle.ojdbc</groupId><artifactId>ojdbc8</artifactId><version>19.3.0.0</version></dependency><!-- oracle8 所需的字符集 --><dependency><groupId>cn.easyproject</groupId><artifactId>orai18n</artifactId><version>12.1.0.2.0</version></dependency><!--mybatis-plus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.1</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1</version></dependency><dependency><groupId>org.freemarker</groupId><artifactId>freemarker</artifactId><version>2.3.28</version></dependency><!--mybatis-plus-->

二、新建配置文件application-dev.yaml

以根据不同的环境配置不同的属性文件,application-{profile} 表示环境名称。
例如,application-dev.yaml用于开发环境,application-prod.yaml用于生产环境。

增加数据库配置 hikari连接池配置

spring:datasource:driver-class-name: oracle.jdbc.OracleDriverurl: jdbc:oracle:thin:@//127.0.0.1:1521/orclusername: murg_demopassword: 654321hikari:connection-test-query: SELECT 1 FROM DUALminimum-idle: 10maximum-pool-size: 50idle-timeout: 120000max-lifetime: 180000connection-timeout: 18000
logging:level:org.springframework.security: debug
启动服务看服务是否正常启动

三、自定义MyBatis 构造对象加工厂。

用于规定查询返回值格式 主要用于_处理和字段大小写规定

package com.murg.bootdemo.interceptor;import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.ObjectWrapper;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import java.util.Map;/*** 自定义ObjectWrapperFactory 结果处理 去除_  字段大写转小写*/
public class MapWrapperFactory implements ObjectWrapperFactory {@Overridepublic boolean hasWrapperFor(Object object) {return object instanceof Map;}@Overridepublic ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) {return new MyMapWrapper(metaObject,(Map)object);}
}
package com.murg.bootdemo.interceptor;import org.apache.commons.lang3.math.NumberUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.wrapper.MapWrapper;import java.util.Map;/*** mybatis 结果处理 去除_  字段大写转小写*/
public class MyMapWrapper extends MapWrapper {public MyMapWrapper(MetaObject metaObject, Map<String, Object> map) {super(metaObject, map);}@Overridepublic String findProperty(String name, boolean useCamelCaseMapping) {if (useCamelCaseMapping&& ((name.charAt(0) >= 'A' && name.charAt(0) <= 'Z')|| name.contains("_"))) {return underlineToCamelhump(name);}return name;}/*** 将下划线风格替换为驼峰风格* @param inputString* @return*/public String underlineToCamelhump(String inputString) {StringBuilder sb = new StringBuilder();boolean nextUpperCase = false;for (int i = 0; i < inputString.length(); i++) {char c = inputString.charAt(i);if (c == '_') {if (sb.length() > 0) {nextUpperCase = true;}} else {if (nextUpperCase) {if (NumberUtils.isNumber(String.valueOf(c))) {sb.append("_").append(c);} else {sb.append(Character.toUpperCase(c));}nextUpperCase = false;} else {sb.append(Character.toLowerCase(c));}}}return sb.toString();}
}

四、自定义WrapperFactoryConverter

以上配置好WrapperFactory后启动会报错

提示找不到合适的converter将string转化为ObjectWrapperFactory对象

缺少对应的converter,springboot不会提供反射机制来构建一个对象的converter,需要手写一个

package com.murg.bootdemo.interceptor;import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;
import org.springframework.boot.context.properties.ConfigurationPropertiesBinding;
import org.springframework.core.convert.converter.Converter;
import org.springframework.stereotype.Component;@Component
@ConfigurationPropertiesBinding
public class ObjectWrapperFactoryConverter implements Converter<String, ObjectWrapperFactory> {@Overridepublic ObjectWrapperFactory convert(String source) {try {return (ObjectWrapperFactory) Class.forName(source).newInstance();} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {throw new RuntimeException(e);}}
}

五、整合MyBatisPlus代码生成器 mybatis-plus-generator配合freemaker引擎

5.1下面这些文件直接创建在resources/generator1/templates的目录下,把内容复制进去就行了。这些文件就是代码生成的格式模板。

controller.java.ftl

package ${package.Controller};import org.springframework.web.bind.annotation.RequestMapping;<#if restControllerStyle>
import org.springframework.web.bind.annotation.RestController;
<#else>
import org.springframework.stereotype.Controller;
</#if>
<#if superControllerClassPackage??>
import ${superControllerClassPackage};
</#if>/*** <p>* ${table.comment!} 前端控制器* </p>** @author ${author}* @since ${date}*/
<#if restControllerStyle>
@RestController
<#else>
@Controller
</#if>
@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}</#if>/<#if controllerMappingHyphenStyle??>${controllerMappingHyphen}<#else>${table.entityPath}</#if>")
<#if kotlin>
class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}()</#if>
<#else>
<#if superControllerClass??>
public class ${table.controllerName} extends ${superControllerClass} {
<#else>
public class ${table.controllerName} {
</#if>}
</#if>

entity.java.ftl

package ${package.Entity};<#list table.importPackages as pkg>
import ${pkg};
</#list>
<#if swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>
<#if entityLombokModel>
import lombok.Getter;
import lombok.Setter;<#if chainModel>
import lombok.experimental.Accessors;</#if>
</#if>/*** <p>* ${table.comment!}* </p>** @author ${author}* @since ${date}*/
<#if entityLombokModel>
@Getter
@Setter<#if chainModel>
@Accessors(chain = true)</#if>
</#if>
<#if table.convert>
@TableName("${schemaName}${table.name}")
</#if>
<#if swagger>
@ApiModel(value = "${entity}对象", description = "${table.comment!}")
</#if>
<#if superEntityClass??>
public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
public class ${entity} extends Model<${entity}> {
<#elseif entitySerialVersionUID>
public class ${entity} implements Serializable {
<#else>
public class ${entity} {
</#if>
<#if entitySerialVersionUID>private static final long serialVersionUID = 1L;
</#if>
<#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field><#if field.keyFlag><#assign keyPropertyName="${field.propertyName}"/></#if><#if field.comment!?length gt 0><#if swagger>@ApiModelProperty("${field.comment}")<#else>/*** ${field.comment}*/</#if></#if><#if field.keyFlag><#-- 主键 --><#if field.keyIdentityFlag>@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)<#elseif idType??>@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})<#elseif field.convert>@TableId("${field.annotationColumnName}")</#if><#-- 普通字段 --><#elseif field.fill??><#-- -----   存在字段填充设置   -----><#if field.convert>@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})<#else>@TableField(fill = FieldFill.${field.fill})</#if><#elseif field.convert>@TableField("${field.annotationColumnName}")</#if><#-- 乐观锁注解 --><#if field.versionField>@Version</#if><#-- 逻辑删除注解 --><#if field.logicDeleteField>@TableLogic</#if>private ${field.propertyType} ${field.propertyName};
</#list>
<#------------  END 字段循环遍历  ----------><#if !entityLombokModel><#list table.fields as field><#if field.propertyType == "boolean"><#assign getprefix="is"/><#else><#assign getprefix="get"/></#if>public ${field.propertyType} ${getprefix}${field.capitalName}() {return ${field.propertyName};}<#if chainModel>public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) {<#else>public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) {</#if>this.${field.propertyName} = ${field.propertyName};<#if chainModel>return this;</#if>}</#list>
</#if><#if entityColumnConstant><#list table.fields as field>public static final String ${field.name?upper_case} = "${field.name}";</#list>
</#if>
<#if activeRecord>@Overridepublic Serializable pkVal() {<#if keyPropertyName??>return this.${keyPropertyName};<#else>return null;</#if>}</#if>
<#if !entityLombokModel>@Overridepublic String toString() {return "${entity}{" +<#list table.fields as field><#if field_index==0>"${field.propertyName}=" + ${field.propertyName} +<#else>", ${field.propertyName}=" + ${field.propertyName} +</#if></#list>"}";}
</#if>
}

entity.kt.ftl

package ${package.Entity}<#list table.importPackages as pkg>
import ${pkg}
</#list>
<#if swagger>
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
</#if>/*** <p>* ${table.comment}* </p>** @author ${author}* @since ${date}*/
<#if table.convert>
@TableName("${schemaName}${table.name}")
</#if>
<#if swagger>
@ApiModel(value = "${entity}对象", description = "${table.comment!}")
</#if>
<#if superEntityClass??>
class ${entity} : ${superEntityClass}<#if activeRecord><${entity}></#if> {
<#elseif activeRecord>
class ${entity} : Model<${entity}>() {
<#elseif entitySerialVersionUID>
class ${entity} : Serializable {
<#else>
class ${entity} {
</#if><#-- ----------  BEGIN 字段循环遍历  ---------->
<#list table.fields as field>
<#if field.keyFlag><#assign keyPropertyName="${field.propertyName}"/>
</#if>
<#if field.comment!?length gt 0>
<#if swagger>@ApiModelProperty("${field.comment}")
<#else>/*** ${field.comment}*/
</#if>
</#if>
<#if field.keyFlag>
<#-- 主键 -->
<#if field.keyIdentityFlag>@TableId(value = "${field.annotationColumnName}", type = IdType.AUTO)
<#elseif idType ??>@TableId(value = "${field.annotationColumnName}", type = IdType.${idType})
<#elseif field.convert>@TableId("${field.annotationColumnName}")
</#if>
<#-- 普通字段 -->
<#elseif field.fill??>
<#-- -----   存在字段填充设置   ----->
<#if field.convert>@TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill})
<#else>@TableField(fill = FieldFill.${field.fill})
</#if>
<#elseif field.convert>@TableField("${field.annotationColumnName}")
</#if>
<#-- 乐观锁注解 -->
<#if field.versionField>@Version
</#if>
<#-- 逻辑删除注解 -->
<#if field.logicDeleteField>@TableLogic
</#if><#if field.propertyType == "Integer">var ${field.propertyName}: Int? = null<#else>var ${field.propertyName}: ${field.propertyType}? = null</#if></#list>
<#-- ----------  END 字段循环遍历  ---------->
<#if entityColumnConstant>companion object {
<#list table.fields as field>const val ${field.name?upper_case} : String = "${field.name}"</#list>}</#if>
<#if activeRecord>override fun pkVal(): Serializable? {
<#if keyPropertyName??>return ${keyPropertyName}
<#else>return null
</#if>}</#if>override fun toString(): String {return "${entity}{" +
<#list table.fields as field>
<#if field_index==0>"${field.propertyName}=" + ${field.propertyName} +
<#else>", ${field.propertyName}=" + ${field.propertyName} +
</#if>
</#list>"}"}
}

mapper.java.ftl

package ${package.Mapper};import ${package.Entity}.${entity};
import ${superMapperClassPackage};
<#if mapperAnnotation>
import org.apache.ibatis.annotations.Mapper;
</#if>/*** <p>* ${table.comment!} Mapper 接口* </p>** @author ${author}* @since ${date}*/
<#if mapperAnnotation>
@Mapper
</#if>
<#if kotlin>
interface ${table.mapperName} : ${superMapperClass}<${entity}>
<#else>
public interface ${table.mapperName} extends ${superMapperClass}<${entity}> {}
</#if>

mapper.xml.ftl

<?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="${package.Mapper}.${table.mapperName}"><#if enableCache><!-- 开启二级缓存 --><cache type="${cacheClassName}"/></#if>
<#if baseResultMap><!-- 通用查询映射结果 --><resultMap id="BaseResultMap" type="${package.Entity}.${entity}">
<#list table.fields as field>
<#if field.keyFlag><#--生成主键排在第一位--><id column="${field.name}" property="${field.propertyName}" />
</#if>
</#list>
<#list table.commonFields as field><#--生成公共字段 --><result column="${field.name}" property="${field.propertyName}" />
</#list>
<#list table.fields as field>
<#if !field.keyFlag><#--生成普通字段 --><result column="${field.name}" property="${field.propertyName}" />
</#if>
</#list></resultMap></#if>
<#if baseColumnList><!-- 通用查询结果列 --><sql id="Base_Column_List">
<#list table.commonFields as field>${field.columnName},
</#list>${table.fieldNames}</sql></#if>
</mapper>

service.java.ftl

package ${package.Service};import ${package.Entity}.${entity};
import ${package.Mapper}.${table.mapperName};
import ${superServiceImplClassPackage};
import org.springframework.stereotype.Service;/**
* <p>* ${table.comment!} 服务实现类* </p>
*
* @author ${author}
* @since ${date}
*/
@Service
public class ${table.serviceImplName} extends ${superServiceImplClass}<${table.mapperName}, ${entity}> {}

 5.2创建生成代码类CodeGenerator 

package com.murg.bootdemo.util;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.ConstVal;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.TemplateType;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.commons.lang3.StringUtils;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;/*** Generator生成代码**/
public class CodeGenerator {public static void main(String[] args) {generator();}public static void generator() {final String[] moduleName = new String[1];FastAutoGenerator.create(new DataSourceConfig.Builder("jdbc:oracle:thin:@//127.0.0.1:1521/orcl","murg_test","654321"))// 全局配置.globalConfig((scanner, builder) -> builder.author(StringUtils.defaultString(getGitUsername(), "codeGenerator")).fileOverride().outputDir(System.getProperty("user.dir") +"/src/main/java").disableOpenDir().enableSwagger().dateType(DateType.ONLY_DATE))// 包配置.packageConfig((scanner, builder) -> builder.parent("com.murg.bootdemo." + scanner.apply("请输入包名(从"+ "com.murg.bootdemo."+ "开始 )?")).xml("mapper").serviceImpl("service")).templateConfig((builder -> builder.disable(TemplateType.SERVICE, TemplateType.CONTROLLER).serviceImpl("/templates/service.java")))// 策略配置.strategyConfig((scanner, builder) -> builder.addInclude(getTables(scanner.apply("请输入表名,多个英文逗号分隔?所有输入 all").toUpperCase())).controllerBuilder().enableRestStyle().enableHyphenStyle().serviceBuilder().convertServiceImplFileName((entityName -> entityName + ConstVal.SERVICE)).entityBuilder().enableLombok().idType(IdType.INPUT).build()).templateEngine(new FreemarkerTemplateEngine())//.addIgnoreColumns("prseno") 排除某个字段.execute();}private static String getGitUsername() {Runtime runtime = Runtime.getRuntime();Process exec;try {exec = runtime.exec("git config user.name");} catch (IOException e) {e.printStackTrace();return null;}InputStreamReader inputStreamReader = new InputStreamReader(exec.getInputStream());BufferedReader bufferedReader = new BufferedReader(inputStreamReader);try {return bufferedReader.readLine();} catch (IOException e) {e.printStackTrace();return null;}}// 处理 all 情况protected static List<String> getTables(String tables) {return "all".equals(tables) ? Collections.emptyList() : Arrays.asList(tables.split(","));}
}

5.3运行CodeGenerator 生成代码

点击左侧按钮 运行main方法控制台输出内容

按照控制台输出内容输入包名和表名,多个表名用英文逗号分隔,会在输入的包下生成文件

六、测试服务数据库连接 获取TT26相关数据

6.1启动类增加注解MapperScan 指定mapper扫描位置
@MapperScan("com.murg.bootdemo.**.mapper")

package com.murg.bootdemo;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;@MapperScan("com.murg.bootdemo.**.mapper")
@SpringBootApplication
public class BootdemoApplication {public static void main(String[] args) {SpringApplication.run(BootdemoApplication.class, args);}}


6.2创建测试接口controller

引入Tt26Service 调用getOne方法查询数据库获取Tt26数据

getOne方法为MyBatis-Plus 通用IService使用不了解的同学可以了解一下MyBatis-Plus用法。

package com.murg.bootdemo.business.controller;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.murg.bootdemo.business.entity.Tt26;
import com.murg.bootdemo.business.service.Tt26Service;
import com.murg.bootdemo.common.WebResult;
import lombok.RequiredArgsConstructor;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;@RequiredArgsConstructor
@RestController
public class TestRestController {private final RestTemplate restTemplate;private final Tt26Service tt26Service;@RequestMapping(value = "/getTt26", method = RequestMethod.GET)public Tt26 getTt26(@RequestParam String code){//通过不同的id获取不同的namereturn tt26Service.getOne(Wrappers.<Tt26>lambdaQuery().eq(Tt26::getCode,code))}}

启动服务访问接口测试结果


文章转载自:
http://urbanist.rbzd.cn
http://aspartase.rbzd.cn
http://dysgenic.rbzd.cn
http://northallerton.rbzd.cn
http://merit.rbzd.cn
http://hermaphroditus.rbzd.cn
http://substantiate.rbzd.cn
http://himalaya.rbzd.cn
http://discophile.rbzd.cn
http://betelnut.rbzd.cn
http://ballonet.rbzd.cn
http://gamekeeper.rbzd.cn
http://priceless.rbzd.cn
http://preternormal.rbzd.cn
http://bimolecular.rbzd.cn
http://surrealism.rbzd.cn
http://cadastration.rbzd.cn
http://herrnhuter.rbzd.cn
http://assheaded.rbzd.cn
http://accentor.rbzd.cn
http://effigy.rbzd.cn
http://astrodynamics.rbzd.cn
http://longheaded.rbzd.cn
http://swinglebar.rbzd.cn
http://iiian.rbzd.cn
http://ots.rbzd.cn
http://isochronous.rbzd.cn
http://dayton.rbzd.cn
http://appear.rbzd.cn
http://brunt.rbzd.cn
http://headliner.rbzd.cn
http://insuppressive.rbzd.cn
http://jcb.rbzd.cn
http://hopelessly.rbzd.cn
http://footsie.rbzd.cn
http://pendency.rbzd.cn
http://stomachic.rbzd.cn
http://rubber.rbzd.cn
http://shower.rbzd.cn
http://euxine.rbzd.cn
http://diazine.rbzd.cn
http://pardah.rbzd.cn
http://twosome.rbzd.cn
http://exophthalmus.rbzd.cn
http://myob.rbzd.cn
http://microteaching.rbzd.cn
http://appallingly.rbzd.cn
http://homography.rbzd.cn
http://balneology.rbzd.cn
http://prussianism.rbzd.cn
http://lanital.rbzd.cn
http://unlettered.rbzd.cn
http://asylum.rbzd.cn
http://glyceric.rbzd.cn
http://tetrachloromethane.rbzd.cn
http://electromagnet.rbzd.cn
http://undemonstrable.rbzd.cn
http://tychonic.rbzd.cn
http://thegosis.rbzd.cn
http://bashaw.rbzd.cn
http://fatimid.rbzd.cn
http://ivy.rbzd.cn
http://diploic.rbzd.cn
http://angiocarpy.rbzd.cn
http://synoecism.rbzd.cn
http://italianism.rbzd.cn
http://jejunum.rbzd.cn
http://enactment.rbzd.cn
http://junius.rbzd.cn
http://fauna.rbzd.cn
http://elaeometer.rbzd.cn
http://navigator.rbzd.cn
http://anthropologist.rbzd.cn
http://tile.rbzd.cn
http://turnbench.rbzd.cn
http://summery.rbzd.cn
http://wherefrom.rbzd.cn
http://moss.rbzd.cn
http://frcp.rbzd.cn
http://gumwater.rbzd.cn
http://lawrencium.rbzd.cn
http://faience.rbzd.cn
http://crucify.rbzd.cn
http://hegumen.rbzd.cn
http://appeal.rbzd.cn
http://lastly.rbzd.cn
http://rhotacize.rbzd.cn
http://melanoma.rbzd.cn
http://galero.rbzd.cn
http://westwards.rbzd.cn
http://uncrowded.rbzd.cn
http://bctv.rbzd.cn
http://circannian.rbzd.cn
http://rwandan.rbzd.cn
http://sporter.rbzd.cn
http://baseman.rbzd.cn
http://overelaborate.rbzd.cn
http://tegestology.rbzd.cn
http://silty.rbzd.cn
http://projectile.rbzd.cn
http://www.15wanjia.com/news/92869.html

相关文章:

  • 请问怎么做网站宁波网站推广优化公司电话
  • 广告公司网站(附falsh及源代码)西安百度推广代理商
  • 邢台专业做网站报价seo实战培训中心
  • 怎么才能在百度上做网站推广河南网站定制
  • 湖北交投建设集团网站精准引流推广
  • 我要建立个人网站韩国今日特大新闻
  • 南宁网站制作费用企业网络推广的方法有哪些
  • 威海网站制作网址搜索
  • 网站建设风格站长工具综合查询官网
  • 网站制作app开发网络优化这个行业怎么样
  • 高端企业网站建设流程可以发广告的平台
  • 手机网站建设咨询爱站关键词挖掘
  • 电子购物网站建设目的郴州网站建设推广公司
  • 做网站公司怎么做什么软件可以发布推广信息
  • 工会网站建设可以seo网络推广专员招聘
  • 沈阳大型网站制作公司最近的电脑培训班在哪里
  • 网站关键词结构深圳快速seo排名优化
  • 影楼模板网站品牌运营策划方案
  • 网站安全建设方案前言矿坛器材友情交换
  • 建网站做站长长沙营销推广
  • vue做的网站域名汇总网络销售是干嘛的
  • 自己建一个网站难吗高明公司搜索seo
  • 商务网站建设 模板国内免费推广产品的网站
  • 自己做网站下载怎么友情链接如何添加
  • dw做网站的所有流程ip切换工具
  • 512 做网站重庆企业免费建站
  • 万户做网站很垃圾线上宣传渠道和宣传方式
  • 门头沟富阳网站建设百度推广是什么工作
  • 北京市网站制作公司批量关键词调排名软件
  • 台州专业网站设计系统新的数据新闻