Springboot集成百度地图实现定位打卡签到功能


1:建表SQL

CREATE TABLE `o_sign` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT 'id',
  `shop_id` int DEFAULT NULL COMMENT '商家id',
  `username` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户名称',
  `location` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '打卡位置',
  `daka_time` timestamp NULL DEFAULT NULL COMMENT '打卡时间',
  `comments` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '备注',
  `isdeleted` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '0:正常数据  1:逻辑删除',
  `clocking_records` int DEFAULT NULL COMMENT '记录打卡次数',
  `integral` int DEFAULT NULL COMMENT '打卡积分',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

百度开放平台 

百度地图开放平台 | 百度地图API SDK | 地图开发

申请key:

<script type="text/javascript" src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=xxxxxxxxxxxxxxxxxxx"></script>

后台打卡逻辑(可以根据自己的具体逻辑进行修改)

    @ApiOperation("定位打卡列表")
    @RepeatSubmit(interval = 2000, message = "接口请求过于频繁")
    @PostMapping("positioning")
    public Result getOrderList(@RequestBody Sign sign){
        //拿到商家id
        String username = sign.getUsername();
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getUsername,username);
        User user = userService.getOne(wrapper);
        Integer userId = user.getId();
        Integer shopId = shopService.getShopId(userId);
        Result result = signService.saveOrUpdate(shopId, sign);
        return result;
    }
    @Override
    public Result saveOrUpdate(Integer shopId, Sign sign) {
        Optional.ofNullable(shopId).orElseThrow(() -> new RuntimeException("shopId is null"));
        //获取当前时间
        String today = DateUtil.today();
        Date dateTime = DateUtil.parse(today, "yyyy-MM-dd");
        LambdaQueryWrapper<Sign> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Sign::getShopId,shopId);
        Sign selectOne = signMapper.selectOne(wrapper);
        Sign signMessage = new Sign();
        BeanUtil.copyProperties(sign, signMessage);
        //一次加20积分,可以低效xxx钱,暂时还在思考中;
        signMessage.setIntegral(INTEGRAL);
        Integer count = 0;
        if (selectOne != null) {
            //获取上次的打卡时间
            Date dakaTime = selectOne.getDakaTime();
            long days = DateUtil.betweenDay(dateTime, dakaTime, true);
            if(days < 1) {
                // 时间间距小于1天
                return Result.fail(Integer.parseInt("500"),"不能一天打卡2次");
            } else {
                // 时间间距大于1天
                Integer clockingRecords = selectOne.getClockingRecords();
                clockingRecords = clockingRecords+1;
                Integer integral = selectOne.getIntegral();
                integral = integral+20;
                String comments = selectOne.getComments();
                signMessage.setComments(comments);
                signMessage.setIntegral(integral);
                signMessage.setClockingRecords(clockingRecords);
                signMessage.setDakaTime(dateTime);
                signMessage.setId(selectOne.getId());
                signMessage.setShopId(shopId);
                signMessage.setUpdateTime(dateTime);
                //走更新 根据商家ID进行修改
                count = signMapper.updateById(signMessage);
                boolean b = count > 0 ? true : false;
                return Result.success(b);
            }
        }
        //走新增
        signMessage.setShopId(shopId);
        signMessage.setClockingRecords(1);
        signMessage.setIsdeleted(IS_DELETED);
        signMessage.setDakaTime(dateTime);
        signMessage.setUpdateTime(dateTime);
        count = signMapper.insert(signMessage);
        boolean b = count > 0 ? true : false;
        return Result.success(b);
    }
   @Override
    public Integer getShopId(Integer userId) {
        Shop shop = shopMapper.selectById(userId);
        return shop.getId();
    }

前台在登录的时候,登录获取位置

  mounted() {
    // 获取地理位置
    var geolocation = new BMapGL.Geolocation()
    // 判断浏览器类型
    var ua = navigator.userAgent.toLowerCase()
    var isFirefox = ua.indexOf('firefox') !== -1
    var isChrome = ua.indexOf('chrome') !== -1 && ua.indexOf('safari') !== -1
    geolocation.getCurrentPosition(function(r) {
      if (this.getStatus() === BMAP_STATUS_SUCCESS) {
        const province = r.address.province
        let city = r.address.city
        const district = r.address.district
        const street = r.address.street
        // 处理火狐浏览器返回的地理位置信息格式
        if (isFirefox) {
          city = r.address.city
        }
        // 处理谷歌浏览器返回的地理位置信息格式
        if (isChrome) {
          if (city === '市辖区' || city === '县') {
            city = r.address.province + r.address.district
          }
        }
        console.log('=======', province)
        console.log('=======', city)
        console.log('=======', district)
        console.log('=======', street)
        // alert(province + city + district + street)
        localStorage.setItem('location', province.trim() + ' ' + city.trim() + ' ' + district.trim() + ' ' + street.trim())
      }
    })
  },

