preface

In August 2017, I created a small program called “Find your book”. Users can manage their own bookshelves/study through this small program, and then match the taste of books and you have the same preferences of people, and displayed on the map.

At that time, after the development of some crazy self-recommendation, by chance, I also met investors willing to do angel investment for this product

This is part of the BP that was written at the time

However, since I didn’t have a clear idea of the business loop and profit model, I decided not to continue, and I returned the investment money to the investors.

The sharing economy is hot in 2017, so a lot of people are thinking about sharing, including me. Now it turns out that most of the sharing economy concepts of that time are dead. The price of books in Our country is very cheap, sharing books can not even afford the logistics cost, and the social system in the business plan can not be set up in a short time.

At that time, I did the whole product design, planning, front-end development and back-end PHP all by myself… I’ve been learning more about uniCloud recently, so I resurrected the idea with UNI-App +uniCloud, since cloud development doesn’t require any server costs anyway.

Don’t want to see a long wordy article can directly jump to the end of the download source

UniCloud cloud development

To be clear in advance, I do not have uniCloud meal, but uniCloud is really good, ali Cloud to the free limit is also high. I have used uniCloud several times to build several apps at no cost. I even used uniCloud for the back end and the Flutter for the front end.

What about the benefits of cloud development? FAAS = Functions as a Service

With cloud development, we developers only need to focus on writing business logic code, and the above knowledge is not relevant to business. The business process for developing a product has also changed dramatically

The traditional process

Cloud-based development processes

UniCloud offers the following services, but these basic services are actually provided by Alibaba Cloud and Tencent Cloud, and you have to choose which service provider to use before using them.

Here directly recommend you to use uniCloud choose Ali cloud, because Tencent really cut ah ~ that point free amount or forget it, but the only benefit of Tencent cloud is Tencent department ecology can be exempted from authentication

Obtain wechat user information

To obtain the openid

uni.login({
    provider:"weixin".success:(res) = >{
        uniCloud.callFunction({
            name:"login".data: {code:res.code
            }
        })
    }
})
Copy the code

AppId and AppSecret obtained from the background of the small program are called in the cloud function of wechat server APIcode2session

const { code } = event;

const res = await uniCloud.httpclient.request(
        "https://api.weixin.qq.com/sns/jscode2session?appid="+appId+"&secret="+appSecret+"&js_code="+code+"&grant_type=authorization_code",
        {
                dataType:"json"})const openid = res.data.openid;
Copy the code

In this way, we have got the OpenID of wechat users, which can be used to establish user identification and user system. All subsequent user product data will be associated with this OpenID.

JWT implements user credentials

There is no such thing as a Session in cloud development, so you can’t use a session-like mechanism to implement user credentials

However, there are more advanced mechanisms at our disposal.

  • The server encrypts the user ID or information to be carried into a Token. The encryption key is saved only on the server.
  • It is returned to the front end through the login interface, and the front end caches itself.
  • Carry this on all subsequent requestsTokenAs a parameter, the cloud function obtains Token decryption and checks the validity

Install jsonWebToken dependencies

npm install jsonwebtoken
Copy the code

Using JWT signature, I am here in order not to directly expose OpenID, the key to write a random string, here to save trouble, directly use the small program appSecret

const jwt = require('jsonwebtoken'); 

/ / token is generated
//jwt.sign(with options such as data, key, expiration time, etc.);
jwt.sign({openid:openid},appSecret,{expiresIn:60*60*24});
Copy the code

The front-end encapsulates the NETWORK request API, and as long as the cached token is found, subsequent requests will be carried automatically. All interfaces that need to verify the user also verify the validity of the token, and then decrypt the OpenID for the user’s data operation.

jwt.verify(token,appSecret);
{openid:openid}
Copy the code

Get the nickname Avatar

Small program SDK has also been iterating, this year a lot of small programs in the user nickname has become wechat users right, is because this interface changed.

Instead of using getUserInfo in the past, we can use getUserProfile to obtain wechat users’ nicknames, profile pictures and other data. It’s just that the API will pop up to the user every time

