本文讲解讲解微信小程序开发的相关的内容。

这里假设我们已经通过微信开发者工具新建了项目。

获取用户地理位置

通过用户授权获取用户的地理位置信息,授权一次之后,下次不需要进行授权。

添加 wxml

<!--pages/index/index.wxml-->
<scroll-view class="scrollarea" scroll-y type="list">
  <view class="view1" bindtap="getLocation">点击获取用户位置</view>
  <view>latitude: {{latitude}}</view>
  <view>longitude: {{longitude}}</view>
</scroll-view>

上面有方法 getLocation,点击 点击获取用户位置 按钮,获取用户当前的经纬度,并在页面上展示出来。

添加 js

// pages/index/index.js

Component({
  data: {
    latitude: 0,
    longitude: 0
  },
  methods: {
    getLocation() {
      var that = this;
      wx.getLocation({
        type: 'gcj02',
        success (res) {
          const latitude = res.latitude
          const longitude = res.longitude
          that.setData({
            latitude: latitude,
            longitude: longitude
          })
        },
        fail: function (error) {
          wx.showModal({
            title: '授权提示',
            content: '需要获取您的地理位置信息,请打开设置页面手动授权。',
            showCancel: false,
            success: function (modalRes) {
              if (modalRes.confirm) {
                wx.openSetting({
                  success: function (settingRes) {
                    if (settingRes.authSetting['scope.userLocation']) {
                      console.log('用户已手动授权');
                    } else {
                      console.log('用户依然拒绝授权');
                    }
                  }
                });
              }
            }
          });
        }
       })
    }
  },
})

我们设置了对应的经纬度变量 - latitudelongitude。通过调用微信方法 wx.getLocation 来获取当前用户的经纬度信息。如果用户允许授权,则直接将获取到的经纬度进行赋值;如果用户拒绝授权,则来到 fail 的部分,我们调用弹窗方法 wx.showModal 对用户进行提示,跳转到用户手动授权地理位置的页面。如果用户依旧不进行授权,我们可以对用户的其他操作进行限制,比如,直接限制进入详情页面。

这里我们为了更加准确获到用户的地理位置信息,使用了 type: 'gcj02'

当然,只是通过配置上面的内容信息,我们还不能获取到用户的地理位置信息。我们还得添加点配置信息。

添加配置信息

我们需要在 app.json 文件中,添加下面的配置信息:

{
  # 其他内容
  "permission": {
    "scope.userLocation": {
      "desc": "你的位置信息将用于小程序位置接口的效果展示"
    }
  },
  "requiredPrivateInfos": ["getLocation"]
}

👌,这时候,我们点击 点击获取用户位置,进行允许后,就可以得到用户的位置信息了。

PS: 注意⚠️ 模拟器获取的用户位置不准确,真机上获取准确

获取用户 openId

获取用户的 openId 需要后端服务来配合。

什么是 OpenId? 当微信用户登录公众号或小程序时,微信平台为每一个微信登录者分配的一个唯一标识符号。

前端部分

我们封装接口请求,在 utils/request.js 文件中,添加内容:

function wxRequest(config) {
  // 其他内容
  // 服务器基地址
  let serverUrl = 'http://127.0.0.1:8080/';
  // 请求地址
  let url = serverUrl + config.url;

  return new Promise(function(resolve, reject) {
    // 调用微信的请求
    wx.request({
      url: url,
      data: data,
      // 返回的数据类型
      dataType: dataType,
      enableCache: false,
      enableHttp2: false,
      enableQuic: false,
      method: method,
      header: header,
      responseType: responseType,
      timeout: timeout,
      success: (res) => {
        console.log('requestData', res)
        if(res.cookies != null && res.cookies.length != 0) {
          wx.setStorageSync('sessionId', res.cookies[0])
        }
        resolve(res)
      },
      fail: (error) => {
        console.log('requestException', error)
        reject(error)
      }
    })
  })
}
exports.wxRequest = wxRequest

上面我们调用了本地的服务,并将其请求进行封装后导出。

因为,我们一进入小程序就获取用户的 openId,所以,我们在入口文件 ./app.js 中添加下面的内容:

// app.js
const app = getApp();
const httpRequest = require('./utils/request.js')
App({
  onLaunch() {
    /**
     * 检查微信用户是否已经登陆到后台服务器
     * 已经登陆的标记,数据库中存在 OPENID
     */
    let code = null;
    // 调用 login 接口
    wx.login({
      success: (res) => {
        code = res.code;
        // 得到登陆用户的临时 code
        wx.getUserInfo({
          success: (data) => {
            // 向开发者服务器发送请求
            let api = 'wx/getLoginCertificate'
            let config = {
              url: api,
              method: 'POST',
              data: {
                code: code
              }
            }
            let promise = httpRequest.wxRequest(config);
            promise.then(res => {
              let isHas = null;
              // 有没有完整的微信登陆者信息
              isHas = res.data == 0 ? false : true;
              app.globalData.isHasUserInfo = isHas
            }).catch(error => {
              console.log('fail ', error)
            })
          }
        })


      }
    })
  },
  globalData: {
    userInfo: null,
    isHasUserInfo: false
  }
})