前台主页打卡功能页面:

<template>
  <div style="color: #666;font-size: 14px;">
    <div style="padding-bottom: 20px">
      <b>您好!{{ user }}</b>
    </div>
    <el-card shadow="hover">
      欢迎使用本系统
      <el-divider shadow="hover" />
      虎虎生威,虎年大吉
    </el-card>

    <el-card shadow="hover" style="margin-top: 20px;width: 300px">
      <div style="margin: 20px 0; font-size: 15px;color: #0d8d84;cursor: pointer">当前系统时间:{{ new Date().getFullYear() + '年' + (new Date().getMonth() + 1) + '月' + new Date().getDate() + '日' }}</div>
      <div
        style="width: 100px; height: 100px; line-height: 100px; border-radius: 50%; background-color: #1E90FF;
        font-size: 25px; color: #fff; text-align: center; cursor: pointer; box-shadow: 0 0 30px rgba(0, 0, 0, .2);"
        type="button"
        @click="sign"
      >
        打 卡
      </div>
    </el-card>
  </div>
</template>

<script>

import { reqPositioning } from '@/api/sign/sign'

export default {
  name: 'Daka',
  data() {
    return {
      user: localStorage.getItem('user') ? localStorage.getItem('user') : {},
      comments: ''
    }
  },
  methods: {
    async sign() {
      this.$confirm('你今天打卡了吗, 是否继续?', '定位打卡可获得20积分', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(async() => {
          const location = localStorage.getItem('location')
          // const password = localStorage.getItem('password')
          const username = this.user
          console.log(username)
          const sign = {
            username: username,
            location: location,
            comments: this.comments
          }
          const res = await this.$API.sign.reqPositioning(sign)
          if (res.code === 200) {
            this.$message({
              type: 'success',
              message: '已打卡成功'
            })
          }
        })
        .catch(() => {
          this.$message({
            type: 'info',
            message: '已取消打卡'
          })
        })
    }
  }
}
</script>
<style scoped>


</style>

效果图:

 

管理员端后端:(分页,模糊查询)

  @ApiOperation("打卡详情信息列表")
    @PostMapping("messageByPage")
    public Result messageByPage(@RequestBody Map<String, Object> requestBodyMaps){
        //拿到商家id
        return signService.selectOrderListByPage(requestBodyMaps);
    }
    @Override
    public Result selectOrderListByPage(Map<String, Object> requestBodyMaps) {
        Integer pageCount = (Integer) requestBodyMaps.get("page");
        Integer pageSize = (Integer) requestBodyMaps.get("pageSize");
        String username = (String) requestBodyMaps.get("username");
        String location = (String) requestBodyMaps.get("location");
        String comments = (String) requestBodyMaps.get("comments");
        LambdaQueryWrapper<Sign> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        //如果姓名不为 null 不为空,模糊查询
        if (StrUtil.isNotBlank(username)) {
            lambdaQueryWrapper.like(Sign::getUsername, "%" + username + "%");
        }
        //如果定位不为空,模糊查询定位信息
        if (StrUtil.isNotBlank(location)) {
            lambdaQueryWrapper.like(Sign::getLocation, "%" + location + "%");
        }
        //如果评论不为空,查询评论信息
        if (StrUtil.isNotBlank(comments)) {
            lambdaQueryWrapper.eq(Sign::getComments,  comments);
        }
        lambdaQueryWrapper.eq(Sign::getIsdeleted,"0");
        signMapper.selectList(lambdaQueryWrapper);
        // 查询第pageCount页,每页pageSize条记录
        Page<Sign> page = new Page<>(pageCount, pageSize);
        IPage<Sign> sighPage = signMapper.selectPage(page, lambdaQueryWrapper);
        // 当前页的记录列表
        List<Sign> sighList = sighPage.getRecords();
        // 总记录数
        long total = sighPage.getTotal();
        SighListVo sighListVo = new SighListVo();
        sighListVo.setSighVoList(sighList);
        sighListVo.setTotal(total);
        return Result.success(sighListVo);
    }
}

 