uni.getUserProfile({
    desc: 'To improve member information'.// Mandatory parameter
    success: (res) = >{ res.userInfo; }})Copy the code

My personal approach is to exchange for OpenID through login first. Users will then see that they do not have a nickname or profile picture, and will be prompted to synchronize their wechat user information via a button. Click and call getUserProfile to get the information displayed and updated to the user database.

// the cloud function takes arguments
const { userInfo,token } = event;
// Get the database object
const db = uniCloud.database();
// Verify and decrypt the token
const payload = verifyToken(token);

// Update the openID user information in the database
const dbRes = await db.collection("users").where({
        openid:payload.openid
}).update({
        nickName:userInfo.nickName,
        avatarUrl:userInfo.avatarUrl,
        gender:userInfo.gender,
        country:userInfo.country,
        province:userInfo.province,
        city:userInfo.city
});
Copy the code

Cloud Database Operations

With the above jsonWebToken-based user credentials, the next business is basically CRUD to other data sets, create bookshelves, upload books, get bookshelf list, get book list, delete bookshelves, delete books…

Library/bookshelf database

constpayload = event.token? verifyToken(event.token):null;
const action = event.action;

let dbRes;
if(action=="create") {// Submit the library name to the content security review
        const res = await msgSecCheck(payload.openid,event.name);
        if(res.result.suggest! ="pass") {return {err:1.msg:"Content is not secure"};
        }

        // The security content is written to the database to create the shelf data
        dbRes = await db.collection("bookshelfs").add({
                owner:payload.openid,
                name:event.name,
                address:event.address,
                geopoint:new db.Geo.Point(event.longitude, event.latitude),
                totalbook:0
        })

        return dbRes;
}
Copy the code

When creating a study, the study name is user-defined and visible to other users. This is called UGC content and must pass content security review…

Implement content security review in uniCloud

const res = await uniCloud.httpclient.request("https://api.weixin.qq.com/wxa/msg_sec_check?access_token="+access_token,{
        method:"POST".dataType:"json".headers: {"Content-Type":"application/json"
        },
        data: {version:"2".openid:"Openid of content producer".scene:2.content:"Text content to be reviewed"}});Copy the code

Our bookshelves needed to be displayed on a map, so we had to let the user select and submit a location. The cloud database also makes it very easy to add location-based indexes, allowing us to list shelf data through location-based query methods

newDb.geo.Point(longitude, latitude)Copy the code

Get library list

The data related to the identity will be verified by token to obtain the OpenID

else if(action=="listmy"){
    dbRes = await db.collection('bookshelfs').where({
            owner:dbCmd.eq(payload.openid)
    })
    .orderBy("_id"."desc")
    .limit(10)
    .get()
}
Copy the code

Look for the study nearest me

else if(action=="listbygeo"){
        dbRes = await db.collection('bookshelfs').field({owner:false}).where({
                geopoint:dbCmd.geoNear({
                        geometry: new db.Geo.Point(event.longitude, event.latitude),
                        maxDistance: 3000.M / / unit
                        minDistance: 0
                })
        })
        .limit(100)// Set a quantity limit
        .get()
}
Copy the code

Modify the study

else if(action=="update") {const res = await msgSecCheck(payload.openid,event.name);

    if(res.result.suggest! ="pass") {return {err:1.msg:"Content is not secure"};
    }

    dbRes = await db.collection("bookshelfs").where({
            "_id":dbCmd.eq(event._id),
            "owner":dbCmd.eq(payload.openid)
    }).limit(1).update({
            name:event.name,
            address:event.address,
            geopoint:new db.Geo.Point(event.longitude, event.latitude)
    })

    return dbRes;
}
Copy the code

To delete a study

else if(action=="delete"){
        dbRes = await db.collection("bookshelfs").where({
                "_id":dbCmd.eq(event._id),
                "owner":dbCmd.eq(payload.openid)
        }).remove();
}
Copy the code

Book database

Through ISBN to climb douban book information

First, scanCodeapi is used to scan the bar code behind the book. After obtaining the bar code information, the cloud function is used to crawl to a certain section to get the book title and cover