在前端中,我们将用户登陆生成的随机的 code 等信息,发送给后端,由后端去请求相关的内容,返回 openId 等信息回来给前端。

我们用本地地址来调试接口,所以要在微信开发工具中设定允许本地地址调试。本地设置 -> “勾选” 不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书

后端部分

下面,我们来讲讲后端的部分。假设我们新建的项目是 com.wechat/wechatservice

后端的开发环境:

- java 版本 17
- spring boot 版本 3.2.1
- Postman - Version 8.12.1
- Navicat Premium - 16.0.12

我们安装的依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.13</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.modelmapper/modelmapper -->
<dependency>
    <groupId>org.modelmapper</groupId>
    <artifactId>modelmapper</artifactId>
    <version>3.2.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>


<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

其他相关的文件内容如下。

配置好 mapper,在入口文件 com.wechat.wechatservice/WechatServiceAppliation.java 中添加内容:

package com.wechat.wechatservice;

@SpringBootApplication
public class WechatServiceApplication {

  // 使用 model mapper
  @Bean
  public ModelMapper modelMapper() {
    ModelMapper modelMapper = new ModelMapper();
    modelMapper.getConfiguration()
            .setMatchingStrategy(MatchingStrategies.STRICT); // https://stackoverflow.com/questions/58838964/modelmapper-failed-to-convert-java-lang-string-to-java-lang-long
    return modelMapper;
  }

  public static void main(String[] args) {
    SpringApplication.run(WechatServiceApplication.class, args);
  }

}

这里我们使用过本地的数据库,所以在 resources/application.properties 中添加内容:

spring.datasource.url=jdbc:mysql://localhost:3306/todo_management
spring.datasource.username=root
spring.datasource.password=

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update

接着我们创建 bean 包,其下添加 WxUserInfo.java 内容,这里因为是 demo,我们怎么简单怎么来:

package com.wechat.wechatservice.bean;

@Getter
@Setter
public class WxUserInfo {
  String code;
}

添加 entity 包,其下添加 WxUserInfo.java 文件,添加内容:

package com.wechat.wechatservice.entity;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "wx_users") // 表名为 wx_users
public class WxUserInfo {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column(name = "open_id", nullable = false)
  private String openId;
}

新建 dto 包,在其下添加文件 WxUserInfoDto.java,其内容是 entity 中的映射,其内容如下:

package com.wechat.wechatservice.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class WxUserInfoDto {
  private String openId;
}

新建 repository 包,添加文件 WxUserInfoRepository.java,内容如下:

package com.wechat.wechatservice.repository;

public interface WxUserInfoRepository extends JpaRepository<WxUserInfo, Long> {
}

添加 util 方法,这样方便管理相关的微信小程序的参数,当然,你也可以放在 application.properties 文件中管理。

package com.wechat.wechatservice.utils;

public class WxUtil {
  private final static String APP_ID = "wx430***";
  private final static String APP_SECRET = "60**";
  private final static String WX_LOGIN_SEVER_URL = "https://api.weixin.qq.com/sns/jscode2session?appid={0}&secret={1}&js_code={2}&grant_type=authorization_code";
  // 获取微信服务请求地址
  public static String getWxServerUrl(String code) throws IOException {
    String url = MessageFormat.format(WX_LOGIN_SEVER_URL, new String[]{APP_ID, APP_SECRET, code});
    return url;
  }
}

再新建 HttpClient.java 文件,内容如下:

package com.wechat.wechatservice.utils;

public class HttpClientUtils {
  /**
   * GET 请求
   */
  public static String getRequest(String url) throws Exception {
    CloseableHttpClient httpClient = httpClient = HttpClients.createDefault();
    CloseableHttpResponse response = null;
    try {
      HttpGet httpGet = new HttpGet(url);
      response = httpClient.execute(httpGet);
      // 相应体
      HttpEntity entity = response.getEntity();
      if(entity != null) {
        // 格式化相应体
        return EntityUtils.toString(entity);
      }
    } catch (ClientProtocolException e) {
      throw e;
    } catch (IOException e) {
      throw e;
    } finally {
      response.close();
      httpClient.close();
    }
    return null;
  }
}

接着,我们就可以写服务了。新建 service 包,在其下面实现接口类 IWxService.java,内容如下:

package com.wechat.wechatservice.service;

