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

汽车网站和移动端建设方案简述提升关键词排名的方法

汽车网站和移动端建设方案,简述提升关键词排名的方法,国内重大新闻10条2022,餐饮培训P2. 配置MySQL和用户注册登录模块 0 概述Tips1 预备知识1.1 SpringBoot 常用模块1.2 pojo层的实现1.3 mapper层的实现1.4 controller层调试CRUD 2 Spring Security2.1 Spring Security 介绍2.2 Spring Security 对接数据库2.3 密码的加密 3 Jwt验证3.1 传统Session验证方式3.2 … P2. 配置MySQL和用户注册登录模块 0 概述Tips1 预备知识1.1 SpringBoot 常用模块1.2 pojo层的实现1.3 mapper层的实现1.4 controller层调试CRUD 2 Spring Security2.1 Spring Security 介绍2.2 Spring Security 对接数据库2.3 密码的加密 3 Jwt验证3.1 传统Session验证方式3.2 Jwt验证方式3.3 Jwt验证所需的配置 4 用户注册和登录模块的实现4.1 用户注册和登录 API 的实现4.1.1 /user/account/login/4.1.2 /user/account/info/ /user/account/register/ 4.2 用户登录模块的前端4.2.1 登录页面和 Login 函数及全局变量 user 的定义与使用4.2.2 获取 user 信息和退出功能 4.3 前端页面授权4.4 登录状态持久化及小问题处理 5 配置MySQL5.1 MySQL的基本结构5.2 IDEA中配置MySQL5.3 Spring使用MySQL需要的依赖5.4 图形化使用MySQL 0 概述 以用户注册登录模块为例学习实现一个具体的业务逻辑的流程。 明白 SpringBoot 如何和 MySQL 结合如何调用数据库中的数据。 了解 pojo, mapper, service, controller 各层的功能及其实现学会自己写一个业务逻辑。 了解 Jwt 验证原理知道密码加密的实现。 重点掌握 SpringBoot 实现一个业务 API 功能的3个流程service, service.impl, controller。 重点掌握前端如何绑定变量如何定义全局变量 vuex如何调用全局变量的函数。 也就是要求掌握第 4 小节所有的代码逻辑。 由于配置 MySQL 不涉及逻辑内容因此放置在本文的最后介绍。 Tips 在写项目的时候要减少搭轮子的时间去找网上现成的轮子比如 JwtUtil 这种。要放行更多接口的时候可以在 config.SecurityConfig 类中添加。使用 Mybatis-plus 从而避免写 SQL 语句不用写简单的 CRUD 操作。具体的接口可以查看Mybatis-plus指南。一些简单的使用在1.4 小节中进行了示范 (querywrapper, userMapper.xxx)。强烈换成阿里源去下依赖用外网下依赖直接给我整崩溃了搞了半天重开了个项目换成阿里源下载依赖才搞好。实现和重写接口的方法可以通过 alt insert 快捷键添加。 1 预备知识 1.1 SpringBoot 常用模块 习惯上会在写后端的时候分层(文件夹)去实现一共分成 4 层: pojo, mapper, service, controller。 pojo: 将 MySQL 中的表 table 翻译成 Java 中的类 class。 mapper: 对 class 的CRUD操作转化成 SQL 语句。 每个 class 需要相应的CRUD操作操作完成后最终会存到数据库中因此需要涉及到 SQL 语句。 service: 业务通常要处理多张表因此组合多个 mapper 实现具体业务。 controller: 调度 service接收前端请求和参数选择将参数传给哪个 service再把 service 结果返回给前端。 1.2 pojo层的实现 pojo 是将 MySQL 中的 user 表转化成 User 类每个类需要自己的属性和方法属性需要自己对照数据库中的字段定义可以通过安装的依赖 lombok 自动实现这些机械化的方法如 get, set, equals, toString 及有参无参构造函数。(依赖的安装见 5.3 节) backend.pojo.User Data NoArgsConstructor AllArgsConstructor public class User {private Integer id;private String username;private String password; }1.3 mapper层的实现 本来是需要写大量的 SQL 语句但 MyBatis-plus 已经帮我们实现好了 SQL 语句只需要通过 extends BaseMapperclass_name 集成过来就行因此不需要写任何 SQL 语句了。 注意 mapper 是一个 interface 而不是 class。 Mapper public interface UserMapper extends BaseMapperUser { }1.4 controller层调试CRUD 实现完 pojo, mapper 后可以在 controller 层中进行一些调试工作。 让我们测试一下上面的 pojo, mapper学习一下怎么调用 MyBatis-plus 实现的接口。 RestController 的作用可以理解成把返回值转成 Json 格式再返回给前端。 下面的CRUD操作仅为了演示怎么调试及使用接口实际实现某个业务功能的时候有另外的写法而且一般在 service 实现。 之后在 http://localhost:3000/.../ 中就能查看是否正确取得结果。 注意是自己设置的后端端口号resources/application.properties 中 server.port3000 设置。 RestController public class UserController {AutowiredUserMapper userMapper;RequestMapping(/user/all/)public ListUser getAllUser() {return userMapper.selectList(null);}RequestMapping(/user/{userId}/)public User getUser(PathVariable int userId) {return userMapper.selectById(userId);}// 可以通过封装 queryWrapper 实现复杂一点的操作这边的功能和上一个函数相同RequestMapping(/user/querywrapper/{userId}/)public User getQueryUser(PathVariable int userId) {QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.eq(id, userId);return userMapper.selectOne(queryWrapper);}// 查询符合多个条件的直接在 queryWrapper 后面加条件即可RequestMapping(/user/querywrapper/{userId})public ListUser getSomeUser(PathVariable int userId) {QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.gt(id, 1).le(id, 2);return userMapper.selectList(queryWrapper);}RequestMapping(/user/add/{userId}/{username}/{password}/)public String addUser(PathVariable int userId,PathVariable String username,PathVariable String password) {User user new User(userId, username, password);userMapper.insert(user);return add success;}RequestMapping(/user/delete/{userId}/)public String deleteUser(PathVariable int userId) {userMapper.deleteById(userId);return delete success;} }2 Spring Security 2.1 Spring Security 介绍 某些功能需要用户拥有权限才能访问因此需要加上授权机制进行权限判断安装依赖 spring-boot-starter-security 把 Spring 实现好的模块集成进来。会发现上面的操作都需要登录之后才能运行默认用户名为 user密码在 IDEA 的控制台中输出。 2.2 Spring Security 对接数据库 现在有个问题就是只能用它默认的用户进行登录我们希望通过数据库里的用户进行登录因此需要对 Spring Security 进行配置。 实现 service.impl.UserDetailsServiceImpl 类继承 UserDetailsService 接口用来接入数据库信息。 重写其 loadUserByUsername 方法该方法是通过 username 找到对应用户的 username, password (UserDetails 类型)。 service.impl.UserDetailServiceImpl Service public class UserDetailsServiceImpl implements UserDetailsService {Autowiredprivate UserMapper userMapper;Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {QueryWrapperUser queryWrapper new QueryWrapper();queryWrapper.eq(username, username);User user userMapper.selectOne(queryWrapper);if (user null) throw new RuntimeException(用户不存在);return new UserDetailsImpl(user);} }实现辅助类 UserDetailsImpl该类实现接口 UserDetails该接口的所有方法在 Spring Security 中指出。 这下就可以通过数据库中用户来登录但是要先把用户密码改成 {noop}password 形式告诉数据库当前字段是未加密的明文。 2.3 密码的加密 这里就有一个很有趣的问题为什么忘记密码之后只能重置密码不能知道原来的密码是什么呢 这是由于密码的加密算法是一个单向的加密反向解码基本不可能。注册存储的是加密后的密码 encoded_pw_save每次用户登录输入密码 pw调用 match 方法看 pw, encoded_pw_save 是否匹配如果匹配则成功登录。当用户忘记密码之后数据库存储的是加密后的密码无法解密出原来的密码因此只能重置。 config.SecurityConfig Configuration EnableWebSecurity public class SecurityConfig {Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();} } 之后可以在 test 中的 BackendApplicationTests 中进行一些测试首先输出一下 pw 对应的加密后密文(每次加密结果可能不同)再看一下 pw, encoded_pw 能否匹配结果为 True之后把数据库中的密码修改成密文格式就能够正常登陆了(加 {noop} 也无法登录只能密文登录)。 void contextLoads() {PasswordEncoder passwordEncoder new BCryptPasswordEncoder();System.out.println(passwordEncoder.encode(pw));System.out.println(passwordEncoder.matches(pw, encoded_pw)); }之后可以修改 controller 添加用户 addUser 的实现直接存储加密之后的密码。 RequestMapping(/user/add/{userId}/{username}/{password}/) public String addUser(PathVariable int userId,PathVariable String username,PathVariable String password) {PasswordEncoder passwordEncoder new BCryptPasswordEncoder();String encodedPassword passwordEncoder.encode(password);User user new User(userId, username, encodedPassword);userMapper.insert(user);return add success; }3 Jwt验证 3.1 传统Session验证方式 所有的页面可以简单的分为2种一种是公开页面也就是不登录就可以查看的页面(比如 login, register, homepage)一种是授权页面也就是需要登录才能查看的页面(其实可以细分成有某个页面的权限才能访问等但我们的项目暂时用不到这么细致)。 那么问题就来了Server 如何判断某个用户是否能够访问某个授权页面呢 首先先介绍一下传统的 Session 验证方式(如下图所示)仅做了解并不使用这种方式。 3.2 Jwt验证方式 对于最近的应用都会遇到跨域问题通常有多个端(Web, App ...)或是多个服务器这时 Session 方式就难以处理。 每个服务器都要存下用户的 SessionID。 因此提出新的验证方式: Jwt验证这个方式的优点在于: 方便处理跨域问题不需要在 Server 端存储信息。 3.3 Jwt验证所需的配置 ​ Jwt验证所需配置具体实现代码 安装依赖 jjwt-api, jjwt-impl, jjwt-jackson。实现 utils.JwtUtil 类作用是创建和解析 jwt_token。实现 config.filter.JwtAuthenticationTokenFilter 类用来验证 jwt_token如果验证成功则提取 user 信息到 Spring Security 的上下文中(存储用户的认证信息全局共享)以便进行后续的权限验证。配置 config.SecurityConfig 类放行登录、注册等接口。 4 用户注册和登录模块的实现 4.1 用户注册和登录 API 的实现 首先更改一下用户 user 的属性id 改成自增在 pojo 中为 id 添加注解 TableId(type IdType.AUTO)。 一共需要实现3个 API: /user/account/token/: 验证用户名密码验证成功后返回 jwt_token/user/account/info/: 根据 jwt_token 返回用户信息/user/account/register/: 注册账号。 SpringBoot 实现 API 可以分为 service 声明接口service.impl 实现接口的具体逻辑controller 接收前端 url。 4.1.1 /user/account/login/ service.user.account.loginService 一般习惯上返回一个 MapString, String 接收各种信息key 要和前端对应起来。 public interface LoginService {MapString, String login(String username, String password); }service.impl.user.account.loginServiceImpl 先封装明文的用户名密码再验证用户是否登录(失败则进行自动处理)之后取出用户 user最后把 user_id 封装成 jwt_token。 记得加上 Service 的注解。 Service public class LoginServiceImpl implements LoginService {Autowiredprivate AuthenticationManager authenticationManager;Overridepublic MapString, String login(String username, String password) {UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(username, password);// 如果认证失败(例如用户名或密码不正确)此方法将抛出一个异常Authentication authenticate authenticationManager.authenticate(authenticationToken);UserDetailsImpl loginUser (UserDetailsImpl) authenticate.getPrincipal();User user loginUser.getUser();String jwt JwtUtil.createJWT(user.getId().toString());MapString, String map new HashMap();map.put(error_message, success);map.put(token, jwt);return map;} } controller.user.account.LoginController 密码需要用密文形式传输而 get 请求是明文传输因此 用post 请求同时要记得把 /user/account/token/ 的 url 放行。 这里传入的参数就是前端传过来的参数以 map 形式传入接口传入的参数需要把对应的 key 抽出来再传入。 记得加上 RestController 注解。 RestController public class LoginController {Autowiredprivate LoginService loginService;PostMapping(/user/account/token/)public MapString, String getToken(RequestParam MapString, String map) {String username map.get(username);String password map.get(password);return loginService.getToken(username, password);} }在前端中可以对 /user/account/token/ 进行调试resp 就是 LoginServiceImpl 中返回的 MapString, String在前端浏览器中的控制台可以查看结果。 $.ajax({url: http://localhost:3000/user/account/token/,type: post,data: {username: user_1,password: p1,},success(resp) {console.log(resp);},error(resp) {console.log(resp);}});4.1.2 /user/account/info/ /user/account/register/ 另外两个接口 API 的流程就不再赘述了过程是一样的只介绍一下具体逻辑 ServiceImpl 的实现Controller 中的请求为 Get, Post 也要记得区分修改删除添加一般都是 Post获取一般是 Get。 service.impl.InfoServiceImpl 这边根据 token 取出 user 的代码直接背过(实现是基于之前 3.3 节的配置)之后但凡要取出 user 直接复制这段代码。 Service public class InfoServiceImpl implements InfoService {Overridepublic MapString, String getinfo() {UsernamePasswordAuthenticationToken authentication (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();UserDetailsImpl loginUser (UserDetailsImpl) authentication.getPrincipal();User user loginUser.getUser();MapString, String res new HashMap();res.put(error_message, success);res.put(id, user.getId().toString());res.put(username, user.getUsername());return res;} }前端测试代码需要在请求报文的表头带上自己的 jwt_tokentoken 根据第一个 api 获取: 这边是需要登录之后得到 jwt_token 才能获取 user也就是说需要授权因此需要加上报文头 headersjwt_token 头部的内容在 config.filter.JwtAuthenticationTokenFilter 中定义以此验证 jwt_token。 $.ajax({url: http://localhost:3000/user/account/info/,type: get,headers: {Authorization: Bearer jwt_token,},success(resp) {console.log(resp);},error(resp) {console.log(resp);}});对于 /user/account/register/ 注册的实现需要判断用户名和密码是否合法用户名是否已存在两次密码是否一致等等最后再把用户添加到数据库由于密码的加密数据库的查询和添加等操作在前面已经介绍过了请大家尝试自行实现以验证是否理解之前内容。 4.2 用户登录模块的前端 4.2.1 登录页面和 Login 函数及全局变量 user 的定义与使用 用户注册和登录的前端页面的实现(html)就不介绍了自行去 Bootstrap 搜索喜欢的样式改一改就行。 登陆之后需要记录当前用户 user这里介绍一下全局变量 user 的定义和使用可以使用 vuex 使用全局变量。 store.index 需要在 index.js 中把 user 导入到 modules。 import MoudleUser from ./userexport default createStore({/* ... */modules: {user: MoudleUser,} })store.user 简单的来说 state 定义 user 的属性mutations 定义更新 state 属性的函数actions 定义需要修改 state 属性的相关函数。 import $ from jqueryexport default {state: {id: ,username: ,photo: ,token: ,is_login: false,},getters: {},mutations: {updateUser(state, user) {state.username user.username;state.id user.id;state.photo user.photo;state.is_login user.is_login;},updateToken(state, token) {state.token token;}},actions: {login(context, data) {$.ajax({url: http://localhost:3000/user/account/token/,type: post,data: {username: data.username,password: data.password,},success(resp) {if (resp.error_message success) {context.commit(updateToken, resp.token);data.success(resp);} else {data.error(resp);}},error(resp) {data.error(resp);}});}},modules: {} }接下来就需要在登录的时候调用 login 函数即可介绍一下如何调用和传入参数。 views.user.account.UserAccountLoginView 以下仅展示核心部分代码首先表单提交时要绑定触发 login 函数(并不是 store 中的 login)另外要将变量 ref 和输入框 input 进行绑定报错信息通过 {{ error_message }} 绑定。 在自定义的表单提交后触发的 login 中要调用 store.user 中 action 的函数 store.dispatch(函数名, data)这样之后就能获得用户的 jwt_token在成功登录之后需要跳转到主页面。 修改 ref 变量时要记得通过 .value 修改值。 form submit.prevent logindiv classmb-3label forusername classform-label用户名/labelinput v-model username typetext idusername classform-control placeholder输入用户名/divdiv classmb-3label forpassword classform-label密码/labelinput v-model password typepassword idpassword classform-control placeholder.../divdiv classerror-message{{ error_message }}/divbutton typesubmit classbtn btn-primary提交/button /formscript import { useStore } from vuex import { ref } from vue import router from /router/indexexport default {setup() {const store useStore();let username ref();let password ref();let error_message ref();const login () {error_message.value ;store.dispatch(login, {username: username.value,password: password.value,success() {router.push({ name: home });},error() {error_message.value 用户名或密码错误;}});};return {username,password,error_message,login}} } /script4.2.2 获取 user 信息和退出功能 在 store.user 的 action 中实现函数 getinfo在登录成功之后获取用户信息(在 login 成功后调用 getinfo)。 ...resp 作用是解析 key, value 到当前对象。 getinfo(context, data) {$.ajax({url: http://localhost:3000/user/account/info/,type: get,headers: {Authorization: Bearer context.state.token,},success(resp) {if (resp.error_message success) {context.commit(updateUser, {...resp,is_login: true,});data.success(resp);} else {data.error(resp);}},error(resp) {data.error(resp);}}); }const login () {error_message.value ;store.dispatch(login, {username: username.value,password: password.value,success() {store.dispatch(getinfo, {success() {console.log(store.state.user);router.push({ name: home });},error(resp) {console.log(resp);}});},error() {error_message.value 用户名或密码错误;}}); }之后可以更改导航栏信息 NavBar如果已登录则显示用户名否则显示登录和注册按钮。 ul classnavbar-nav v-if $store.state.user.is_loginli classnav-item dropdowna classnav-link dropdown-toggle href# rolebutton data-bs-toggledropdown aria-expandedfalse{{ $store.state.user.username }}/aul classdropdown-menurouter-link classdropdown-item :to {name: user_bot_index}Bots/router-linklihr classdropdown-divider/lilia classdropdown-item href#退出/a/li/ul/li /ulul classnavbar-nav v-elseli classnav-itemrouter-link class nav-link :to {name: user_account_login} rolebutton登录/router-link/lili classnav-itemrouter-link class nav-link :to {name: user_account_register} rolebutton注册/router-link/li /ul 之后实现一下 logout 退出功能和之前的流程一样只需要把所有 state 清空即可请自行实现验证之前的内容是否理解掌握。注册页面的实现同样也不介绍了都是一样的流程请自行实现。 4.3 前端页面授权 当用户未登录时如果访问授权页面则重定向到登录页面强制要求用户登录在 router 中实现该功能。 beforeEach 是进到每个页面之前要执行的函数。 import store from ../store/indexconst routes [{path: /,name: home,redirect: /pk/,meta: {requestAuth: true,}},/* ... */{path: /:catchAll(.*),redirect: /404/}, ]router.beforeEach((to, from, next) {if (to.meta.requestAuth !store.state.user.is_login) {next({ name: user_account_login });} else {next();} });4.4 登录状态持久化及小问题处理 每次刷新的时候登录状态都会重置为“未登录状态”这是因为 jwt_token 是存储在内存中因此要把 token 存储在浏览器的 localstore 硬盘中。 首先修改 store.user 中的登录和退出函数分别将 jwt_token 在本地保存和删除。 login(context, data) {/* ... */success(resp) {if (resp.error_message success) {localStorage.setItem(jwt_token, resp.token);context.commit(updateToken, resp.token);data.success(resp);} else {data.error(resp);}},}); }, logout(context) {localStorage.removeItem(jwt_token);context.commit(logout); }之后每次进入到登录页面时先判断本地是否存在 jwt_token如果存在再判断 token 是否已经过期(调用 state.dispatch(getInfo)如果成功则说明未过期)如果未过期则跳转到首页不需要重新登录。 至此就实现了登录状态的持久化每次刷新都会跳转到首页并且保持登录状态。细心的朋友会发现每次刷新都会闪一下登录页面视觉效果不太好因此 user 中再维护一个全局变量 is_pulling_info通过这个变量设置默认先不让登录页面的组件显示 v-if $store.state.user.is_pulling_info如果未登录再显示。 登录页面添加以下代码: const jwt_token localStorage.getItem(jwt_token); if (jwt_token) {store.commit(updateToken, jwt_token);store.dispatch(getinfo, {success() {router.push({ name: home });store.commit(updatePullingInfo, false);},error() {store.commit(updatePullingInfo, false);},}); } else {store.commit(updatePullingInfo, false);console.log(store.state.user.is_pulling_info); }5 配置MySQL 5.1 MySQL的基本结构 MySQL 中包含多个数据库 databases每个 database 含有多个表 tables (每张表 table 可以理解成 Java 中的 class 类)。 MySQL 级别相关操作: 启动 MySQL (需要管理员权限): net start mysql80 链接 Mysql: mysql -u root -p 以下操作仅做简单介绍在实际写项目的时候由于是使用 MyBatis-plus因此不会用到这些 SQL 语句。 database 级别相关操作: 创建数据库 create database kob_db; 删除数据库 drop database kob_db; 进入数据库 use kob_db; 查看 MySQL 中所有数据库 show databases; table 级别相关操作: (要先进入需要的 database) 创建表 create table user(id int, username varchar(100), password varchar(100)); 删除表 drop table user; 查看当前数据库中所有表 show tables; row 级别相关操作: 增: insert into user values(1, yukiii, 123); 删: delete from user where id 1; 查: select * from user; select password from user where id 1; 5.2 IDEA中配置MySQL IDEA 中配置 MySQL 数据库 kob_db 的步骤如下: 从 MySQL 中选择要连接的数据库 填写用户密码选择要连接到 kob_db 数据库架构选择默认架构 测试连接成功后则 IDEA 中配置成功 5.3 Spring使用MySQL需要的依赖 打开Maven仓库地址在搜索栏搜索要装的依赖再将依赖复制到 pom.xml 的 dependencies。 需要安装的依赖: Spring Boot Starter JDBC, Project Lombok, MySQL Connector/J, mybatis-plus-boot-starter, mybatis-plus-generator依赖解析的会比较久 在 pom.xml 添加完后会发现报红需要在右侧的 Maven 选项中重新加载所有依赖项。 SpringBoot 在访问 MySQL 时也需要用户名和密码在 resources/application.properties 添加以下代码: 用户名和密码填写自己的 MySQL 用户url 中换成 MySQL 端口和数据库名。 spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.urljdbc:mysql://localhost:3306/kob_db?serverTimezoneAsia/ShanghaiuseUnicodetruecharacterEncodingutf-8 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver5.4 图形化使用MySQL 对某个表中记录的 CRUD 操作都在图形化界面中提供每个操作之后都要记得点击上传更新到数据库中。 对表进行修改的操作在右侧右键要修改的表可以跳出相应界面。
http://www.zqtcl.cn/news/727144/

