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

苍穹外卖资源点整理+个人错误解析-Day06-微信登陆、商品浏览

一.HttpClient

介绍

可以理解为zaijava程序中通过httopclient这个工具包可以来构造http请求,并且可以来发送http请求。

使用的话需要导入maven坐标:

核心API

发送请求步骤:

创建httpclient对象

创建htp请求对象

调用httpclient的execute方法发送请求

入门案例

注意的是如图,即使不装配httpclientmaven坐标也可以使用的原因就是这个。因为阿里云的oss就是基于httpclient

发送GET请求

分为以下几步:
1.创建HttpClient对象

2.创建请求对象

3.发送请求

4.获取服务端返回的状态码判断请求成没成功

5.获得响应回来的数据

6.因为数据封装成了HttpEntity,需要使用工具类进行解析

7.关闭资源

public void  testGET() throws IOException {//创建HttpClient对象:HttpClients.createDefault().varCloseableHttpClient httpClient= HttpClients.createDefault();////创建请求对象:ew HttpGet().var,()写请求的地址HttpGet httpGet = new HttpGet("http://localhost:8080/user/shou/status");//发送请求:httpClient.execute(httpGet).varCloseableHttpResponse response = httpClient.execute(httpGet);//获取服务端返回的状态码判断请求成没成功int statusCode =response.getStatusLine().getStatusCode();System.out.println("服务端返回的状态码为:"+statusCode);//获得响应回来的数据:response.getEntity().varHttpEntity entity = response.getEntity();//因为数据封装成了HttpEntity,需要使用工具类进行解析:String body = EntityUtils.toString(entity);System.out.println("服务端返回的数据为:"+body);//关闭资源response.close();httpClient.close();
}

进行测试运行后可能会报错,报500的话先试着启动redis。

发送POST请求

步骤如下

1.创建HttpClient对象

2.创建请求对象

3.因为post请求需要提交请求参数,还是json形式,所以需要封装参数

        3.1.设置请求参数 

httpPost.setEntity(entity);

        3.2.由于setentity方法需要HttpEntity这么一个对象,但是HttpEntity是一个接口类,所以需要使用实现类:StringEntity,进行构造StringEntity对象:

StringEntity entity = new StringEntity("");

        3.3. new StringEntity("");需要传入一个字符串对象,而这个字符串应该是一个json,需要构造json字符串:可以写成”username:‘’,password:''“,但不推荐

        3.4通过jsonobject进行json字符串构建:

JSONObject jsonObject =new JSONObject();
jsonObject.put("username","admin");
jsonObject.put("password", "123456");

        3.5.指定传入字符串格式以及编码

//指定编码方式
entity.setContentEncoding("utf-8");
//指定数据格式
entity.setContentType("application/json");

4.发送POST请求

5.解析返回结果

6.guan'bi'zi'yuan

完整代码:
 

@Test
public void testPOST() throws Exception {//创建HttpClient对象CloseableHttpClient httpClient= HttpClients.createDefault();//创建请求对象HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");//因为post请求需要提交请求参数,还是json形式,所以需要封装参数JSONObject jsonObject =new JSONObject();jsonObject.put("username","admin");jsonObject.put("password", "123456");StringEntity entity = new StringEntity(jsonObject.toString());//指定编码方式entity.setContentEncoding("utf-8");//指定数据格式entity.setContentType("application/json");httpPost.setEntity(entity);//发送POST请求CloseableHttpResponse response = httpClient.execute(httpPost);//解析返回结果int statusCode = response.getStatusLine().getStatusCode();System.out.println("响应码为:"+statusCode);HttpEntity entity1 = response.getEntity();String body = EntityUtils.toString(entity1);System.out.println("响应结果为"+body);//关闭资源response.close();httpClient.close();
}

二.微信小程序开发

1.微信小程序介绍

网址:https://mp.weixin.qq.com/cgi-bin/wx

注册范围:

开发支持:

接入流程:

2.准备工作

注册

注册完毕后进行填写:

左侧找到开发管理——开发设置

找到APPID与小程序密钥:

下载微信小程序开发的开发者工具:

在微信开发者工具中编辑代码

3.入门案例(本地设置将本地调试库版本修改到2.27及以下)