管理员端前端:

<template>
  <div>
    <div style="margin-bottom: 20px;">
      <el-card shadow="hover" body-style="width:100%;">
        <div style="display: flex">
          <div style="display: flex;align-items:center;margin-right:20px">
            <i class="el-icon-user" />
          </div>
          <div style="display: flex;align-items:center;margin-right:10px">商家名称:</div>
          <el-input v-model="comment.username" style="width: 200px" placeholder="请输入用户名" clearable />
          <div style="display: flex;align-items:center;margin-left:20px;">
            <i class="el-icon-location-outline" />
          </div>
          <div style="display: flex;align-items:center;margin-left:30px;margin-right:10px ">打卡位置:</div>
          <el-input v-model="comment.location" style="width: 200px" placeholder="请输入地址" clearable />
          <div style="display: flex;align-items:center;margin-left:20px;">
            <i class="el-icon-loading" />
          </div>
          <div style="display: flex;align-items:center;margin-left:20px;margin-right:10px">频率:</div>
          <!--          <el-input v-model="phone" style="width: 200px" placeholder="请输入频率" clearable />-->
          <el-select v-model="comment.comments" placeholder="请选择" clearable>
            <el-option
              v-for="item in options"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
          <div style="padding: 0px 20px;margin-left: 50px">
            <el-button type="primary" icon="el-icon-search" @click="load">查找</el-button>
          </div>
          <div>
            <el-button type="primary" plain icon="el-icon-info" @click="clearInput">清空</el-button>
          </div>
        </div>
      </el-card>
    </div>
    <el-card shadow="hover">
      <el-table :data="sighList" border stripe :header-cell-class-name="'headerBg'" @selection-change="handleSelectionChange">
        <el-table-column type="selection" width="55" />
        <el-table-column prop="id" label="ID" width="80" sortable />
        <el-table-column prop="username" label="商家名称" align="center" />
        <el-table-column prop="location" label="打卡位置" />
        <el-table-column prop="dakaTime" label="打卡时间" align="center" />
        <el-table-column prop="comments" label="打卡频率" align="center" />
        <el-table-column prop="clockingRecords" label="打卡次数" align="center" />
        <el-table-column prop="integral" label="打卡积分" align="center" />

        <el-table-column label="操作" width="300" align="center">
          <template slot-scope="scope">
            <el-button style="margin-right: 20px" type="success" @click="handleEdit(scope.row)">编辑 <i class="el-icon-edit" /></el-button>
            <el-popconfirm
              class="ml-5"
              confirm-button-text="确定"
              cancel-button-text="我再想想"
              icon="el-icon-info"
              icon-color="red"
              title="您确定删除吗?"
              @confirm="deleteById(scope.row)"
            >
              <el-button
                slot="reference"
                style="width: 80px"
                type="danger"
              >删除 <i class="el-icon-remove-outline" /></el-button>
            </el-popconfirm>
          </template>
        </el-table-column>
      </el-table>

      <div style="padding: 10px 0">
        <el-pagination
          :current-page="pageParams.page"
          :page-sizes="[2, 5, 10, 20]"
          :page-size="pageParams.pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="pageParams.page"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>

      <el-dialog title="信息" :visible.sync="dialogFormVisible" width="40%" :close-on-click-modal="false">
        <el-form label-width="140px" size="small" style="width: 85%;">
          <el-form-item label="商家名称">
            <el-input v-model="form.username" autocomplete="off" disabled />
          </el-form-item>
          <el-form-item label="打卡位置">
            <el-input v-model="form.location" autocomplete="off" disabled />
          </el-form-item>
          <el-form-item label="打卡时间">
            <el-date-picker v-model="form.dakaTime" disabled type="datetime" value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择日期时间" />
          </el-form-item>
          <el-form-item label="打卡次数">
            <el-input v-model="form.clockingRecords" autocomplete="off" disabled />
          </el-form-item>
          <el-form-item label="打卡积分">
            <el-input v-model="form.integral" autocomplete="off" disabled />
          </el-form-item>
          <el-form-item label="打卡频率">
            <el-select v-model="form.comments" placeholder="请选择">
              <el-option
                v-for="item in options"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </el-form>
        <div slot="footer" class="dialog-footer">
          <el-button @click="dialogFormVisible = false">取 消</el-button>
          <el-button type="primary" @click="save">确 定</el-button>
        </div>
      </el-dialog>
    </el-card>
  </div>