let { isbn } = event;
let res = await uniCloud.httpclient.request("https://search.douban.com/book/subject_search?search_text="+isbn+"&cat=1001");

let reg = /window\.__DATA__ = "(.*)"/;
if(reg.test(res.data)){
      let bookdata=RegExp. $1;// The data is encrypted and needs to be decrypted
      
}
Copy the code

Crawl to the information need to decrypt, this article will not tell about the decryption process, partners can see the source code.

Is this reptile illegal? I don’t think it is illegal, because the cover and title information belong to public information and can not be crawled from a certain section. We can also obtain it from other places, such as book e-commerce, such as payment API, etc. In addition, we did not collect the ratings, comments and other data unique to a certain segment.

But if you have another opinion, please help me out in the comments section

Build your own ISBN library

  • Upload the cover image to the cloud storage system
let coverImage = await uniCloud.httpclient.request(data.cover_url);
	  
let uploadResult = await uniCloud.uploadFile({
    cloudPath:isbn+".jpg".fileContent:coverImage.data
})
Copy the code
  • Own ISBN database
let resData = {
        isbn:isbn,
        title:data.title,// Book title
        cover_url:uploadResult.fileID,// The cover is in the cloud storage URL
        abstract:data.abstract// Book additional information
}

dbResult = await db.collection("isbnlib").add(resData);
Copy the code

So with our own ISBN database, when users scan the same books next time, the information will be returned directly from our OWN ISBN database without having to crawl. This is also a method to build a database by using the crowdsourcing mechanism

let dbResult = await db.collection("isbnlib").limit(1).where({
              isbn:isbn
}).get();

// If the database already has the same ISBN, the database directly returns the information
if(dbResult.affectedDocs>0) {return dbResult.data[0];
}
Copy the code

Bookshelf database

We create a new table of books for bookshelf management and bind the bookshelf ID and ISBN

  • Get book information from the ISBN library
const bookInfo = await db.collection("isbnlib").doc(event.isbnid).get();
Copy the code
  • Set up book data

Bind the bookshelf ID, ISBNID, openID, etc to determine who owns the book and in which bookshelf.

dbRes = await db.collection("books").add({
        owner:payload.openid,
        shelfid:event.shelfid,
        title:bookInfo.data[0].title,
        cover_url:bookInfo.data[0].cover_url,
        isbn:bookInfo.data[0].isbn,
        isbnid:event.isbnid,
        createtime:now,
        updatetime:now,
});
Copy the code
  • Bookshelf total number field updated
await db.collection("bookshelfs").where({
        owner:payload.openid,
        _id:event.shelfid,
}).update({
        totalbook:dbCmd.inc(1)//inc increments the value of the current field
})

return dbRes;
Copy the code
  • Book search
dbRes = await db.collection('isbnlib')
                .where({
                    title=new RegExp(event.keyword, 'i')
                })
		.orderBy("_id"."desc")
		.orderBy("updatetime"."desc")
		.limit(12)
		.get();
Copy the code

Generate bookshelf posters

Use canvas to generate posters, canvas drawing details we see the project source code. Here I mainly use the applets interface to get the applets with parameters.

There are three kinds of API for generating small program code, among which interface A and interface C are limited in number and can only generate 10W at most. In case this small program user quantity is large, the library bookshelf quantity exceeds 10W, that cannot use interface A. So we’re going to use interface B, which is infinite.

  • The cloud function calls the Wxacode interface
const access_token = await getAccessToken();
const res = await uniCloud.httpclient.request("https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token="+access_token,{
        method:"POST".headers: {"Content-Type":"application/json"
        },
        data: {scene:"The information that can be obtained in the small program after scanning the code in wechat".page:"Scan code to enter the applet page"}});Copy the code

However, the limitation of interface B is that the scene field is up to 32 characters long and the parameter name can only be called scene. And this interface can only be used with published applets.

conclusion

This is the core technology part of my book sharing project in 2017, which is re-implemented with uniCloud. This article shares the core process like a running account. The reading experience may not be good, but sharing is not easy.

Program source code

Wechat search public number Da Shuai Lao Ape, reply books social small program to get all the source code of this project.