了解小程序目录结构

而每个界面的组成:

编写小程序代码

index.wxml中显示:

动态显示数据:

index.wxml:

  <view class="container">

  <view>

  <!-- 插值表达式{{}}。动态显示界面 msg这个变量需要声明,在index.js文件中声明-->

  {{msg}}

  </view>

  </view>

获取当前微信用户头像,昵称:

 <view>

  <button type="default">获取用户信息</button>

  </view>

会出现按钮:
但这个是没有 type="default的显示效果,有default:

type=primary:

type=warm:

获取用户信息:

在index中写出代码:

 //获取微信用户头像以及昵称

  getUserInfo(){

    //这个方法是获取用户信息

wx.getUserProfile({

  desc: '获取用户信息',

  //回调函数

   success :(res)=>{

    //打印一下,在调试器的console进行输出

    console.loge(res.userInfo)

  }

})

index.xwml:

 {{msg}}

  </view>

  <view>

  <button bindtap="getUserInfo" type="default">获取用户信息</button>

  </view>

接下来将获取的用户信息(头像昵称)输出到小程序开头显示:

   

index.wxml:

 <view class="container">

  <view>

  {{msg}}

  </view>

  <view>

  <button bindtap="getUserInfo" type="default">获取用户信息</button>

  昵称:{{nickName}}

  <image style="width: 100px;height: 100px;" src="{{url}}"></image>

  </view>

  </view>

index.js:

Page({

  data:{

    msg:'hello',

    nickName:'',

    url:''

  },

  //获取微信用户头像昵称

  getUserInfo(){

    wx.getUserProfile({

      desc: '获取微信用户信息',

      //回调函数

      success:(res) =>{

        console.log(res.userInfo)

        //为数据赋值

        this.setData({

          nickName:res.userInfo.nickName

          url:res.userInfo.avatarUrl

        })

      }

    })

  }

})

接下来就是获取微信登录用户的唯一标识(openid):

获取用户授权码:

index.wxml:

 <view>

    <button bind:tap="wxLogin" type="warn">微信登陆</button>

    授权码:{{code}}

  </view>

index.js:

wxlogin({

wx.login({

  success: (res) => {

    console.log(res.code)

    this.setData({

      code:res.code

    })

在小程序中如何发布异步请求:

 <view>

   <button bind:tap="sendRequest" type="default">发送请求</button> 

//发送请求

sendRequest(){

wx.request({

  url: 'http://localhost:8080/user/status',

  method:'GET'

  success:(res)=>{

console.log(res.data)//这里的data是固定写法,代表的是后端响应回来的整个json数据,数据格式为{code:'',msg:'',data:{}}

  }

})

}

后端也成功进行响应:

编译小程序

三.微信登陆

导入小程序代码

微信开发者工具中找到导入,进行项目导入:

微信登录流程

想要实现微信登录,首先是在小程序端调用wx.login方法进行登录,并获得一个code(授权码),随机调用wx.request方法发送code,并对后端服务层进行请求,请求内容为自己编写的后端服务。

后端服务获得code,并调用微信接口服务,在调用时需要传输appid+appsercet+code这三个参数。

那么如何做到从后端调用微信接口服务?

使用HttpClient发送http请求并传输参数,最终可以调用为微信接口服务。

然后微信接口服务给后端服务器传输session_key+openid(微信用户唯一标识)等参数。

并且拿到openid之后,我们可以自定义登录状态,即记录当前微信用户的一些信息,比如将用户的openid存进数据库,并给用户产生一个token.

后端将自定义登录状态再传输给小程序,小程序也可以对传进来的数据(token)进行存储即storage中.

后续发起的各种业务请求携带的token再发送给服务端,服务端解析token,允许进行业务操作。

进行小程序操作前,先启动项目以及redis.

在重新编译小程序,进行登录时报错:

报错原因:可能是基础库版本太高,我调成了2.26再进行尝试:

成功弹出登录:

进行接口测试:请求网址:

https://api.weixin.qq.com/sns/jscode2session

成功:

分析

产品原型:

因为是微信登录,直接进行微信登录。通过获得微信用户的唯一标识来确认用户。新用户不需要自己注册,自动完成注册。

接口设计:

发现路径是/user/user/login。

为什么会有两个user:

第一个user代表的是用户端发送过来的请求

第二个user代表的是用户模块

代码开发

先在服务端进行配置微信登录所需配置项:

为什么要进行配置,是因为要实现微信登录的话,就需要在程序中去调用微信的接口服务,而调用接口服务就需要将appid以及sercet作为参数传过去。

然后配置为微信用户生成jwt令牌的配置:

管理端生分令牌与为用户端的这些微信用户生成令牌是分开算的:

这是管理端

这是微信用户端

设计接口所对应的dto以及vo:

dto:

@Data
public class UserLoginDTO implements Serializable {private String code;}

vo:

public class UserLoginVO implements Serializable {private Long id;private String openid;private String token;}
进行控制层开发:
  @PostMapping("login")@ApiOperation("微信登录")public Result<UserLoginDTO> login(@RequestBody UserLoginDTO userLoginDTO){log.info("微信登录:{}",userLoginDTO);
User user=service.wxlogin(userLoginDTO);

代码中为什么泛型使用的是userloginvo,而接收参数使用的是dto

首先明确VO,DTO定义上的区别。

1. DTO (Data Transfer Object) - 数据传输对象

定义: DTO 是一个专门用来在不同层(比如前端和后端)之间传输数据简单对象。它只包含需要传输的字段,不包含业务逻辑。

2. VO (View Object) - 视图对象

定义: VO 是一个专门用来封装后端返回给前端界面展示的数据的简单对象。它是为了满足前端“视图”(页面)的展示需求而设计的。

而接收参数使用的是dto,dto中只有一个属性code,即微信唯一标识码。根据接口分析,前端会传递一个code的字符串,所以我们使用dto接收。

通过接收到的code,我们最终要返回给前端的数据就是vo类型(查看接口设计)

然后需要生成jwt令牌,但是生成令牌的前提是需要使用微信用户的相关信息,所以提供登录方法并返回用户对象:

User user=service.wxlogin(userLoginDTO);

为什么要封装成user类:

1.输入参数 UserLoginDTO  只包含一个简单的 code 字段,而微信登录过程需要获取和处理更多用户信息,所以wxlogin方法内部需要调用微信API,将临时授权码(code)转换为包含openid等信息的完整用户数据。

2.User 实体类包含了用户的完整信息(id、openid、姓名、手机号等),便于后续业务处理,便于后续操作,如保存到数据库或从中提取必要字段生成令牌

3. 后续处理需要,控制器后续需要从User对象中提取 id 字段来生成JWT令牌,并且最终需要根据User对象构建 UserLoginVO 返回给前端,包含id、openid和token信息。

紧接为微信用户生成jwt令牌,使用的是jwtutils工具包的createJWT方法:

而前面两个参数已经配置到配置文件了,现在需要获取值

而这两个参数被封装进了JwtProperties类中并且被componnent注解,说明可以自动注入:

控制层代码:

JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims)

没有claim对象,进行局部创建:

//为微信用户生成令牌Map<String, Object> claims = new HashMap<>();claims.put("userId",user.getId());JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims)
}

claims.put的作用:
- Claims(声明) :是JWT(JSON Web Token)中的核心部分,用于存储自定义的用户信息
- 在代码中, claims 是一个 Map<String, Object> 对象,用于存储键值对形式的用户信息
- claims.put(key, value) 方法用于向这个Map中添加一条声明信息
具体作用分析
- 存储用户标识 :将用户的唯一ID( user.getId() )以键名 "userId" 存入claims中
- 关联用户身份 :这使得生成的JWT令牌与特定用户绑定
- 便于后续验证 :当用户后续请求需要身份验证时,可以从令牌中提取这个用户ID

以上步骤我们获得了前端传入code的id,openid,以及token,可以将这些封装到vo类并返回给前端:

String token= JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);
UserLoginVO userLoginVO = UserLoginVO.builder().id(user.getId()).openid(user.getOpenid()).token(token).build();
return Result.success(userLoginVO);

controller完整代码

@Autowired
private UserService service;
@Autowired
private JwtProperties jwtProperties;//微信登录@PostMapping("login")@ApiOperation("微信登录")public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){log.info("微信登录:{}",userLoginDTO);
User user=service.wxlogin(userLoginDTO);//为微信用户生成令牌Map<String, Object> claims = new HashMap<>();claims.put("userId",user.getId());String token= JwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),claims);UserLoginVO userLoginVO = UserLoginVO.builder().id(user.getId()).openid(user.getOpenid()).token(token).build();return Result.success(userLoginVO);}
进行实现类开发:

首先,我要实现微信登录,如果是新用户还得自动注册。

与之前用户名密码的登录逻辑不一样,之前的登录逻辑是用户名密码登录,查询自己的用户表检查用户密码是否正确,并且新用户需要自己注册。

而这个微信登录,需要调用微信服务器的接口来获得当前微信用户的openid。

1.调用微信接口获取openid:

微信接口为:https://api.weixin.qq.com/sns/jscode2session

那么如何调用接口?

使用HttpClient向接口发送get请求,起哄httpclient被封装成了工具类:HttpClient。

第二个参数为map,其实就是我们请求的参数,创建map的局部变量:

Map<String, String> map=new HashMap<>();

根据微信开发者工具接口文档一共需要四个参数:

然后将需要给map集合传入这几个请求参数,其中appid与secret被封装进配置属性类,直接注入即可:

@Autowired
private WeChatProperties weChatProperties;
map.put("appid",weChatProperties.getAppid());
map.put("secret",weChatProperties.getSecret());
map.put("js_code",userLoginDTO.getCode());
map.put("grant_type","authorization_code");

请求发送出去之后需要接收它的返回结果:

String json=HttpClientUtil.doGet(WX_LOGIN,map);

而返回结果是返回的json数据包(文档里明确写了),我们需要数据包中的openid,需要从json字符串中解析出。

如何解析出来?使用JSON:

//将json中的openid对象解析出
JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString("openid");

2.判断openid是否为空,为空表示登录失败抛出登录异常:

if (openid==null){throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}

3.判断当前用户是否是新用户

需要创建UserMapper类:

进行判断查询是否为新用户就是查询库内有无openid:

//根据openid查询用户
@Select("select * from user where openid = #{openid}")
User getByOpenid(String openid);
User user = userMapper.getByOpenid(openid);
if (user==null){//如果是则自动完成注册user = User.builder().openid(openid).createTime(LocalDateTime.now()).build();//此处注册其他的都没有这没关系,性别什么的可以去个人中心写,不用在代码实现
userMapper.insert(user);
}

insertxml语句:

<insert id="insert"  useGeneratedKeys="true" keyProperty="id">insert into user (openid, name, phone, sex, id_number, avatar, create_time)values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})
</insert>

4.返回这个用户对象
return user;

写完之后其实发现第一部分代码模块相对来说是比较独立的,可以单独抽一个方法出来:

private String getOpenid(String code) {//1.将小程序发送的code发送给微信,得到一个access_token和openidMap<String, String> map=new HashMap<>();map.put("appid",weChatProperties.getAppid());map.put("secret",weChatProperties.getSecret());map.put("js_code",code);map.put("grant_type","authorization_code");String json=HttpClientUtil.doGet(WX_LOGIN,map);JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;
}

然后再引用这个方法即可:

  @Overridepublic User wxlogin(UserLoginDTO userLoginDTO) {
String openid = getOpenid(userLoginDTO.getCode());

完整userserviceimpl代码:

public class UserServiceImpl implements UserService {public static final String WX_LOGIN= "https://api.weixin.qq.com/sns/jscode2session";@Autowiredprivate WeChatProperties weChatProperties;@Autowiredprivate UserMapper userMapper;//微信登录@Overridepublic User wxlogin(UserLoginDTO userLoginDTO) {
String openid = getOpenid(userLoginDTO.getCode());//与之前的登录逻辑不一样。以前是用户名密码登录,需要查询自己的用户表然后检查用户名密码是否正确//而现在这个是微信用户,需要调用微信服务器的接口来获得当前微信用户的openid//        //1.调用微信接口服务,获取当前微信用户的openid
//        Map<String, String> map=new HashMap<>();
//        map.put("appid",weChatProperties.getAppid());
//        map.put("secret",weChatProperties.getSecret());
//        map.put("js_code",userLoginDTO.getCode());
//        map.put("grant_type","authorization_code");
//        String json=HttpClientUtil.doGet(WX_LOGIN,map);
//        //将json中的openid对象解析出
//        JSONObject jsonObject = JSON.parseObject(json);
//        String openid = jsonObject.getString("openid");//2.判断当前openid是否为空,为空则登陆失败抛出登录异常if (openid==null){throw new LoginFailedException(MessageConstant.LOGIN_FAILED);}//3.判断是不是新用户,拿openid去自己的用户表进行查询//进行查询是不是新用户就得需要一个usermapperUser user = userMapper.getByOpenid(openid);if (user==null){//如果是则自动完成注册user = User.builder().openid(openid).createTime(LocalDateTime.now()).build();//此处注册其他的都没有这没关系,性别什么的可以去个人中心写,不用在代码实现userMapper.insert(user);}//4.返回这个用户对象return user;}private String getOpenid(String code) {//1.将小程序发送的code发送给微信,得到一个access_token和openidMap<String, String> map=new HashMap<>();map.put("appid",weChatProperties.getAppid());map.put("secret",weChatProperties.getSecret());map.put("js_code",code);map.put("grant_type","authorization_code");String json=HttpClientUtil.doGet(WX_LOGIN,map);JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;}
}

测试

点击debug运行代码,打开redis,运行微信小程序,加入断点发现:

四.导入商品浏览功能代码

分析

分析一下,需要分类接口,查询菜品接口,查询套餐接口,根据套餐id查询套餐接口

代码导入

查询分类:
/*** 查询分类* @param type* @return*/
//type为012分别可以查询不同的分类
@GetMapping("/list")
@ApiOperation("查询分类")
public Result<List<Category>> list(Integer type) {List<Category> list = categoryService.list(type);return Result.success(list);
}
根据分类id查询菜品
@RestController("userDishController")
@RequestMapping("/user/dish")
@Slf4j
@Api(tags = "C端-菜品浏览接口")
public class DishController {@Autowiredprivate DishService dishService;public Result<List<DishVO>> list(Long categoryId){//在用户端,根据id查询菜品不仅仅是要查询出菜品,还得将口味查询出Dish dish=new Dish();dish.setCategoryId(categoryId);dish.setStatus(StatusConstant.ENABLE);List<DishVO> list =dishService.listWithFlavor(dish);return Result.success(list);}
//serviceList<DishVO> listWithFlavor(Dish dish);

在这段代码中,数据被添加到dishVOList的过程分为以下几个关键步骤:

1. 获取原始数据 :通过 dishMapper.list(dish) 查询得到原始的Dish对象列表dishList

2. 创建目标容器 : List<DishVO> dishVOList = new ArrayList<>() 创建一个空的DishVO列表,用于存储最终结果

3. 遍历转换过程 :通过增强for循环 for (Dish d : dishList) 遍历每个原始Dish对象:

为每个Dish对象创建对应的DishVO对象: DishVO dishVO = new DishVO()

使用 BeanUtils.copyProperties(d, dishVO) 将Dish对象的所有属性复制到DishVO对象中

查询与当前菜品关联的口味数据: List<DishFlavor> flavors =dishFlavorMapper.getByDishId(d.getId())

将口味数据设置到DishVO对象中: dishVO.setFlavors(flavors)

4. 添加到结果列表 :通过 dishVOList.add(dishVO) 将转换好的DishVO对象添加到dishVOList中

5. 返回完整结果 :循环结束后, return dishVOList 返回包含所有转换后数据的列表

//条件查询菜品以及口味,serviceimpl@Overridepublic List<DishVO> listWithFlavor(Dish dish) {//这一行代码的作用-// 调用Mapper层的 list() 方法,根据条件查询符合要求的菜品数据//- dish 对象作为查询条件,包含了过滤条件//- 返回的是数据库实体类 Dish 的列表,这些对象直接对应数据库表结构List<Dish> dishList = dishMapper.list(dish);//这一行代码作用是:  创建一个新的 DishVO 类型的列表,用于存储转换后的视图对象List<DishVO> dishVOList=new ArrayList<>();for (Dish d:dishList){DishVO dishVO = new DishVO();BeanUtils.copyProperties(d,dishVO);//根据菜品id查询对应的口味List<DishFlavor> flavors = dishFlavorMapper.getByDishId(d.getId());dishVO.setFlavors(flavors);dishVOList.add(dishVO);}return dishVOList;}
}
根据分类id查询套餐接口
//controller
@GetMapping("/list")
@ApiOperation("根据分类id查询套餐")
public Result<List<Setmeal>> list(Long categoryId) {Setmeal setmeal = new Setmeal();setmeal.setCategoryId(categoryId);setmeal.setStatus(StatusConstant.ENABLE);List<Setmeal> list = setmealService.list(setmeal);return Result.success(list);
}
/*** 条件查询* @param setmeal* @return*/
public List<Setmeal> list(Setmeal setmeal) {List<Setmeal> list = setmealMapper.list(setmeal);return list;
}

xml:
<select id="list" parameterType="Setmeal" resultType="Setmeal">select * from setmeal<where><if test="name != null">and name like concat('%',#{name},'%')</if><if test="categoryId != null">and category_id = #{categoryId}</if><if test="status != null">and status = #{status}</if></where>
</select>
根据套餐id查询关联的菜品接口
/*** 根据套餐id查询包含的菜品列表** @param id* @return*/
@GetMapping("/dish/{id}")
@ApiOperation("根据套餐id查询包含的菜品列表")
public Result<List<DishItemVO>> dishList(@PathVariable("id") Long id) {List<DishItemVO> list = setmealService.getDishItemById(id);return Result.success(list);
}
/*** 根据id查询菜品选项* @param id* @return*/
public List<DishItemVO> getDishItemById(Long id) {return setmealMapper.getDishItemBySetmealId(id);
}
/*** 根据套餐id查询菜品选项* @param setmealId* @return*/
@Select("select sd.name, sd.copies, d.image, d.description " +"from setmeal_dish sd left join dish d on sd.dish_id = d.id " +"where sd.setmeal_id = #{setmealId}")
List<DishItemVO> getDishItemBySetmealId(Long setmealId);
http://www.dtcms.com/a/598525.html

相关文章:

  • 此网站不支持下载视频怎么办鞍山58同城找工作 招聘
  • 湛江网警网站 优化 教程
  • ROS1基础入门:从零搭建机器人通信系统(Python/C++)
  • Scikit-learn 入门指南:从零到一掌握机器学习经典库(2025 最新版)
  • 做那个的网站谁有手机如何制作小程序
  • 自己做的网站把密码改忘了怎么办wordpress 表格 插件
  • 成都园林景观设计公司推荐南京seo培训
  • InvivoCrown丨艾美捷代理(货号:SIM0053)BioSIM抗人IL-13抗体(来瑞组单抗生物类似药)研究级探索2型免疫反应的精密研究
  • oracle数据库网站开发网站建设和成本
  • 太原网页设计厦门网站排名优化软件
  • 专题网站建设意义何在网址大全2345
  • 网站建设视频教程最新企业品牌文化建设学习网站
  • CI/CD集成工程师前景分析:与开发岗位的全面对比
  • 网站建设费计入哪个科目汕头网站优化公司
  • 智慧团建网站什么时候维护好中国招商平台
  • 自己做的网站访问不栅格化系统制作网页界面设计
  • 做定制网站多少钱四川网络推广公司哪家好
  • Paint Net(windows免费绘画软件) v5.1.10
  • 东莞网站建设方案表短链接生成源码
  • 翻墙到国外网站怎么做网页设计岗位职责
  • JavaEE入门--计算机是怎么工作的
  • GRPO(Group Relative Policy Optimization)
  • .net空网站做九九乘法表wordpress怎样上传主题
  • 网站建设包括备案吗方象科技服务案例
  • 从 ps -ef 之后怎么获得这个进程的更多信息
  • Tkinter Canvas 方案和OpenCV+PIL 方案的差别
  • 深入解析socket函数:从服务端创建到内核实现原理
  • 吴江住房城乡建设局网站广州专业建网站公司
  • 网站上的美工图片要怎么做做物理的网站
  • 找单位做网站需要注意什么手机建站程序源码