Repository: JJLi0427/MQTT_LostFind_WXAPP
Branch: main
Commit: fb73b0a484bc
Files: 41
Total size: 54.8 KB
Directory structure:
gitextract_h1km9fww/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── build.yml
├── CODE_OF_CONDUCT.md
├── LICENSE.txt
├── README.md
├── SECURITY.md
├── go_client/
│ ├── README.md
│ ├── client.go
│ ├── config.json
│ ├── database.go
│ ├── go.mod
│ ├── go.sum
│ ├── handle.go
│ └── mqtt.go
├── project.sql
└── wxapp/
├── app.js
├── app.json
├── app.wxss
├── pages/
│ ├── add/
│ │ ├── add.js
│ │ ├── add.json
│ │ ├── add.wxml
│ │ └── add.wxss
│ ├── found/
│ │ ├── found.js
│ │ ├── found.json
│ │ ├── found.wxml
│ │ └── found.wxss
│ ├── home/
│ │ ├── home.js
│ │ ├── home.json
│ │ ├── home.wxml
│ │ └── home.wxss
│ ├── one/
│ │ ├── one.js
│ │ ├── one.json
│ │ ├── one.wxml
│ │ └── one.wxss
│ └── stats/
│ ├── stats.js
│ ├── stats.json
│ ├── stats.wxml
│ └── stats.wxss
├── project.config.json
├── project.private.config.json
└── utils/
└── base64.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.wxml linguist-language=html
*.wxss linguist-language=css
================================================
FILE: .github/workflows/build.yml
================================================
name: Build Go Client
on:
push:
branches: [ main ]
tags: [ 'v*' ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, windows, darwin]
goarch: [amd64, arm64]
exclude:
# Exclude Windows ARM builds as they're less common
- goos: windows
goarch: arm64
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Build
working-directory: ./go_client
run: |
# Set filename based on OS
if [ "${{ matrix.goos }}" = "windows" ]; then
FILENAME="mqtt_client_${{ matrix.goos }}_${{ matrix.goarch }}.exe"
else
FILENAME="mqtt_client_${{ matrix.goos }}_${{ matrix.goarch }}"
fi
# Build the binary
GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build -o $FILENAME -ldflags '-w -s' ./*.go
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: mqtt_client_${{ matrix.goos }}_${{ matrix.goarch }}
path: ./go_client/mqtt_client_${{ matrix.goos }}_${{ matrix.goarch }}*
release:
needs: build
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: mqtt_client_*/*
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [21271260@bjtu.edu.cn](21271260@bjtu.edu.cn). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html), version 2.0, available at https://www.contributor-covenant.org/version/2/0/.
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq
================================================
FILE: LICENSE.txt
================================================
MIT License
Copyright (c) 2024 Jiajun Li, Shuo Long, Jincheng Zheng
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
<div align="center">
# A Lost and Find WXAPP Base on MQTT Communication
<img src="https://github.com/JJLi0427/MQTT_LostFind_WXAPP/assets/112649584/cc003934-f94b-47cd-b077-490bdcc4f28b" width="16%">
<img src="https://github.com/JJLi0427/MQTT_LostFind_WXAPP/assets/112649584/902bb7d2-b87d-4559-a12c-d7cd6ed9e0cf" width="15%">
<img src="https://github.com/JJLi0427/MQTT_LostFind_WXAPP/assets/112649584/4ee4251f-2bea-4d31-b44d-8796e06ce8aa" width="16%">
<img src="https://github.com/JJLi0427/MQTT_LostFind_WXAPP/assets/112649584/902bb7d2-b87d-4559-a12c-d7cd6ed9e0cf" width="15%">
<img src="https://github.com/JJLi0427/MQTT_LostFind_WXAPP/assets/112649584/542e9391-0a9b-4fce-ac33-8754bc45bf4f" width="16%">
</div>
<div align="center">
<img src="https://img.shields.io/github/created-at/JJLi0427/MQTT_LostFind_WXAPP?style=flat">
<img src="https://img.shields.io/github/v/release/JJLi0427/MQTT_LostFind_WXAPP?style=flat&color=yellow">
<img src="https://img.shields.io/github/license/JJLi0427/MQTT_LostFind_WXAPP?style=flat&labelColor=grey&color=brown">
<img src="https://img.shields.io/github/watchers/JJLi0427/MQTT_LostFind_WXAPP?style=flat&logo=github&labelColor=grey&color=green">
<img src="https://img.shields.io/github/stars/JJLi0427/MQTT_LostFind_WXAPP?style=flat&logo=github&labelColor=grey&color=orange">
<img src="https://img.shields.io/github/forks/JJLi0427/MQTT_LostFind_WXAPP?style=flat&logo=github&labelColor=grey&color=blue">
</div>
### Menu
- [Contributors](#Contributors)
- [Introduction](#Introduction)
- [中文简介](#中文简介)
- [Quick start](#Quick-start)
- [Todo](#Todo)
- [License](#License)
## Contributors
<a href="https://github.com/JJLi0427/MQTT_LostFind_WXAPP/graphs/contributors">
<img src="https://contrib.rocks/image?repo=JJLi0427/MQTT_LostFind_WXAPP" />
</a>
## Introduction
We are a group of students from Beijing Jiaotong University aiming to develop a campus lost and found mini program. Having recognized the lightweight, convenient, and secure nature of the MQTT communication protocol, we have chosen to build our project around it. Currently, we have successfully crafted a comprehensive mini program interface and interactive logic. For the communication related to lost and found items, we have developed a communication client using Go language to interact with the backend database effectively.
### Project Structure

In this project, we have adopted a classical front-end separation approach. We have implemented a dual-message link architecture relying on MQTT communication. The front-end interface retrieves data from the backend database using DBAPI upon the launch of the Mini Program or page display. For operations like adding, deleting, and modifying data—such as uploading lost items or finding lost items—we have developed a global MQTT client in Go language based on message subscription communication.
The client is designed to modify the server-side database, Mini Program only after receiving messages sent by the Mini Program. Communication between the Mini Program and the client is unidirectional, ensuring information security and efficient transmission. This setup aims to streamline the architecture while bolstering security and communication efficiency.
### Database Design
#### *For lost item*
| id | username | type | name | area | photo |
|----|----------|------|------|------|-------|
| int | String | Bool | String | String | BASE64(photo) |
* `id` each lost item will auto have an id in this table
* `username` is the woner name of lost property, it help us link to user table
* `type` means the lost item status
* `name` is the lost item name
* `area` is where you lost it
* `photo` show the lost item photo, *we will improve this feature in the future*
#### *For WXAPP user*
| studentid | username | phonenumber |
|-----------|----------|-------------|
| Bigint | String | Bigint |
* `studentid` consistent with the student id in school
* `usewrname` every need a username
* `phonenumber` it help to contact with you
### MQTT Communication
In the Internet of Things communication, MQTT is the first choice of most people, this protocol adopts a publish/subscribe model, only subscribed to a specific topic can receive a specific message, all communication is based on the MQTT server to do the relay, which improves the security and transmission efficiency. Based on this, we develop a communication client between the Lost and Found Mini Program and the back-end database, receive the subscribed messages and then operate the database, so that we can ensure the security of information release and the efficiency of communication on campus.
#### *An example of our MQTT client runtime*

### WXAPP Design

- `Home page`: Show the WXAPP function enterance
- `User page`: Show user information, everyone should long in WXAPP in tihs page at first
- `Lost page`: User can add and manage their lost item in this page
- `Find page`: If any user find a lost item, thry can upload in this page
- `Summary page`: Show the summary of WXAPP work history
## 中文简介
团队来自于北京交通大学,项目旨在开发一个校园失物招领小程序。由于 MQTT 通信协议具有轻量,便捷和安全的特性,我们选择基于它构建我们的项目。目前项目已经实现了小程序界面和逻辑交互。对于失物招领相关的通信,我们使用 Go 语言的通信客户端,以便小程序与后端数据库进行高效的交互。
### 架构设计
在这个项目中,我们采用比较经典的前后端分离的方式。然后进一步我们依赖 MQTT 通信设计了一个双消息链路的架构。前端界面在小程序启动或者页面展示时通过DBAPI从后端数据库中获取数据。在上传失物或者是寻得失物等需要增删改操作数据的场景,我们专门用 Go 语言开发了一个全局的基于消息订阅方式来通信的 MQTT 客户端,客户端收到小程序发送的消息后才会对服务器中的小程序做修改,小程序对客户端的通信是单向的,以此保证信息安全和通信的高效。
### 数据库
我们设计了以下两个表来存储我们的业务数据:
1. [失物表](#For-lost-item)存储了丢失物品相关的信息: `失物ID-id` `丢失用户名-username` `当前状态-type` `丢失地点-area` `照片-photo`
2. [用户表](#For-WXAPP-user)存储了用户相关的数据: `学生ID-studentid` `用户名-username` `手机号-phonenumber`
### MQTT通信
在物联网通信中,MQTT 是大部分人的首选,这个协议采用的是发布/订阅模式,只有订阅了特定的主题才能收到特定的消息,所有通信都是基于 MQTT 服务器做的中转,这提高了安全性和传输的效率。基于此开发失物招领小程序和后端数据库之间的通信客户端,接收订阅的消息然后再操作数据库,这样我们可以保证校园内的信息发布的安全性和通信的效率。
### 小程序设计
我们总共为这个失物招领小程序设计了五个页面:
- `主页`: 是小程序的入口,也是所有功能页面的入口
- `用户页`: 显示用户信息,所有用户都需要在这个页面先登录
- `丢失页`: 用户可以在这个页面上传和管理他们的失物
- `寻得页`: 找到了失物的用户可以在这个页面更新信息
- `总结页`: 程序运行历史记录总结
## Quick start
1. At first install these dependencies
* [MySQL](https://www.mysql.com/) as our project database
* [EMQX](https://www.emqx.io/zh) help us build MQTT server
* [MQTT client wechat miniprogram](https://github.com/emqx/MQTT-Client-Examples/tree/master/mqtt-client-wechat-miniprogram) project help WXAPP connect the go client
2. Clone our repo or download our release code
3. Create a database through `project.sql`
3. `cd ./go_client` modify `config.json`, input your database and MQTT server configuration
4. Build go MQTT clinet from source code client.go and run it:
```shell
go mod init client
go mod tidy
GOOS={$YOUR_SYSTEM} GOARCH={$YOUR_CPU} go build -o {$EXE_FILE_NAME} -ldflags '-w -s' ./*.go
./{$EXE_FILE_NAME}
```
6. Load WXAPP project through [Weixin DevTools](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)
7. Change the IP and Port part of the WXAPP code
## Todo
**All we are completed**
1. -[x] ~~Optimized the MQTT client architecture and runs based on configuration files~~
2. -[X] ~~WXAPP implements online user registration~~
3. -[x] ~~WXAPP realize lost items'photo upload~~
## License
This project is licensed under the [MIT License](https://opensource.org/license/MIT) - see the [LICENSE.txt](https://github.com/JJLi0427/MQTT_LostFind_wxapp/blob/main/LICENSE.txt) file for details.
================================================
FILE: SECURITY.md
================================================
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| v2.0 | :white_check_mark: |
| v1.1+ | :white_check_mark: |
| v1.0 | :x: |
- Unfortunately, we can't provide security support for v1.0 because it doesn't use the client config.json
## Reporting a Vulnerability
- **Reporting Method**: Please report security vulnerabilities using the GitHub Issues tab.
- **Response Time**: Reported vulnerabilities will be addressed within one week.
- **Vulnerability Acceptance**: Once a vulnerability report is received, the security team will review it, and you will be notified of the acceptance or rejection of the report.
- **Further Updates**: If the vulnerability is accepted, we will work on a fix and provide updates on the progress.
## Additional Information
Please reach out to our security team at 21271260@bjtu.edu.cn for any security-related concerns or inquiries.
Thank you for helping us keep the project secure!
================================================
FILE: go_client/README.md
================================================
### Build Project
This project support build for any os.
```shell
# handle import package version
go mod init client
go mod tidy
# build
GOOS={$YOUR_SYSTEM} GOARCH={$YOUR_CPU} go build -o {$EXE_FILE_NAME} -ldflags '-w -s' ./*.go
# run
./{$EXE_FILE_NAME}
# test build for arm linux device
GOOS=linux GOARCH=arm64 go build -o client -ldflags '-w -s' ./*.go
```
### Use it
You need to change the config.json before run it, make sure you split mqtt and sql server.
### Problem & Log
It will save log auto, check the log to help you debug.
================================================
FILE: go_client/client.go
================================================
package main
import (
"context"
"database/sql"
"encoding/json"
"io"
"log"
"os"
"os/signal"
"runtime"
"sync"
mqtt "github.com/eclipse/paho.mqtt.golang"
_ "github.com/go-sql-driver/mysql"
)
var dbMutex sync.Mutex
// Config 结构体用于存储配置信息
type Config struct {
DatabaseServer struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Database string `json:"database"`
} `json:"database server"`
MqttServer struct {
Host string `json:"host"`
Port int `json:"port"`
Topic []string `json:"other_topic"`
} `json:"mqtt server"`
}
// 从配置文件中加载配置
func loadConfig() Config {
config := Config{}
file, err := os.ReadFile("./config.json")
if err != nil {
log.Fatal(err)
}
err = json.Unmarshal(file, &config)
if err != nil {
log.Fatal(err)
}
// 检查配置中的所有字段
if config.DatabaseServer.Host == "" {
log.Fatal("Config file is missing 'database server host'.")
}
if config.DatabaseServer.Port == 0 {
log.Fatal("Config file is missing 'database server port'.")
}
if config.DatabaseServer.User == "" {
log.Fatal("Config file is missing 'database server user'.")
}
if config.DatabaseServer.Password == "" {
log.Fatal("Config file is missing 'database server password'.")
}
if config.DatabaseServer.Database == "" {
log.Fatal("Config file is missing 'database server database'.")
}
if config.MqttServer.Host == "" {
log.Fatal("Config file is missing 'mqtt server host'.")
}
if config.MqttServer.Port == 0 {
log.Fatal("Config file is missing 'mqtt server port'.")
}
// if len(config.MqttServer.Topic) == 0 {
// log.Fatal("Config file is missing 'mqtt server topic'.")
// }
return config
}
// 等待中断信号并关闭数据库和MQTT客户端
func waitForInterrupt(ctx context.Context, db *sql.DB, client mqtt.Client) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
select {
case <-c:
log.Println("Interrupt signal received. Disconnecting...")
case <-ctx.Done():
log.Println("Context cancelled. Disconnecting...")
}
client.Disconnect(1000) // 断开MQTT客户端连接
db.Close() // 关闭数据库连接
log.Println("Disconnected successfully.")
}
// logWriter 是一个自定义的 io.Writer,用于写入日志
type logWriter struct{}
// Write 实现了 io.Writer 的 Write 方法
func (lw logWriter) Write(p []byte) (n int, err error) {
// 打开日志文件
logFile, err := os.OpenFile("client.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
return 0, err
}
defer logFile.Close()
// 写入日志到文件和控制台
mw := io.MultiWriter(os.Stdout, logFile)
return mw.Write(p)
}
func main() {
// 设置日志输出到自定义的 Writer
log.SetOutput(logWriter{})
// 打印操作系统,处理器平台,CPU核心数和内存
memStats := &runtime.MemStats{}
runtime.ReadMemStats(memStats)
log.Println(
"OS:",
runtime.GOOS,
"|",
"Arch:",
runtime.GOARCH,
"|",
"CPU cores:",
runtime.NumCPU(),
"|",
"Memory:",
(memStats.Sys / 1024),
"MB",
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel() // 在main函数结束时取消上下文
config := loadConfig() // 加载配置
db := connectDatabase(config) // 连接到数据库
client := createMqttClient(config) // 创建MQTT客户端
subscribeTopics(client, config, db) // 订阅主题
waitForInterrupt(ctx, db, client) // 等待中断信号
}
================================================
FILE: go_client/config.json
================================================
{
"database server":{
"host":"",
"port":3306,
"user":"",
"password":"",
"database":""
},
"mqtt server":{
"host":"",
"port":1883,
"other_topic":[
]
}
}
================================================
FILE: go_client/database.go
================================================
package main
import (
"database/sql"
"log"
"strconv"
"strings"
)
// 连接到数据库
func connectDatabase(config Config) *sql.DB {
db, err := sql.Open(
"mysql",
strings.Join([]string{
config.DatabaseServer.User,
":",
config.DatabaseServer.Password,
"@tcp(",
config.DatabaseServer.Host,
":",
strconv.Itoa(config.DatabaseServer.Port),
")/",
config.DatabaseServer.Database,
}, ""),
)
if err != nil {
log.Fatal(err)
}
err = db.Ping()
if err != nil {
log.Fatal(err)
}
log.Printf(
"Connected to the database at: %s:%d\n",
config.DatabaseServer.Host,
config.DatabaseServer.Port,
)
log.Printf(
"Successfully connected to database: %s\n",
config.DatabaseServer.Database,
)
return db
}
================================================
FILE: go_client/go.mod
================================================
module client
go 1.20
require (
github.com/eclipse/paho.mqtt.golang v1.4.3
github.com/go-sql-driver/mysql v1.8.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
golang.org/x/net v0.23.0 // indirect
golang.org/x/sync v0.1.0 // indirect
)
================================================
FILE: go_client/go.sum
================================================
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik=
github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
================================================
FILE: go_client/handle.go
================================================
package main
import (
"database/sql"
"log"
"strconv"
"strings"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
// 处理 "lost" 主题的消息
func handleLostTopic(payload string, db *sql.DB) {
parts := strings.Split(payload, ",")
if len(parts) == 4 {
user := parts[0]
datatype := "lost"
name := parts[1]
area := parts[2]
photo := parts[3]
dbMutex.Lock() // 加锁
// 往数据库中存储失物信息
_, err := db.Exec(
"INSERT INTO sutff (username, type, name, area, photo) VALUES (?, ?, ?, ?, ?)",
user,
datatype,
name,
area,
photo,
)
dbMutex.Unlock() // 解锁
if err != nil {
log.Fatal(err)
}
log.Printf(
"Lost item add to database {User: %s, Name: %s, Area: %s}\n",
user,
name,
area,
)
}
}
// 处理 "delete" 主题的消息
func handleDeleteTopic(payload string, db *sql.DB) {
id, err := strconv.Atoi(payload)
if err != nil {
log.Fatal(err)
}
// 删除数据库中指定物品
dbMutex.Lock()
_, err = db.Exec(
"DELETE FROM sutff WHERE id = ?",
id,
)
dbMutex.Unlock()
if err != nil {
log.Fatal(err)
}
log.Printf("Lost item with ID:%d has been deleted\n", id)
}
// 处理 "find" 主题的消息
func handleFindTopic(payload string, db *sql.DB) {
id, err := strconv.Atoi(payload)
if err != nil {
log.Fatal(err)
}
// 更新数据库中的数据
dbMutex.Lock()
_, err = db.Exec(
"UPDATE sutff SET type = ? WHERE id = ?",
"find",
id,
)
dbMutex.Unlock()
if err != nil {
log.Fatal(err)
}
log.Printf("Lost item with ID:%d has been marked as found\n", id)
}
// 处理 "signup" 主题的消息
func handleSignupTopic(payload string, db *sql.DB) {
parts := strings.Split(payload, ",")
if len(parts) == 3 {
studentid := parts[0]
username := parts[1]
phonenumber := parts[2]
dbMutex.Lock()
// 查询数据库中是否已经存在具有相同 studentid 的用户
var existingUser string
err := db.QueryRow("SELECT studentid FROM user WHERE studentid = ?", studentid).Scan(&existingUser)
// 如果查询结果返回了一个或多个记录,那么我们就不需要再插入新的用户
if err == nil {
dbMutex.Unlock()
log.Printf("User with studentid %s already exists\n", studentid)
return
}
// 往数据库中存储新用户信息
_, err = db.Exec(
"INSERT INTO user (studentid, username, phonenumber) VALUES (?, ?, ?)",
studentid,
username,
phonenumber,
)
dbMutex.Unlock()
if err != nil {
log.Fatal(err)
}
log.Printf(
"New user add to database {User: %s, Name: %s, Phone: %s}\n",
studentid,
username,
phonenumber,
)
}
}
// 处理接收到的消息
func handleMessage(client mqtt.Client, msg mqtt.Message, db *sql.DB) {
getmsg := string(msg.Payload())
if msg.Topic() == "lost" {
lastComma := strings.LastIndex(getmsg, ",")
if lastComma != -1 {
getmsg = getmsg[:lastComma] + ",BASE64(photo)"
}
}
log.Printf("Recevie topic[%s] message: %s\n", msg.Topic(), getmsg)
payload := string(msg.Payload())
// 根据主题处理消息
switch msg.Topic() {
case "lost":
handleLostTopic(payload, db)
case "delete":
handleDeleteTopic(payload, db)
case "find":
handleFindTopic(payload, db)
case "signup":
handleSignupTopic(payload, db)
case "exit":
log.Println("remot-eclient log out safely.")
case "error":
log.Println("remot-eclient lost connection, please try again later.")
}
// 可以基于此位置进一步开发,处理更多的主题
}
================================================
FILE: go_client/mqtt.go
================================================
package main
import (
"database/sql"
"strconv"
"strings"
"sync"
"time"
"log"
mqtt "github.com/eclipse/paho.mqtt.golang"
)
// 项目必须的主题
var extraTopics = []string{
"lost",
"delete",
"find",
"signup",
"exit",
"error",
}
// 创建MQTT客户端
func createMqttClient(config Config) mqtt.Client {
opts := mqtt.NewClientOptions()
broker := strings.Join(
[]string{
config.MqttServer.Host,
strconv.Itoa(config.MqttServer.Port),
},
":",
)
opts.AddBroker(broker)
timestamp := time.Now().Unix()
clientID := strings.Join(
[]string{
"receiveclient_",
strconv.FormatInt(timestamp, 10),
},
"",
)
opts.SetClientID(clientID)
client := mqtt.NewClient(opts)
log.Printf("Created MQTT client with ID: %s\n", clientID)
// 建立连接
token := client.Connect()
if token.Wait() && token.Error() != nil {
log.Fatal(token.Error())
}
// 打印连接到服务器的 IP 和端口
log.Printf("Connected to MQTT broker at: %s\n", broker)
return client
}
// 订阅主题
func subscribeTopics(client mqtt.Client, config Config, db *sql.DB) {
messageHandler := func(client mqtt.Client, msg mqtt.Message) {
// 启动一个新的goroutine来处理这个消息
go handleMessage(client, msg, db)
}
// 创建一个新的切片,包含config.MqttServer.Topic和extraTopics的所有元素
allTopics := append([]string{}, config.MqttServer.Topic...)
allTopics = append(allTopics, extraTopics...)
var wg sync.WaitGroup
for _, topic := range allTopics {
wg.Add(1)
go func(t string) {
defer wg.Done()
token := client.Subscribe(t, 0, messageHandler)
if token.Wait() && token.Error() != nil {
log.Fatal(token.Error())
}
log.Printf("Subscribe: %s\n", t)
}(topic)
}
wg.Wait()
}
================================================
FILE: project.sql
================================================
-- 创建用户表
CREATE TABLE user (
studentid BIGINT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
phonenumber BIGINT NOT NULL
);
-- 创建失物表
CREATE TABLE stuff (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
type BOOLEAN NOT NULL,
name VARCHAR(255) NOT NULL,
area VARCHAR(255) NOT NULL,
photo LONGTEXT NOT NULL,
FOREIGN KEY (username) REFERENCES user (username)
);
================================================
FILE: wxapp/app.js
================================================
// app.js
App({
globalData: {
mylost:{
list:[
],
total: 3
},
uname: "app"
},
})
================================================
FILE: wxapp/app.json
================================================
{
"pages": [
"pages/home/home",
"pages/one/one",
"pages/add/add",
"pages/stats/stats",
"pages/found/found"
],
"tabBar": {
"selectedColor": "#b4282d",
"position": "bottom",
"list": [
{
"pagePath": "pages/home/home",
"text": "首页",
"iconPath": "images/home.png",
"selectedIconPath": "images/home1.png"
},
{
"pagePath": "pages/one/one",
"text": "我的",
"iconPath": "images/my.png",
"selectedIconPath": "images/my1.png"
}
]
},
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#ffe605",
"navigationBarTitleText": "",
"navigationBarTextStyle": "black"
},
"style": "v2"
}
================================================
FILE: wxapp/app.wxss
================================================
/**app.wxss**/
/* .container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
padding: 200rpx 0;
box-sizing: border-box;
} */
================================================
FILE: wxapp/pages/add/add.js
================================================
// pages/add/add.js
import mqtt from "../../utils/mqtt.min.js";
import {Base64} from "../../utils/base64";
var newitem = {}
var app = getApp()
newitem.photo = ""
Page({
data: {
mylost:{
list:[
],
total: "",
},
imgsrc:"/images/photo.png",
client: null,
username: "Jiajun Li",
photo:"",
},
doDeleteRow(e){
wx.showModal({
title: '确认是否删除?',
confirmColor: "#ff461f",
success: (res) => {
if (!res.confirm) {
return
}
var nid = e.currentTarget.dataset.nid;
const clientId = new Date().getTime()
this.data.client = mqtt.connect(`wxs://101.201.100.189:8084/mqtt`, {
...this.data.mqttOptions,
clientId,
})
if (this.data.client) {
this.data.client.publish("delete",String(nid));
}
setTimeout(()=>{
this.data.client.end();
this.data.client = null;
},1000)
var index = e.currentTarget.dataset.index
var dataList = this.data.mylost.list
dataList.splice(index,1)
let total = this.data.mylost.total - 1
wx.showLoading({
title: '删除中',
mask:true
})
setTimeout(function () {
wx.hideLoading()
}, 1000)
this.setData({
"mylost.list":dataList,
"mylost.total":total
})
wx.hideLoading()
}
})
},
nameinput(e){
newitem.name = e.detail.value
},
areainput(e){
newitem.area = e.detail.value
},
chooseimg(e) {
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sizeType: ['compressed'],
sourceType: ['album'],
success: res=>{
this.setData({
imgsrc:res.tempFiles[0].tempFilePath,
})
newitem.photo = wx.getFileSystemManager().readFileSync(res.tempFiles[0].tempFilePath, 'base64')
}
})
},
additem(e){
if (newitem.photo == "") {
newitem.photo = wx.getFileSystemManager().readFileSync("/images/photo.png", 'base64')
}
if(newitem.name != "" && newitem.area != "" && newitem.phoneNumber != "" && app.globalData.uname != "app"){
let total = this.data.mylost.total + 1
this.setData({
"mylost.total":total
})
newitem.id = total
this.setData({
"mylost.list": this.data.mylost.list.concat(newitem)
})
const clientId = new Date().getTime()
this.data.client = mqtt.connect(`wxs://101.201.100.189:8084/mqtt`, {
...this.data.mqttOptions,
clientId,
})
if (this.data.client) {
this.data.client.publish("lost",app.globalData.uname+","+newitem.name+","+newitem.area+","+newitem.photo);
wx.showToast({
title: "上传中",
})
}
setTimeout(()=>{
this.data.client.end();
this.data.client = null;
},1000)
}
},
onLoad(options) {
},
onShow() {
let that = this;
wx.request({
url:"http://121.43.238.224:8520/api/sutff",
method:"POST",
data:{
nm:app.globalData.uname
},
success:(res) => {
let processedData = res.data.data.map(item => {
item.photo = Base64.decode(item.photo);
return item;
});
that.setData({
"mylost.list": processedData,
"mylost.total": processedData.length
})
},
fail:(err) => {
console.log(err);
}
})
}
})
================================================
FILE: wxapp/pages/add/add.json
================================================
{
"usingComponents": {},
"navigationBarBackgroundColor": "#ffe605",
"enablePullDownRefresh": true,
"backgroundColor": "#ffe605",
"navigationBarTextStyle": "black"
}
================================================
FILE: wxapp/pages/add/add.wxml
================================================
<!--pages/add/add.wxml-->
<view class="container">
<view class="top">
<view class="first">我的遗失物品数量</view>
<view class="sum">{{mylost.total}}</view>
</view>
<view class="table">
<view class="item">
<view class="title">失物列表</view>
</view>
<view class="item" wx:for="{{mylost.list}}" wx:for-item="row" wx:key="index">
<view class="save">
<view class="photo">
<image src="data:image/jpg;base64, {{row.photo}}" mode='aspectFit'></image>
</view>
<view class="moredata">
<view class="name">{{row.name}}</view>
<view>
<view class="txt">
<lable class="zh">遗失位置</lable>
<label class="en">| AREA</label>
</view>
<view class="area">
<label class="area">@{{row.area}}</label>
</view>
</view>
</view>
<view class="delete" bindtap="doDeleteRow" data-nid="{{row.id}}" data-index="{{index}}" >
<label>🗑️</label>
</view>
</view>
</view>
</view>
</view>
<view calss="new">
<view class="item">
<view class="title">新增失物</view>
</view>
<view class="row">
<input placeholder="物品描述(例:iphone14紫色)" placeholder-class="txt" bindinput="nameinput"/>
</view>
<view class="row">
<input placeholder="丢失位置(例:SY101)" placeholder-class="txt" bindinput="areainput"/>
</view>
<view class="choose">
<!-- <view bindtap="chooseimg" class="btn">选择图片</view> -->
<image src="{{imgsrc}}" mode='aspectFit' class="choosepthoto" bindtap="chooseimg">点击选择图片</image>
</view>
<view>
<button class="submit" bindtap="additem" style="width:25%">添加</button>
</view>
</view>
================================================
FILE: wxapp/pages/add/add.wxss
================================================
/* pages/add/add.wxss */
.top{
background-color: #ffe605;
height: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
color: white;
}
.top .first{
font-size: 40rpx;
font-weight: 900;
}
.top .sum{
font-size: 75rpx;
padding: 15rpx;
font-weight: 900;
}
.function{
display: flex;
flex-direction: row;
justify-content: flex-start;
background-color: #ffc506;
}
.function .menu{
height: 100rpx;
width: 374rpx;
margin: 20rpx 0;
font-size: 30rpx;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.function .menu:nth-child(odd){
border-right: 1rpx solid white;
}
.table .item{
border-bottom: 1px solid #e7e7e7;
}
.item .title{
margin:20rpx 20rpx;
padding-left: 10rpx;
border-left: 5rpx solid #ffc506;
}
.save{
margin: 20rpx 20rpx;
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.save .photo{
width: 175rpx;
height: 175rpx;
}
.save .photo image{
width: 100%;
height: 100%;
border-radius: 30rpx;
}
.moredata{
width: 350rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.moredata .name{
margin-top: 20rpx;
margin-left: 20rpx;
font-size: 40rpx;
font-weight: bold;
}
.txt{
font-size: 26rpx;
margin-left: 20rpx;
}
.txt .zh{
color: #8c8c8c;
}
.txt .en{
color: #cccccc;
}
.area{
font-size: 32rpx;
color: #ff8800;
font-weight: bold;
margin-left: 10rpx;
}
.delete{
width: 150rpx;
text-align: right;
display: flex;
flex-direction: column;
justify-content: center;
font-size: 40rpx;
font-weight: bold;
}
.row{
padding: 10rpx 0rpx;
border-bottom: 1rpx solid #ddd;
position: relative;
margin-top: 50rpx;
margin-left: 100rpx;
margin-right: 100rpx;
}
.choose{
text-align: center;
}
.choosepthoto{
margin: 50rpx 0;
width: 200rpx;
height: 200rpx;
border-radius: 20rpx;
}
.btn {
background-color: #ffc506;
padding: 20rpx 20rpx;
color: #fff;
border-radius: 20rpx;
margin-top: 50rpx;
margin-bottom: 10rpx;
margin-left: 230rpx;
margin-right: 230rpx;
font-weight: bold;
}
.submit{
bottom: 40rpx;
border-radius: 20rpx;
margin: 50rpx;
color: #fff;
background-color: #ff8800;
font-size: 32rpx;
font-weight: bold;
}
================================================
FILE: wxapp/pages/found/found.js
================================================
// pages/found/found.js
import mqtt from "../../utils/mqtt.min.js";
import {Base64} from "../../utils/base64";
var newitem = {}
newitem.imgsrc = ""
Page({
data: {
found:{
list:[
],
totalFound: "",
},
mylost:{
list:[
],
totalLost: 0,
},
client: null,
phone : "phone",
name : "name"
},
FindTheRow(e){
var index = e.currentTarget.dataset.index;
wx.showModal({
title: '是否已找回?',
confirmColor: "#ff461f",
success: (res) => {
if (!res.confirm) {
return
}
var nid = e.currentTarget.dataset.nid;
wx.request({
url:"http://121.43.238.224:8520/api/lostuser",
method:"POST",
data:{id:nid},
success:(res) => {
this.setData({
name: res.data.data[0].username
}, () => {
wx.request({
url:"http://121.43.238.224:8520/api/getphone",
method:"POST",
data:{user:this.data.name},
success:(res) => {
this.setData({
phone: res.data.data[0].phonenumber
}, () => {
wx.showModal({
title: '请联系',
content: this.data.name + ': (+86)' + this.data.phone,
confirmColor: "#ff461f",
success: (res) => {
if (!res.confirm) {
return
}
}
})
})
}
})
});
}
})
const clientId = new Date().getTime()
this.data.client = mqtt.connect(`wxs://101.201.100.189:8084/mqtt`, {
...this.data.mqttOptions,
clientId,
})
if (this.data.client) {
this.data.client.publish("find",String(nid));
}
setTimeout(()=>{
this.data.client.end();
this.data.client = null;
},1000)
var index = e.currentTarget.dataset.index;
var dataList = this.data.mylost.list;
var findList = this.data.found.list;
var removedItem = dataList.splice(index, 1)[0]; // Remove item from dataList and store it in removedItem
let totalLost = this.data.mylost.totalLost - 1;
let totalFound = this.data.found.totalFound + 1;
findList.push(removedItem); // Add removed item to findList
// Update the data in your component/state as needed
this.setData({
'mylost.list': dataList,
'mylost.totalLost': totalLost,
'found.list': findList,
'found.totalFound': totalFound
});
}
})
},
additem(e){
if(newitem.name != "" && newitem.area != ""){
let total = this.data.mylost.total + 1
this.setData({
"mylost.total":total
})
newitem.id = total
this.setData({
"mylost.list": this.data.mylost.list.concat(newitem)
})
}
},
onLoad(options) {
let that = this;
wx.request({
url:"http://121.43.238.224:8520/api/sutffbytype",
method:"POST",
// data:{nm:"Jiajun Li"},
data:{tp:"lost"},
success:(res) => {
let processedData = res.data.data.map(item => {
item.photo = Base64.decode(item.photo);
return item;
});
that.setData({
"mylost.list": processedData,
"mylost.total": processedData.length
})
},
fail:(err) => {console.log(err);}
})
wx.request({
url:"http://121.43.238.224:8520/api/sutffbytype",
method:"POST",
data:{tp:"find"},
success:(res) => {
let processedData = res.data.data.map(item => {
item.photo = Base64.decode(item.photo);
return item;
});
that.setData({
"found.list": processedData,
"found.totalFound": processedData.length
})
},
fail:(err) => {console.log(err);}
})
},
})
================================================
FILE: wxapp/pages/found/found.json
================================================
{
"usingComponents": {}
}
================================================
FILE: wxapp/pages/found/found.wxml
================================================
<!--pages/found/found.wxml-->
<view class="container">
<view class="top">
<view class="first">您已找回的物品数量</view>
<view class="sum">{{found.totalFound}}</view>
</view>
<view class="table">
<view class="item">
<view class="title">失物列表</view>
</view>
<view class="item" wx:for="{{mylost.list}}" wx:for-item="row" wx:key="index">
<view class="save">
<view class="photo">
<image src="data:image/jpg;base64, {{row.photo}}" mode='aspectFit'></image>
</view>
<view class="moredata">
<view class="name">{{row.name}}</view>
<view>
<view class="txt">
<lable class="zh">遗失位置</lable>
<label class="en">| AREA</label>
</view>
<view class="area">
<label class="area">@{{row.area}}</label>
</view>
</view>
</view>
<view class="delete" bindtap="FindTheRow" data-nid="{{row.id}}" data-index="{{index}}" >
<label>✅</label>
</view>
</view>
</view>
</view>
<view class="table">
<view class="item">
<view class="title">已找回列表</view>
</view>
<view class="item" wx:for="{{found.list}}" wx:for-item="row" wx:key="index">
<view class="save">
<view class="photo">
<image src="data:image/jpg;base64, {{row.photo}}" mode='aspectFit'></image>
</view>
<view class="moredata">
<view class="name">{{row.name}}</view>
<view>
<view class="txt">
<lable class="zh">遗失位置</lable>
<label class="en">| AREA</label>
</view>
<view class="area">
<label class="area">@{{row.area}}</label>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
================================================
FILE: wxapp/pages/found/found.wxss
================================================
/* pages/found/found.wxss */
.top{
background-color: #ffe605;
height: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
color: white;
}
.top .first{
font-size: 40rpx;
font-weight: 900;
}
.top .sum{
font-size: 75rpx;
padding: 15rpx;
font-weight: 900;
}
.function{
display: flex;
flex-direction: row;
justify-content: flex-start;
background-color: #ffc506;
}
.function .menu{
height: 100rpx;
width: 374rpx;
margin: 20rpx 0;
font-size: 30rpx;
color: white;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.function .menu:nth-child(odd){
border-right: 1rpx solid white;
}
.table .item{
border-bottom: 1px solid #e7e7e7;
}
.item .title{
margin:20rpx 20rpx;
padding-left: 10rpx;
border-left: 5rpx solid #ffc506;
}
.save{
margin: 20rpx 20rpx;
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.save .photo{
width: 175rpx;
height: 175rpx;
}
.save .photo image{
width: 100%;
height: 100%;
border-radius: 30rpx;
}
.moredata{
width: 350rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.moredata .name{
margin-top: 20rpx;
margin-left: 20rpx;
font-size: 40rpx;
font-weight: bold;
}
.txt{
font-size: 26rpx;
margin-left: 20rpx;
}
.txt .zh{
color: #8c8c8c;
}
.txt .en{
color: #cccccc;
}
.area{
font-size: 32rpx;
color: #ff8800;
font-weight: bold;
margin-left: 10rpx;
}
.delete{
width: 150rpx;
text-align: right;
display: flex;
flex-direction: column;
justify-content: center;
font-size: 40rpx;
font-weight: bold;
}
.row{
padding: 10rpx 0rpx;
border-bottom: 1rpx solid #ddd;
position: relative;
margin-top: 50rpx;
margin-left: 100rpx;
margin-right: 100rpx;
}
.choose{
text-align: center;
}
.choosepthoto{
margin: 50rpx 0;
width: 200rpx;
height: 200rpx;
border-radius: 20rpx;
}
.btn {
background-color: #ffc506;
padding: 20rpx 20rpx;
color: #fff;
border-radius: 20rpx;
margin-top: 50rpx;
margin-bottom: 10rpx;
margin-left: 230rpx;
margin-right: 230rpx;
font-weight: bold;
}
.submit{
bottom: 40rpx;
border-radius: 20rpx;
margin: 50rpx;
color: #fff;
background-color: #ff8800;
font-size: 32rpx;
font-weight: bold;
}
================================================
FILE: wxapp/pages/home/home.js
================================================
// pages/home/home.js
Page({
data: {
},
goto(e){
wx.navigateTo({
url: '/pages/one/one',
})
},
})
================================================
FILE: wxapp/pages/home/home.json
================================================
{
"usingComponents": {},
"navigationBarTitleText": "在线失物招领平台"
}
================================================
FILE: wxapp/pages/home/home.wxml
================================================
<!--pages/home/home.wxml-->
<view class="container">
<navigator class="menu" url="/pages/add/add">
<image src="/images/1.png" style="width:100rpx; height:100rpx"></image>
<view>我的失物</view>
</navigator>
<navigator class="menu" url="/pages/found/found">
<image src="/images/2.png" style="width:100rpx; height:100rpx"></image>
<view>寻得物品</view>
</navigator>
<navigator class="menu" url="/pages/stats/stats">
<image src="/images/3.png" style="width:100rpx; height:100rpx"></image>
<view>平台统计</view>
</navigator>
<view class="last">
<image src="/images/4.jpg" style="width:180rpx; height:180rpx"></image>
</view>
</view>
================================================
FILE: wxapp/pages/home/home.wxss
================================================
/* pages/home/home.wxss */
.container{
border-top: 1px solid #ddd;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.container .menu{
width: 750rpx;
height: 370rpx;
border-bottom: 1px solid #ddd;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container .last{
width: 750rpx;
height: 180rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
================================================
FILE: wxapp/pages/one/one.js
================================================
// pages/one/one.js
import mqtt from "../../utils/mqtt.min.js";
const app = getApp()
var newMsg = {}
Page({
data: {
name: "",
account: "",
phoneNumber: "",
imagePath: "/images/one.png",
},
nameInput(e){
newMsg.name = e.detail.value
},
accountInput(e){
newMsg.account = e.detail.value
},
phoneNumberInput(e){
newMsg.phoneNumber = e.detail.value
},
updateMsg(e){
if(newMsg.account != "" && newMsg.name != "" && newMsg.phoneNumber != "") {
let that = this;
wx.request({
url:"http://121.43.238.224:8520/api/user",
method:"POST",
data:{
f_id:newMsg.account,
f_name:newMsg.name,
f_phone:newMsg.phoneNumber
},
success:(res) => {
that.setData({
account: res.data.data[0].studentid,
name: res.data.data[0].username,
phoneNumber: res.data.data[0].phonenumber
}),
wx.showToast({
title: "登录成功",
}),
app.globalData.uname = newMsg.name;
},
fail:(err) => {
console.log(err);
wx.showToast({
title: "登录失败",
});
},
})
}
},
signIn() {
if(newMsg.account != "" && newMsg.name != "" && newMsg.phoneNumber != "") {
const clientId = new Date().getTime()
this.data.client = mqtt.connect(`wxs://101.201.100.189:8084/mqtt`, {
...this.data.mqttOptions,
clientId,
})
if (this.data.client) {
this.data.client.publish("signup", newMsg.account+","+newMsg.name+","+newMsg.phoneNumber);
}
setTimeout(()=>{
this.data.client.end();
this.data.client = null;
},1000)
wx.showLoading({
title: '注册中',
mask: true
})
setTimeout(function () {
wx.hideLoading(),
this.updateMsg()
}, 2000)
// this.updateMsg()
}
},
})
================================================
FILE: wxapp/pages/one/one.json
================================================
{
"usingComponents": {}
}
================================================
FILE: wxapp/pages/one/one.wxml
================================================
<!--pages/one/one.wxml-->
<view class="container">
<view class="menu">
<image src="{{imagePath}}" class="avatar" mode='aspectFit'></image>
<view>姓名 : {{name}} 学号 :{{account}} 电话 :{{phoneNumber}}</view>
</view>
<view class="menu1">
<view class="row">
<input placeholder="姓名" placeholder-class="txt" bindinput="nameInput"/>
</view>
<view class="row">
<input placeholder="学号" placeholder-class="txt" bindinput="accountInput"/>
</view>
<view class="row">
<input placeholder="电话" placeholder-class="txt" bindinput="phoneNumberInput"/>
</view>
<view>
<text></text>
<view>
<button class="submit1" bindtap="updateMsg" style="width:30%">登录平台</button>
<button class="submit2" bindtap="signIn" style="width:30%">注册</button>
</view>
</view>
</view>
</view>
================================================
FILE: wxapp/pages/one/one.wxss
================================================
/* pages/one/one.wxss */
.top{
background-color: #ffe605;
height: 150rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color:white;
}
.top .first{
font-size: 50rpx;
font-weight: 800;
}
.news{
display: flex;
flex-direction: row;
justify-content: space-between;
font-size: 30rpx;
font-weight: 600;
}
.container .menu{
margin-top: 100rpx;
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container .menu1{
margin-top: 250rpx;
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: column;
justify-content: center;
}
.submit1{
bottom: 40rpx;
border-radius: 20rpx;
margin-top: 80rpx;
color: #fff;
background-color: #ff8800;
font-size: 32rpx;
font-weight: bold;
}
.submit2{
bottom: 40rpx;
border-radius: 20rpx;
margin-top: 20rpx;
color: #fff;
background-color: #ff8800;
font-size: 32rpx;
font-weight: bold;
}
.avatar {
height: 100px;
width: 100px;
border-radius: 100px;
}
.row{
padding: 10rpx 0rpx;
border-bottom: 1rpx solid #ddd;
position: relative;
margin-top: 50rpx;
margin-left: 100rpx;
margin-right: 100rpx;
}
================================================
FILE: wxapp/pages/stats/stats.js
================================================
// pages/stats/stats.js
Page({
data: {
user: {
list: [
],
totalUser: 0,
activeNumber: 0,
},
todayFound: 0,
totalFound: 0,
},
onLoad(options) {
let that = this;
wx.request({
url:"http://121.43.238.224:8520/api/sutffcount",
success:(res) => {
that.setData({
totalFound: res.data.data[0].sutffcount
})
},
fail:(err) => {console.log(err);}
})
wx.request({
url:"http://121.43.238.224:8520/api/usercount",
success:(res) => {
that.setData({
"user.totalUser": res.data.data[0].usercount
})
},
fail:(err) => {console.log(err);}
})
},
})
================================================
FILE: wxapp/pages/stats/stats.json
================================================
{
"usingComponents": {}
}
================================================
FILE: wxapp/pages/stats/stats.wxml
================================================
<!--pages/stats/stats.wxml-->
<view class="container">
<view class="top">
<view class="first">📢最新消息</view>
<view class="sum" animation="{{animationData}}">恭喜{{user.list[0].username}}同学找回物品</view>
</view>
<view class="menu1">
<view class="first">总共帮助找回物品数</view>
<view class="sum">{{totalFound}}</view>
</view>
<view class="menu2">
<view class="first">今日新增失物数量</view>
<view class="sum">{{todayFound}}</view>
</view>
<view class="menu3">
<view class="first">平台用户数</view>
<view class="sum">{{user.totalUser}}</view>
</view>
<view class="last">
<image src="/images/4.jpg" style="width:180rpx; height:180rpx"></image>
</view>
</view>
================================================
FILE: wxapp/pages/stats/stats.wxss
================================================
/* pages/stats/stats.wxss */
.top{
background-color: #ffe605;
height: 200rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color:black;
}
.top .first{
font-size: 55rpx;
font-weight: 900;
color:rgb(255, 255, 255);
}
.top .sum{
font-size: 32rpx;
font-weight: 800;
color:rgb(255, 255, 255);
}
.news{
display: flex;
flex-direction: row;
justify-content: space-between;
font-size: 30rpx;
font-weight: 600;
}
.container .menu1{
background-color: #ffec45;
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container .menu2{
background-color: #fff280;
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container .menu3{
background-color: #fffac5;
width: 750rpx;
height: 300rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.container .last{
width: 750rpx;
height: 180rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.first{
font-size: 30rpx;
font-weight: 500;
}
.sum{
font-size: 45rpx;
padding: 15rpx;
font-weight: 900;
}
================================================
FILE: wxapp/project.config.json
================================================
{
"compileType": "miniprogram",
"setting": {
"coverView": true,
"es6": true,
"postcss": true,
"minified": true,
"enhance": true,
"showShadowRootInWxmlPanel": true,
"packNpmRelationList": [],
"babelSetting": {
"ignore": [],
"disablePlugins": [],
"outputPath": ""
},
"condition": false,
"ignoreUploadUnusedFiles": false
},
"editorSetting": {
"tabIndent": "insertSpaces",
"tabSize": 2
},
"condition": {},
"libVersion": "3.0.0",
"packOptions": {
"ignore": [],
"include": []
},
"appid": "wx4c8e412409542089"
}
================================================
FILE: wxapp/project.private.config.json
================================================
{
"description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
"condition": {
"miniprogram": {
"list": [
{
"name": "",
"pathName": "pages/add/add",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "",
"pathName": "pages/new/new",
"query": "",
"launchMode": "default",
"scene": null
}
]
}
},
"projectname": "wxapp",
"setting": {
"compileHotReLoad": true,
"useIsolateContext": false,
"urlCheck": false
}
}
================================================
FILE: wxapp/utils/base64.js
================================================
export let Base64 = {tables : [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O' ,'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' ], UTF16ToUTF8 : function (str) { let results = [], len = str.length; for (let i = 0; i < len; i++) { let code = str.charCodeAt(i); if (code > 0x0000 && code <= 0x007F) { results.push(str.charAt(i)); } else if (code >= 0x0080 && code <= 0x07FF) { let byte1 = 0xC0 | ((code >> 6) & 0x1F); let byte2 = 0x80 | (code & 0x3F); results.push( String.fromCharCode(byte1), String.fromCharCode(byte2) ); } else if (code >= 0x0800 && code <= 0xFFFF) { let byte1 = 0xE0 | ((code >> 12) & 0x0F); let byte2 = 0x80 | ((code >> 6) & 0x3F); let byte3 = 0x80 | (code & 0x3F); results.push( String.fromCharCode(byte1), String.fromCharCode(byte2), String.fromCharCode(byte3), ); } else if (code >= 0x00010000 && code <= 0x001FFFFF) { } else if (code >= 0x00200000 && code <= 0x03FFFFFF) { } else { } } return results.join(''); }, UTF8ToUTF16 : function (str) { let results = [], len = str.length; let i = 0; for (let i = 0; i < len; i++) { let code = str.charCodeAt(i); if (((code >> 7) & 0xFF) == 0x0) { results.push(str.charAt(i)); } else if (((code >> 5) & 0xFF) == 0x6) { let code2 = str.charCodeAt(++i); let byte1 = (code & 0x1F) << 6; let byte2 = code2 & 0x3F; let utf16 = byte1 | byte2; results.push(Sting.fromCharCode(utf16)); } else if (((code >> 4) & 0xFF) == 0xE) { let code2 = str.charCodeAt(++i); let code3 = str.charCodeAt(++i); let byte1 = (code << 4) | ((code2 >> 2) & 0x0F); let byte2 = ((code2 & 0x03) << 6) | (code3 & 0x3F); let utf16 = ((byte1 & 0x00FF) << 8) | byte2; results.push(String.fromCharCode(utf16)); } else if (((code >> 3) & 0xFF) == 0x1E) { } else if (((code >> 2) & 0xFF) == 0x3E) { } else { } } return results.join(''); }, encode : function (str) { if (!str) { return ''; } let utf8 = this.UTF16ToUTF8(str); let i = 0; let len = utf8.length; let results = []; while (i < len) { let c1 = utf8.charCodeAt(i++) & 0xFF; results.push(this.tables[c1 >> 2]); if (i == len) { results.push(this.tables[(c1 & 0x3) << 4]); results.push('=='); break; } let c2 = utf8.charCodeAt(i++); if (i == len) { results.push(this.tables[((c1 & 0x3) << 4) | ((c2 >> 4) & 0x0F)]); results.push(this.tables[(c2 & 0x0F) << 2]); results.push('='); break; } let c3 = utf8.charCodeAt(i++); results.push(this.tables[((c1 & 0x3) << 4) | ((c2 >> 4) & 0x0F)]); results.push(this.tables[((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6)]); results.push(this.tables[c3 & 0x3F]); } return results.join(''); }, decode : function (str) { if (!str) { return ''; } let len = str.length; let i = 0; let results = []; while (i < len) { let code1 = this.tables.indexOf(str.charAt(i++)); let code2 = this.tables.indexOf(str.charAt(i++)); let code3 = this.tables.indexOf(str.charAt(i++)); let code4 = this.tables.indexOf(str.charAt(i++)); let c1 = (code1 << 2) | (code2 >> 4); results.push(String.fromCharCode(c1)); if (code3 != -1) { let c2 = ((code2 & 0xF) << 4) | (code3 >> 2); results.push(String.fromCharCode(c2)); } if (code4 != -1) { let c3 = ((code3 & 0x3) << 6) | code4; results.push(String.fromCharCode(c3)); } } return this.UTF8ToUTF16(results.join('')); }};
gitextract_h1km9fww/
├── .gitattributes
├── .github/
│ └── workflows/
│ └── build.yml
├── CODE_OF_CONDUCT.md
├── LICENSE.txt
├── README.md
├── SECURITY.md
├── go_client/
│ ├── README.md
│ ├── client.go
│ ├── config.json
│ ├── database.go
│ ├── go.mod
│ ├── go.sum
│ ├── handle.go
│ └── mqtt.go
├── project.sql
└── wxapp/
├── app.js
├── app.json
├── app.wxss
├── pages/
│ ├── add/
│ │ ├── add.js
│ │ ├── add.json
│ │ ├── add.wxml
│ │ └── add.wxss
│ ├── found/
│ │ ├── found.js
│ │ ├── found.json
│ │ ├── found.wxml
│ │ └── found.wxss
│ ├── home/
│ │ ├── home.js
│ │ ├── home.json
│ │ ├── home.wxml
│ │ └── home.wxss
│ ├── one/
│ │ ├── one.js
│ │ ├── one.json
│ │ ├── one.wxml
│ │ └── one.wxss
│ └── stats/
│ ├── stats.js
│ ├── stats.json
│ ├── stats.wxml
│ └── stats.wxss
├── project.config.json
├── project.private.config.json
└── utils/
└── base64.js
SYMBOL INDEX (33 symbols across 10 files)
FILE: go_client/client.go
type Config (line 21) | type Config struct
function loadConfig (line 38) | func loadConfig() Config {
function waitForInterrupt (line 79) | func waitForInterrupt(ctx context.Context, db *sql.DB, client mqtt.Clien...
type logWriter (line 94) | type logWriter struct
method Write (line 97) | func (lw logWriter) Write(p []byte) (n int, err error) {
function main (line 110) | func main() {
FILE: go_client/database.go
function connectDatabase (line 11) | func connectDatabase(config Config) *sql.DB {
FILE: go_client/handle.go
function handleLostTopic (line 13) | func handleLostTopic(payload string, db *sql.DB) {
function handleDeleteTopic (line 46) | func handleDeleteTopic(payload string, db *sql.DB) {
function handleFindTopic (line 66) | func handleFindTopic(payload string, db *sql.DB) {
function handleSignupTopic (line 87) | func handleSignupTopic(payload string, db *sql.DB) {
function handleMessage (line 127) | func handleMessage(client mqtt.Client, msg mqtt.Message, db *sql.DB) {
FILE: go_client/mqtt.go
function createMqttClient (line 27) | func createMqttClient(config Config) mqtt.Client {
function subscribeTopics (line 59) | func subscribeTopics(client mqtt.Client, config Config, db *sql.DB) {
FILE: project.sql
type user (line 2) | CREATE TABLE user (
type stuff (line 9) | CREATE TABLE stuff (
FILE: wxapp/pages/add/add.js
method doDeleteRow (line 23) | doDeleteRow(e){
method nameinput (line 64) | nameinput(e){
method areainput (line 68) | areainput(e){
method chooseimg (line 72) | chooseimg(e) {
method additem (line 87) | additem(e){
method onLoad (line 118) | onLoad(options) {
method onShow (line 122) | onShow() {
FILE: wxapp/pages/found/found.js
method FindTheRow (line 27) | FindTheRow(e){
method additem (line 105) | additem(e){
method onLoad (line 118) | onLoad(options) {
FILE: wxapp/pages/home/home.js
method goto (line 7) | goto(e){
FILE: wxapp/pages/one/one.js
method nameInput (line 14) | nameInput(e){
method accountInput (line 18) | accountInput(e){
method phoneNumberInput (line 22) | phoneNumberInput(e){
method updateMsg (line 26) | updateMsg(e){
method signIn (line 58) | signIn() {
FILE: wxapp/pages/stats/stats.js
method onLoad (line 15) | onLoad(options) {
Condensed preview — 41 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (65K chars).
[
{
"path": ".gitattributes",
"chars": 60,
"preview": "*.wxml linguist-language=html\n*.wxss linguist-language=css\n\n"
},
{
"path": ".github/workflows/build.yml",
"chars": 1669,
"preview": "name: Build Go Client\n\non:\n push:\n branches: [ main ]\n tags: [ 'v*' ]\n pull_request:\n branches: [ main ]\n\njob"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3278,
"preview": "# Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as contributors and"
},
{
"path": "LICENSE.txt",
"chars": 1093,
"preview": "MIT License\n\nCopyright (c) 2024 Jiajun Li, Shuo Long, Jincheng Zheng\n\nPermission is hereby granted, free of charge, to a"
},
{
"path": "README.md",
"chars": 7559,
"preview": "<div align=\"center\">\n\n# A Lost and Find WXAPP Base on MQTT Communication \n\n <img src=\"https://github.com/JJLi0427/MQTT_"
},
{
"path": "SECURITY.md",
"chars": 985,
"preview": "# Security Policy\n\n## Supported Versions\n| Version | Supported |\n| ------- | ------------------ |\n| v2.0 | :"
},
{
"path": "go_client/README.md",
"chars": 538,
"preview": "### Build Project\nThis project support build for any os.\n```shell\n# handle import package version\ngo mod init client\ngo "
},
{
"path": "go_client/client.go",
"chars": 3221,
"preview": "package main\n\nimport (\n\t\"context\"\n\t\"database/sql\"\n\t\"encoding/json\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"runtime\"\n\t\"sync\"\n\n\t"
},
{
"path": "go_client/config.json",
"chars": 251,
"preview": "{\n \"database server\":{\n \"host\":\"\",\n \"port\":3306,\n \"user\":\"\",\n \"password\":\"\",\n \"dat"
},
{
"path": "go_client/database.go",
"chars": 731,
"preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// 连接到数据库\nfunc connectDatabase(config Config) *sq"
},
{
"path": "go_client/go.mod",
"chars": 301,
"preview": "module client\n\ngo 1.20\n\nrequire (\n\tgithub.com/eclipse/paho.mqtt.golang v1.4.3\n\tgithub.com/go-sql-driver/mysql v1.8.1\n)\n\n"
},
{
"path": "go_client/go.sum",
"chars": 1014,
"preview": "filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=\nfilippo.io/edwards25519 v1.1.0/go.mod h1:"
},
{
"path": "go_client/handle.go",
"chars": 3419,
"preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"log\"\n\t\"strconv\"\n\t\"strings\"\n\n\tmqtt \"github.com/eclipse/paho.mqtt.golang\"\n)\n\n// 处"
},
{
"path": "go_client/mqtt.go",
"chars": 1621,
"preview": "package main\n\nimport (\n\t\"database/sql\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"log\"\n\n\tmqtt \"github.com/eclipse/paho.mqt"
},
{
"path": "project.sql",
"chars": 417,
"preview": "-- 创建用户表\nCREATE TABLE user (\n studentid BIGINT PRIMARY KEY,\n username VARCHAR(255) NOT NULL,\n phonenumber BIGIN"
},
{
"path": "wxapp/app.js",
"chars": 113,
"preview": "// app.js\nApp({\n globalData: {\n mylost:{\n list:[\n\n ],\n total: 3\n },\n uname: \"app\"\n },\n})\n"
},
{
"path": "wxapp/app.json",
"chars": 743,
"preview": "{\n \"pages\": [\n \"pages/home/home\",\n \"pages/one/one\",\n \"pages/add/add\",\n \"pages/stats/stats\",\n \"pages/foun"
},
{
"path": "wxapp/app.wxss",
"chars": 200,
"preview": "/**app.wxss**/\n/* .container {\n height: 100%;\n display: flex;\n flex-direction: column;\n align-items: center;\n justi"
},
{
"path": "wxapp/pages/add/add.js",
"chars": 3462,
"preview": "// pages/add/add.js\nimport mqtt from \"../../utils/mqtt.min.js\";\nimport {Base64} from \"../../utils/base64\";\n\nvar newitem "
},
{
"path": "wxapp/pages/add/add.json",
"chars": 174,
"preview": "{\n \"usingComponents\": {},\n \"navigationBarBackgroundColor\": \"#ffe605\",\n \"enablePullDownRefresh\": true,\n \"backgroundCo"
},
{
"path": "wxapp/pages/add/add.wxml",
"chars": 1702,
"preview": "<!--pages/add/add.wxml-->\n<view class=\"container\">\n <view class=\"top\">\n <view class=\"first\">我的遗失物品数量</view>\n <vie"
},
{
"path": "wxapp/pages/add/add.wxss",
"chars": 2275,
"preview": "/* pages/add/add.wxss */\n\n.top{\n background-color: #ffe605;\n height: 200rpx;\n display: flex;\n flex-direction: column"
},
{
"path": "wxapp/pages/found/found.js",
"chars": 4121,
"preview": "// pages/found/found.js\nimport mqtt from \"../../utils/mqtt.min.js\";\nimport {Base64} from \"../../utils/base64\";\n\nvar newi"
},
{
"path": "wxapp/pages/found/found.json",
"chars": 27,
"preview": "{\n \"usingComponents\": {}\n}"
},
{
"path": "wxapp/pages/found/found.wxml",
"chars": 1836,
"preview": "<!--pages/found/found.wxml-->\n<view class=\"container\">\n <view class=\"top\">\n <view class=\"first\">您已找回的物品数量</view>\n "
},
{
"path": "wxapp/pages/found/found.wxss",
"chars": 2279,
"preview": "/* pages/found/found.wxss */\n\n.top{\n background-color: #ffe605;\n height: 200rpx;\n display: flex;\n flex-direction: co"
},
{
"path": "wxapp/pages/home/home.js",
"chars": 120,
"preview": "// pages/home/home.js\nPage({\n data: {\n\n },\n\n goto(e){\n wx.navigateTo({\n url: '/pages/one/one',\n })\n },\n})"
},
{
"path": "wxapp/pages/home/home.json",
"chars": 67,
"preview": "{\n \"usingComponents\": {},\n \"navigationBarTitleText\": \"在线失物招领平台\"\n}"
},
{
"path": "wxapp/pages/home/home.wxml",
"chars": 658,
"preview": "<!--pages/home/home.wxml-->\n<view class=\"container\">\n <navigator class=\"menu\" url=\"/pages/add/add\">\n <image src=\"/im"
},
{
"path": "wxapp/pages/home/home.wxss",
"chars": 479,
"preview": "/* pages/home/home.wxss */\n\n.container{\n border-top: 1px solid #ddd;\n display: flex;\n flex-direction: column;\n justi"
},
{
"path": "wxapp/pages/one/one.js",
"chars": 1948,
"preview": "// pages/one/one.js\nimport mqtt from \"../../utils/mqtt.min.js\";\nconst app = getApp()\nvar newMsg = {}\n\nPage({\n data: {\n "
},
{
"path": "wxapp/pages/one/one.json",
"chars": 27,
"preview": "{\n \"usingComponents\": {}\n}"
},
{
"path": "wxapp/pages/one/one.wxml",
"chars": 837,
"preview": "<!--pages/one/one.wxml-->\n<view class=\"container\">\n <view class=\"menu\"> \n <image src=\"{{imagePath}}\" class=\"avatar\" "
},
{
"path": "wxapp/pages/one/one.wxss",
"chars": 1229,
"preview": "/* pages/one/one.wxss */\n.top{\n background-color: #ffe605;\n height: 150rpx;\n display: flex;\n flex-direction: column;"
},
{
"path": "wxapp/pages/stats/stats.js",
"chars": 702,
"preview": "// pages/stats/stats.js\nPage({\n data: {\n user: {\n list: [\n \n ],\n totalUser: 0,\n activeNumber"
},
{
"path": "wxapp/pages/stats/stats.json",
"chars": 27,
"preview": "{\n \"usingComponents\": {}\n}"
},
{
"path": "wxapp/pages/stats/stats.wxml",
"chars": 688,
"preview": "<!--pages/stats/stats.wxml-->\n<view class=\"container\">\n <view class=\"top\">\n <view class=\"first\">📢最新消息</view>\n <vi"
},
{
"path": "wxapp/pages/stats/stats.wxss",
"chars": 1284,
"preview": "/* pages/stats/stats.wxss */\n\n.top{\n background-color: #ffe605;\n height: 200rpx;\n display: flex;\n flex-direction: co"
},
{
"path": "wxapp/project.config.json",
"chars": 602,
"preview": "{\n \"compileType\": \"miniprogram\",\n \"setting\": {\n \"coverView\": true,\n \"es6\": true,\n \"postcss\": true,\n \"minif"
},
{
"path": "wxapp/project.private.config.json",
"chars": 696,
"preview": "{\n \"description\": \"项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/"
},
{
"path": "wxapp/utils/base64.js",
"chars": 3620,
"preview": "export let Base64 = {tables : [\t\t\t'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',\t\t\t'I', 'J', 'K', 'L', 'M', 'N', 'O' ,'P',\t\t\t'Q"
}
]
About this extraction
This page contains the full source code of the JJLi0427/MQTT_LostFind_WXAPP GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 41 files (54.8 KB), approximately 17.7k tokens, and a symbol index with 33 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.