相关文章:

  • 网站开发使用哪种语言wordpress 免费主机
  • 山东免费网站制作绿色食品网站模板
  • 做搜狗网站优化点广州网站开发人
  • 网站建设违法行为广东seo快速排名
  • 体育彩票网站开发该做哪些步骤深圳网站建设策划方案
  • 金华网站建设电话做网站用虚拟机还是服务器
  • 整容医院网站建设目的顺企网贵阳网站建设
  • 微网站 htmlseo做的好的网站
  • 免费做网站推荐东平网页设计
  • 所有复刻手表网站wordpress 标题简码
  • 云南建设厅建设网站首页网站建设s
  • 网站用户需求报告网站充值怎么做的
  • 找代码的网站有一个网站是做釆购的是什么网
  • 做外贸最好的网站有哪些php网站开发工程师待遇
  • 做推文封面的网站首页>新闻>正文 网站怎么做
  • 黄页推广引流网站企业网站导航菜单
  • 合肥专门做网站的公司广告代理商是什么意思
  • wordpress显示一个类目seo推广
  • 营销型电子商务网站特点如何申请免费空间和域名
  • 网站建设 主要学是么vk汉化网站谁做的
  • 做英文网站费用多少学校网站开发毕业设计
  • 红动中国设计网站官网网页制作的论文
  • 云阳一平米网站建设西安设计工作室推荐
  • 网站长尾关键词优化网页设计定制代理
  • 海东电子商务网站建设运城市网站建设公司
  • 网站建设得要素电子商务网站建设与维护项目五
  • 网站备案无前置审批文件南宁市建设厅网站
  • 百度网站体检手机网页小游戏
  • 大型购物网站建设费用广告设计与制作软件有哪些
  • 郑州建设工程交易中心网站汉寿做网站的公司