import com.wechat.wechatservice.dto.WxUserInfoDto;

public interface IWxService {
  String getLoginCertificate(String code) throws Exception;

  WxUserInfoDto addWxUserInfo(WxUserInfoDto wxUserInfoDto);
}

然后,我们对接口类 IWxService.java 进行具体实现。在 service 包下新建包 impl,然后在其下新建 WxServiceImpl.java 文件,内容如下:

package com.wechat.wechatservice.service;

public interface IWxService {
    // 请求地址
    String code = wxUserDto.getCode();
    String requestUrl = WxUtil.getWxServerUrl(code);
    // 发送请求
    String response = HttpClientUtils.getRequest(requestUrl);
    // 格式化数据
    ObjectMapper objectMapper = new ObjectMapper();
    JsonNode jsonNode = objectMapper.readTree(response);
    String openId = jsonNode.get("openid").asText();
    // 转换数据格式
    UserDto userDto = new UserDto();
    userDto.setOpenId(openId);

    // 查找是否存在一条数据
    Optional<User> userExist = userRepository.findOneByOpenId(openId);

    if(userExist.isEmpty()) {
      // 转换为 entity
      User user = modelMapper.map(userDto, User.class);
      // 写入数据
      userRepository.save(user);
      return userDto;
    }
    return userDto;
}

最后,我们实现控制器,我们在 com.wechat.wechatservice 下新建包 controller,然后添加文件 WxAction.java,内容如下:

package com.wechat.wechatservice.controller;

@RestController
@RequestMapping("/wx")
@AllArgsConstructor
public class WxAction {
  private IWxService iWxService;

  // Get wetchat user open id
  @PostMapping("/getLoginCertificate")
  public String getLoginCertificate(@RequestBody WxUserInfo wxUserInfo) throws Exception {
    String result = this.iWxService.getLoginCertificate(wxUserInfo.getCode());
    return result;
  }
}

到此,我们可以通过使用 postman 进行接口调用,在 Navicat 中验证是否添加数据成功了。

小程序跳转

小程序的跳转,需要对方的小程序允许我们的小程序。这里假设我们已经有了跳转的条件了。

实现的跳转逻辑,我们需要获取到对方小程序的 appIdpath,才能知道往哪里跳。如下:

// 小程序跳转
jumpMiniProgram() {
  let wxAppId = this.data.wx_appid;
  let wxAppPath = this.data.wx_path;

  wx.navigateToMiniProgram({
    appId: wxAppId,
    path: wxAppPath,
    success(res) {
      console.log(res, 'success');
    },
    fail(err) {
      console.log(err, "error");
    }
  });
}

引入 vant

我们引入成熟的 UI 框架加快开发速度。这里我们选择了 vant

我们可以参考 vant weapp 小程序 - 快速上手 来集成。

构建 npm 的过程可能会报错。配置好文件后,需要关闭开发者工具,再打开。参考文件 构建npm时报错 NPM packages not found. Please confirm npm packages ......

完成构建之后,会多出一个文件夹 miniprogram_npm

分享功能

微信小程序的分享功能,着手:

  1. 分享给好友
  2. 分享到朋友圈

开启分享

onLaunch() {
    // 微信分享 - https://developers.weixin.qq.com/miniprogram/dev/api/share/wx.showShareMenu.html
    wx.showShareMenu({
      withShareTicket: true,
      menus: ['shareAppMessage', 'shareTimeline']
    })
}

分享给好友

在页面中添加,比如页面 pages/index/index.ts 页面中添加:

methods: {
  // 分享给好友
  onShareAppMessage() {
    return {
      title: "分享标题",
      path: "/pages/index/index",
      imageUrl: "/assets/imgs/share.png"
    }
  },
}

分享到朋友圈

在页面中添加,比如页面 pages/index/index.ts 页面中添加:

methods: {
  // 分享到朋友圈
  onShareTimeline() {
    return {
      title: "分享标题",
      query: "/pages/index/index",
      imageUrl: "/assets/imgs/share.png"
    }
  },
}

上线设置域名

设置域名的步骤如下:

  1. 登陆小程序管理后台
  2. 前往 开发 -> 开发设置
  3. tab 业务域名设置,需要管理员扫码登陆
  4. 按提示将校验文件放在服务的根目录,然后验证是否成功添加(根据域名路径访问该文件)
  5. 添加域名

参考

  • 微信小程序-获取用户位置(经纬度+所在城市)
  • 微信小程序获取详细地址踩坑一文通
  • 【SpringBoot 微信小程序】保存微信登录者的个人信息
  • 微信小程序——[http://127.0.0.1 不在以下 request 合法域名列表中]解决方案
  • 小程序 不在以下 request 合法域名列表中,请参考文档