这是我参与8月更文挑战的第12天,活动详情查看:8月更文挑战
在 Github
开源 欢迎来star
链接如下:
使用Golang+微信小程序+Python 前后端分离打造的车辆信息联络平台实战项目。
1. 背景
1.1 实现目标:
- 用户可通过拍照识别车牌号进行绑定车牌号,也能通过车牌号找到对应的车主。
- 车牌冲突可进行申诉反馈。
- 可通过文本消息提醒、在线聊天、短信提醒等形式与对方车主产生联系。
- 强大的社区模块,支持闲置物品的交互买卖。
- 实时获取充电桩信息,方便用户选择。
- 除基本管理操作,后台还可对车流量进行实时监控。
1.2 开发环境:
开发语言: 后 端:Golang v1.15、Python v3.7
前 端:微信小程序基础库 v2.16.0、Vue v3.3.0
算 法 : Pytorch v1.7.1、Cuda v11.0
数 据 库 : MySQL v5.7.30、MongoDB v4.4.6、Redis v4.0.9
短信服务 :腾讯云短信
文件存储 :七牛云存储
服 务 器 : 阿里云服务器
1.3 go语言部分代码展示
model层
package model
import "github.com/jinzhu/gorm"
type Car struct {
gorm.Model
CarName string //车名字
CarNum string //车牌号
CarImages string //车照片
CarBossId uint //车主ID
CarBossName string
}
service 层
package service
import (
"CarDemo1/model"
"CarDemo1/pkg/e"
"CarDemo1/serializer"
"fmt"
)
type ShowFriendService struct {
}
type ShowMyFriendInfoService struct {
}
type CreateFriendService struct {
UserID uint `form:"user_id" json:"user_id"`
}
type DeleteFriendService struct {
UserID string `form:"user_id" json:"user_id"`
}
//展示好友
func (service *ShowFriendService) Show(id string) serializer.Response {
var user model.User
var friendsList []model.User
code := e.Success
model.DB.Table("user").Where("id = ?",id).First(&user)
fmt.Println(user)
err := model.DB.Model(&user).Association("Relations").Find(&friendsList).Error
fmt.Println(friendsList)
if err != nil {
code = e.ErrorFriendFound
return serializer.Response{
Status: code,
Data: err,
Msg: e.GetMsg(code),
}
}
return serializer.Response{
Status: code,
Data: serializer.BuildFriends(friendsList),
Msg: e.GetMsg(code),
}
}
//关注好友
func (service *CreateFriendService) Create(id uint) serializer.Response {
var user model.User
var friend model.User
code := e.Success
model.DB.Model(&friend).Where(`id = ? `,id).First(&friend) //被关注者
model.DB.Model(&user).Where(`id = ?`,service.UserID).First(&user) //关注者
err := model.DB.Model(&user).Association(`Relations`).Append([]model.User{friend}).Error
if err != nil {
code = e.ErrorFriendFound
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Error: err.Error(),
}
}
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
}
}
//解除好友关系
func (service *DeleteFriendService) Delete(id uint) serializer.Response {
var user model.User
var friend []model.User
code := e.Success
model.DB.Model(&friend).Where(`id = ?`,id).First(&friend) //被关注者
model.DB.Model(&user).Where(`id = ?`,service.UserID).First(&user) //关注者
err := model.DB.Model(&user).Association(`Relations`).Delete(friend).Error
//model.DB.Model(&user).Association("Relations").Clear()
// Remove the relationship between source & arguments if exists
// only delete the reference, won’t delete those objects from DB.
if err != nil {
code = e.ErrorFriendFound
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
Error:err.Error(),
}
}
return serializer.Response{
Status: code,
Msg: e.GetMsg(code),
}
}
//展示好友的详细信息
func (service *ShowMyFriendInfoService) Show(id string) serializer.Response {
var user model.User
code := e.Success
err := model.DB.Model(&model.User{}).Where(`id = ?`,id).First(&user).Error
if err != nil {
code = e.ErrorDatabase
return serializer.Response{
Status: code,
Data: err,
Msg: e.GetMsg(code),
}
}
return serializer.Response{
Status: code,
Data: serializer.BuildUser(user),
Msg: e.GetMsg(code),
}
}
serializer层
package serializer
import "CarDemo1/model"
type Friend struct {
ID uint `json:"id"`
UserName string `json:"user_name"`
UserAvatar string `json:"user_avatar"`
}
//序列化好友
func BuildFriend(item model.User) Friend {
return Friend{
ID: item.ID,
UserName : item.UserName,
UserAvatar : item.Avatar,
}
}
//序列化好友列表
func BuildFriends(items []model.User) (Friends []Friend) {
for _, item := range items {
Friend := BuildFriend(item)
Friends = append(Friends, Friend)
}
return Friends
}
1.4 小程序部分代码展示
publish.wxml 页面
<view class="container-publish">
<view class="chose-image" bindtap="uploadImage">
<image src="/images/publish.png"></image>
<text>+图片</text>
</view>
<view class="image-list" wx:if="{{imageList.length > 0}}">
<view class="item" wx:for="{{imageList}}" wx:key="key">
<image src="{{item.path}}"></image>
<icon wx:if="{{item.percent==100}}" class="rm" type="clear" color="red" data-index="{{index}}" data-item="{{item}}" bindtap="removeImage"></icon>
<progress percent="{{item.percent}}" wx:if="{{item.error}}" color="#FF0000" />
<progress percent="{{item.percent}}" wx:else />
</view>
</view>
<view class="text">
<textarea placeholder=" 来呀,写下你的心情" value="{{content}}" bindinput="bindContentInput" />
</view>
<view class="function-view">
<view class="row" >
<view class="left">{{ topicTitle }}</view>
<view class="right">
<image class="go-icon" src='/images/iconsmall.png'></image>
</view>
</view>
<view class="topic-all">
<view wx:for="{{ topicList }}" wx:key="topicList" class="topic-content">
<view data-id="{{item.id}}" class="{{item.id == idx ?'choose':'notchoose'}}" bindtap="goIndex" data-id="{{item.id}}">#{{item.category_name}} </view>
</view>
</view>
</view>
</view>
<view class="publish-btn" bindtap="publishNews">发 布</view>
publish.js页面
// pages/publish/publish.js
var api = require('../../config/api.js')
Page({
/**
* 页面的初始数据
*/
data: {
userInfo: wx.getStorageSync('userInfo'),
imageList: [],
content: "",
address: "",
topicId: null,
topicTitle: "选择合适的话题",
idx: "",
},
resetData: function () {
this.setData({
imageList: [],
content: "",
address: "",
topicId: null,
topicTitle: "选择合适的话题",
});
},
uploadImage: function () {
// 选择图片并上传
wx.chooseImage({
count: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success: res => {
var oldLength = parseInt(this.data.imageList.length);
// 最多上传9张
let totalCount = res.tempFiles.length + this.data.imageList.length;
if (totalCount > 9) {
wx.showToast({
title: '图片最多选择9张',
icon: 'none'
});
return
};
console.log(this.data.imageList.concat(res.tempFiles));
// 本地图片在页面预览
this.setData({
imageList: this.data.imageList.concat(res.tempFiles)
});
// 上传新挑选的图片(原图片无需再上传)
for (var index in res.tempFiles) {
let imageFilePath = res.tempFiles[index].path;
var filePathSplit = imageFilePath.split('.');
var ext = filePathSplit[filePathSplit.length - 1];
// 创建随机字符串
let randowString = Math.random().toString(36).slice(-8) + String(new Date().getTime());
var fileKey = randowString + "." + ext;
var targetIndex = parseInt(oldLength) + parseInt(index);
this.setData({
["imageList[" + targetIndex + "].key"]: fileKey
});
}
}
})
},
removeImage: function (event) {
// 判断是否正在上传,如果正在上传就终止,否则就删除;
// 删除图片,终止 & 删除
var index = event.currentTarget.dataset['index'];
var item = event.currentTarget.dataset['item'];
if (item.percent == 100) {
cos.deleteObject({
Bucket: "mini-1251317460",
Region: "ap-chengdu",
Key: item.key
}, (err, data) => {
if (err) {
wx.showToast({
title: '删除失败',
icon: 'none'
});
} else {
var imageList = this.data.imageList;
imageList.splice(index, 1);
this.setData({
imageList: imageList
});
}
});
}
},
getLocation: function () {
wx.chooseLocation({
success: res => {
this.setData({
address: res.address
})
}
});
},
updateTopic: function (item) {
this.setData({
topicId: item.id,
topicTitle: item.title
})
},
bindContentInput: function (e) {
this.setData({
content: e.detail.value
});
},
publishNews: function () {
//发布至少需要一张图片
if (this.data.imageList.length < 1) {
wx.showToast({
title: '至少选择一张图片',
icon: 'none'
});
return
}
// 发布内容不能为空
if (this.data.content.length < 1) {
wx.showToast({
title: '内容不能为空',
icon: 'none'
});
return
}
wx.showLoading({
title: '发布中...',
duration: 2000,
})
var content = this.data.content
wx.uploadFile({
method:"POST",
url: api.CreateSocial+content,
filePath:this.data.imageList[0].path ,
name: 'file', // 和后端沟通好 接收文件的name
header: {
"Authorization": wx.getStorageSync('token'),
user_id: this.data.userInfo.id,
category_id: this.data.idx,
},
success: function (res) {
var data = JSON.parse(res.data)
console.log(data)
if(data.status == 200){
wx.showToast({
title: '绑定成功',
duration: 2000,
})
wx.navigateTo({
url: '/pages/publishSuccess/publishSuccess',
})
}else{
wx.showToast({
title: '绑定失败',
duration: 2000,
})
}
},
fail: function (err) {
console.log(err)
}
});
}
})
2. 功能介绍
2.1 主体部分
声明:这里的首页以及个人信息页面是参考隔壁有坑的小程序前端。
原作者github:GitHub
- 主页面中,UI界面简介大方得体。方便用户快速了解小程序的大体功能,也非常感谢原作者的开源!
- 主页面呈现四个模块
- 社区模块
- 亲友模块
- 聊天模块
- 个人中心
2.2 用户模块
个人中心是可以对用户个人的信息进行修改、由于是用微信登陆,所以姓名和头像是读取微信的头像和名字。所以名字和头像是不支持修改的。
但是手机号、邮箱号、车辆是可以进行解绑定的。 用户可以通过绑定自己的车牌号来管理自己的车辆。
车牌,我们提供了一个ocr的算法接口,可以对车牌进行识别,然后返回车牌信息进行绑定车辆。
- 个人信息
- 绑定邮箱
- 绑定手机
- 绑定车牌
2.3 社区模块
推荐模块、亲友圈、闲来康康、我的世界等。
- 我的世界模块可以查看到用户个人发布的帖子。
- 帖子详情、可以对帖子进行评论、点赞等操作。
- 帖子发送,用户可以通过话题进行发布帖子。
2.4 聊天模块以及充电功能
- 聊天功能,实现实时聊天。
- 系统消息,系统可有针对性的对其进行发送信息。
- 用户反馈,可以进行评论举报、聊天举报、车牌申诉等功能。
- 充电桩查询,我们用爬虫将学校充电桩的情况进行爬取,使得用户能够查看充电桩的情况。
2.5 算法方面
算法部分的结果都是通过flask框架进行api接口的返回。
2.5.1 FasterRCNN网络车牌识别
2.5.2 YOLOV5 车辆识别
2.6 后台管理模块
后台模块相对简单,并没有设计到比较多的功能,后需再进行完善。
- 用户模块管理 - 车辆模块管理 - 反馈信息管理 - 车流监控管理
可对用户进行拉黑、封号处理
可下架、修改用户的帖子信息。
可对用户的车辆进行处理、更换车牌号等
3. 总结
本人目前大二,很多东西都不怎么懂。所以写的代码结构不是很好。
- go的ws也有涉及。
- gorm的多对多也有了深入的了解。还有后端的一些逻辑结构。
- 熟悉了腾讯云短信,七牛云存储,阿里云服务器的一些操作。
算法方面
- FasterRCNN的车牌识别
- YOLO网络的车辆检测
不过顺便可以把这个当作下学期的软工实践了哈哈!
喜欢的小伙伴可以关注我噢~