</template>

<script>
export default {
  name: 'Sign',
  data() {
    return {
      options: [{
        value: '1',
        label: '一般'
      }, {
        value: '2',
        label: '中等'
      }, {
        value: '3',
        label: '优秀'
      }],
      value: '',
      tableData: {},
      sighList: [],
      // 分页参数
      pageParams: {
        page: 1,
        pageSize: 10,
        total: 0
      },
      comment: {
        username: '',
        location: '',
        comments: ''
      },
      name: '',
      form: {},
      dialogFormVisible: false,
      multipleSelection: [],
      user: localStorage.getItem('user') ? localStorage.getItem('user') : {}
    }
  },
  mounted() {
    this.load()
  },
  methods: {
    // 删除这条信息
    async deleteById(row) {
      this.$confirm('删除这条打开信息后后无法回撤, 是否继续?', '删除评论', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(async() => {
          const res = await this.$API.sign.reqdeleteById(row)
          if (res.code === 200) {
            this.$message.success('删除成功')
            this.load()
          }
        })
        .catch(() => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          })
          this.load()
        })
    },
    clearInput() {
      this.comment.username = ''
      this.comment.location = ''
      this.comment.comments = ''
      this.load()
    },
    async load() {
      const selectedOption = this.options.find(option => option.value === this.comment.comments)
      const selectedLabel = selectedOption ? selectedOption.label : ''
      console.log(selectedLabel)
      console.log(this.comment)
      this.comment.comments = selectedLabel
      const res = await this.$API.sign.reqmessageByPage(this.pageParams, this.comment)
      if (res.code === 200) {
        this.pageParams.total = res.data.total
        this.sighList = res.data.sighVoList
      }
    },

    async save() {
      const selectedOption = this.options.find(option => option.value === this.form.comments)
      const selectedLabel = selectedOption ? selectedOption.label : this.form.comments
      console.log(selectedLabel)
      console.log(this.tableData)
      this.tableData.comments = selectedLabel
      const res = await this.$API.sign.reqUpdateMessage({ ...this.tableData })
      if (res.code === 200) {
        this.$message({
          type: 'success',
          message: '保存成功'
        })
        this.dialogFormVisible = false
        this.load()
      }
    },

    handleEdit(row) {
      this.form = JSON.parse(JSON.stringify(row))
      console.log(this.form)
      this.tableData = row
      this.dialogFormVisible = true
    },
    handleSelectionChange(val) {
      console.log(val)
      this.multipleSelection = val
    },

    handleSizeChange(pageSize) {
      console.log(pageSize)
      this.pageParams.pageSize = pageSize
      this.load()
    },
    handleCurrentChange(pageNum) {
      console.log(pageNum)
      this.pageNum = pageNum
      this.load()
    }
  }

}
</script>

<style scoped>
.headerBg {
  background: #eee!important;
}
</style>

 效果图:

 

 学习来源:(自己根据自己的需求进行适当修改)

Springboot+Vue集成百度地图实现定位打卡功能_哔哩哔哩_bilibiliSpringboot + Vue实现定位打卡Demo,非常适合集成到毕设, 视频播放量 8676、弹幕量 2、点赞数 197、投硬币枚数 137、收藏人数 364、转发人数 44, 视频作者 程序员青戈, 作者简介 毕设私聊Q:1938976892,相关视频:Vue 集成 高德地图 、百度地图 解决方案 - 小白新手版,[虚拟定位][视频解析]3款超实用软件推荐,更好更方便的使用您的手机!,不会还在用虚拟定位吧?一台闲置手机轻松实现远程打卡!,8分钟学完springboot+vue前后分离的增删改查,无需root轻松进行虚拟定位&获取经纬度教学,vue中使用百度地图vue-baidu-map详解 注册引入组件 缩放 覆盖点 01,百度地图-如何调用API,Fake Location(免root)定位神器,SpringBoot+Vue集成网页版在线聊天(极简版),爱思助手一键虚拟定位https://www.bilibili.com/video/BV1yS4y1e7sN/?spm_id_from=333.999.0.0&vd_source=419fe38ebae639bb2494d02c5fe95313

posted @ 2023-04-20 18:12  wode林夕  阅读(577)  评论(0编辑  收藏  举报  来源