Initial commit
This commit is contained in:
57
cloudfunctions/createPost/index.js
Normal file
57
cloudfunctions/createPost/index.js
Normal file
@@ -0,0 +1,57 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
const _ = db.command
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { content, images = [], video, location, hashtags = [], mood, petId } = event
|
||||
|
||||
if (!content?.trim() && images.length === 0) {
|
||||
return { code: -1, message: '内容不能为空' }
|
||||
}
|
||||
|
||||
try {
|
||||
const post = {
|
||||
authorId: OPENID,
|
||||
petId: petId || null,
|
||||
content: (content || '').trim(),
|
||||
images,
|
||||
video: video || null,
|
||||
location: location || null,
|
||||
hashtags,
|
||||
mood: mood || null,
|
||||
likeCount: 0,
|
||||
commentCount: 0,
|
||||
createdAt: db.serverDate(),
|
||||
updatedAt: db.serverDate(),
|
||||
}
|
||||
|
||||
const res = await db.collection('posts').add({ data: post })
|
||||
|
||||
// 更新用户帖子统计
|
||||
await db.collection('users')
|
||||
.where({ openid: OPENID })
|
||||
.update({ data: { 'stats.posts': _.inc(1) } })
|
||||
|
||||
// 更新话题统计
|
||||
if (hashtags.length > 0) {
|
||||
const batch = hashtags.map(tag =>
|
||||
db.collection('topics')
|
||||
.where({ tag })
|
||||
.get()
|
||||
.then(r => {
|
||||
if (r.data.length === 0) {
|
||||
return db.collection('topics').add({ data: { tag, count: 1, createdAt: db.serverDate() } })
|
||||
}
|
||||
return db.collection('topics').where({ tag }).update({ data: { count: _.inc(1) } })
|
||||
})
|
||||
)
|
||||
await Promise.all(batch).catch(() => {})
|
||||
}
|
||||
|
||||
return { code: 0, data: { _id: res._id, ...post } }
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/createPost/package.json
Normal file
1
cloudfunctions/createPost/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "createPost", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
76
cloudfunctions/getNearbyPets/index.js
Normal file
76
cloudfunctions/getNearbyPets/index.js
Normal file
@@ -0,0 +1,76 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
const _ = db.command
|
||||
|
||||
// 粗略经纬度范围换算(1度≈111km)
|
||||
function latDelta(km) { return km / 111 }
|
||||
function lngDelta(km, lat) { return km / (111 * Math.cos(lat * Math.PI / 180)) }
|
||||
|
||||
function calcDistance(lat1, lng1, lat2, lng2) {
|
||||
const R = 6371
|
||||
const dLat = (lat2 - lat1) * Math.PI / 180
|
||||
const dLng = (lng2 - lng1) * Math.PI / 180
|
||||
const a = Math.sin(dLat / 2) ** 2 +
|
||||
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) * Math.sin(dLng / 2) ** 2
|
||||
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)) * 1000 // meters
|
||||
}
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { latitude, longitude, radiusKm = 5 } = event
|
||||
|
||||
if (!latitude || !longitude) {
|
||||
return { code: -1, message: '缺少位置信息' }
|
||||
}
|
||||
|
||||
// 在线判断:60秒内更新过位置的用户为在线
|
||||
const onlineThreshold = new Date(Date.now() - 60 * 1000).toISOString()
|
||||
|
||||
try {
|
||||
const dLat = latDelta(radiusKm)
|
||||
const dLng = lngDelta(radiusKm, latitude)
|
||||
|
||||
const usersRes = await db.collection('users')
|
||||
.where(
|
||||
_.and([
|
||||
{ openid: _.neq(OPENID) }, // 排除自己
|
||||
{ 'lastLocation.latitude': _.gt(latitude - dLat).and(_.lt(latitude + dLat)) },
|
||||
{ 'lastLocation.longitude': _.gt(longitude - dLng).and(_.lt(longitude + dLng)) },
|
||||
{ pets: _.exists(true) },
|
||||
])
|
||||
)
|
||||
.field({
|
||||
openid: true, nickName: true, avatarUrl: true,
|
||||
lastLocation: true, lastSeen: true, isOnline: true,
|
||||
pets: true, location: true, bio: true,
|
||||
})
|
||||
.limit(30)
|
||||
.get()
|
||||
|
||||
const nearby = usersRes.data
|
||||
.map(user => {
|
||||
const userLat = user.lastLocation?.latitude
|
||||
const userLng = user.lastLocation?.longitude
|
||||
if (!userLat || !userLng) return null
|
||||
|
||||
const distance = calcDistance(latitude, longitude, userLat, userLng)
|
||||
if (distance > radiusKm * 1000) return null
|
||||
|
||||
return {
|
||||
userId: user.openid,
|
||||
user,
|
||||
pet: user.pets?.[0] || null,
|
||||
distance,
|
||||
isOnline: user.isOnline && user.lastSeen > onlineThreshold,
|
||||
location: { latitude: userLat, longitude: userLng },
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
.sort((a, b) => a.distance - b.distance)
|
||||
|
||||
return { code: 0, data: nearby }
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/getNearbyPets/package.json
Normal file
1
cloudfunctions/getNearbyPets/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "getNearbyPets", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
34
cloudfunctions/getPost/index.js
Normal file
34
cloudfunctions/getPost/index.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { postId } = event
|
||||
if (!postId) return { code: -1, message: '缺少 postId' }
|
||||
|
||||
try {
|
||||
const post = await db.collection('posts').doc(postId).get()
|
||||
if (!post.data) return { code: -1, message: '帖子不存在' }
|
||||
|
||||
const authorRes = await db.collection('users')
|
||||
.where({ openid: post.data.authorId })
|
||||
.field({ nickName: true, avatarUrl: true, openid: true, pets: true })
|
||||
.get()
|
||||
|
||||
const likeRes = await db.collection('likes')
|
||||
.where({ userId: OPENID, postId })
|
||||
.count()
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {
|
||||
...post.data,
|
||||
author: authorRes.data[0] || null,
|
||||
isLiked: likeRes.total > 0,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/getPost/package.json
Normal file
1
cloudfunctions/getPost/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "getPost", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
83
cloudfunctions/getPosts/index.js
Normal file
83
cloudfunctions/getPosts/index.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
const _ = db.command
|
||||
const PAGE_SIZE = 10
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { page = 0, type = 'hot', userId, latitude, longitude } = event
|
||||
|
||||
try {
|
||||
let query = db.collection('posts')
|
||||
const skip = page * PAGE_SIZE
|
||||
|
||||
if (userId) {
|
||||
// 某用户的帖子
|
||||
query = query.where({ authorId: userId })
|
||||
} else if (type === 'feed') {
|
||||
// 关注的用户帖子
|
||||
const followsRes = await db.collection('follows')
|
||||
.where({ followerId: OPENID })
|
||||
.field({ followeeId: true })
|
||||
.get()
|
||||
const followeeIds = followsRes.data.map(f => f.followeeId)
|
||||
followeeIds.push(OPENID)
|
||||
query = query.where({ authorId: _.in(followeeIds) })
|
||||
} else if (type === 'nearby' && latitude && longitude) {
|
||||
// 附近帖子:使用地理围栏查询(简化版,用 geo_near)
|
||||
query = query.where(
|
||||
_.and([
|
||||
{ 'location.latitude': _.gt(latitude - 0.1) },
|
||||
{ 'location.latitude': _.lt(latitude + 0.1) },
|
||||
{ 'location.longitude': _.gt(longitude - 0.15) },
|
||||
{ 'location.longitude': _.lt(longitude + 0.15) },
|
||||
])
|
||||
)
|
||||
}
|
||||
// type === 'hot' 全量按热度排序
|
||||
|
||||
const total = await query.count()
|
||||
const postsRes = await query
|
||||
.orderBy('createdAt', 'desc')
|
||||
.skip(skip)
|
||||
.limit(PAGE_SIZE)
|
||||
.get()
|
||||
|
||||
const posts = postsRes.data
|
||||
|
||||
// 批量获取作者信息
|
||||
const authorIds = [...new Set(posts.map(p => p.authorId))]
|
||||
const authorsRes = await db.collection('users')
|
||||
.where({ openid: _.in(authorIds) })
|
||||
.field({ nickName: true, avatarUrl: true, openid: true, pets: true, location: true })
|
||||
.get()
|
||||
const authorMap = {}
|
||||
authorsRes.data.forEach(u => { authorMap[u.openid] = u })
|
||||
|
||||
// 获取当前用户点赞状态
|
||||
const postIds = posts.map(p => p._id)
|
||||
const likesRes = await db.collection('likes')
|
||||
.where({ userId: OPENID, postId: _.in(postIds) })
|
||||
.field({ postId: true })
|
||||
.get()
|
||||
const likedSet = new Set(likesRes.data.map(l => l.postId))
|
||||
|
||||
const enriched = posts.map(p => ({
|
||||
...p,
|
||||
author: authorMap[p.authorId] || null,
|
||||
isLiked: likedSet.has(p._id),
|
||||
}))
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: {
|
||||
list: enriched,
|
||||
total: total.total,
|
||||
hasMore: skip + posts.length < total.total,
|
||||
},
|
||||
}
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/getPosts/package.json
Normal file
1
cloudfunctions/getPosts/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "getPosts", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
54
cloudfunctions/likePost/index.js
Normal file
54
cloudfunctions/likePost/index.js
Normal file
@@ -0,0 +1,54 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
const _ = db.command
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { postId } = event
|
||||
|
||||
if (!postId) return { code: -1, message: '缺少 postId' }
|
||||
|
||||
try {
|
||||
// 检查是否已点赞
|
||||
const existRes = await db.collection('likes')
|
||||
.where({ userId: OPENID, postId })
|
||||
.get()
|
||||
|
||||
let liked
|
||||
let delta
|
||||
|
||||
if (existRes.data.length > 0) {
|
||||
// 取消点赞
|
||||
await db.collection('likes').doc(existRes.data[0]._id).remove()
|
||||
delta = -1
|
||||
liked = false
|
||||
} else {
|
||||
// 点赞
|
||||
await db.collection('likes').add({
|
||||
data: {
|
||||
userId: OPENID,
|
||||
postId,
|
||||
createdAt: db.serverDate(),
|
||||
},
|
||||
})
|
||||
delta = 1
|
||||
liked = true
|
||||
}
|
||||
|
||||
// 原子更新帖子点赞数
|
||||
await db.collection('posts').doc(postId).update({
|
||||
data: { likeCount: _.inc(delta) },
|
||||
})
|
||||
|
||||
// 获取最新计数
|
||||
const postRes = await db.collection('posts').doc(postId).field({ likeCount: true }).get()
|
||||
|
||||
return {
|
||||
code: 0,
|
||||
data: { liked, count: postRes.data.likeCount },
|
||||
}
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/likePost/package.json
Normal file
1
cloudfunctions/likePost/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "likePost", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
48
cloudfunctions/login/index.js
Normal file
48
cloudfunctions/login/index.js
Normal file
@@ -0,0 +1,48 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID, APPID } = cloud.getWXContext()
|
||||
|
||||
try {
|
||||
// 查找或创建用户
|
||||
const userRes = await db.collection('users').where({ openid: OPENID }).get()
|
||||
let userInfo
|
||||
|
||||
if (userRes.data.length === 0) {
|
||||
// 新用户 - 创建档案
|
||||
const newUser = {
|
||||
openid: OPENID,
|
||||
nickName: event.nickName || '宠物爱好者',
|
||||
avatarUrl: event.avatarUrl || '',
|
||||
handle: '',
|
||||
location: '',
|
||||
bio: '',
|
||||
stats: { posts: 0, friends: 0, likes: 0 },
|
||||
lastLocation: null,
|
||||
isOnline: true,
|
||||
lastSeen: new Date().toISOString(),
|
||||
pets: [],
|
||||
createdAt: db.serverDate(),
|
||||
updatedAt: db.serverDate(),
|
||||
}
|
||||
const addRes = await db.collection('users').add({ data: newUser })
|
||||
userInfo = { _id: addRes._id, ...newUser }
|
||||
} else {
|
||||
// 老用户 - 更新在线状态
|
||||
userInfo = userRes.data[0]
|
||||
await db.collection('users').doc(userInfo._id).update({
|
||||
data: {
|
||||
isOnline: true,
|
||||
lastSeen: db.serverDate(),
|
||||
updatedAt: db.serverDate(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return { code: 0, data: { userInfo } }
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
9
cloudfunctions/login/package.json
Normal file
9
cloudfunctions/login/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "login",
|
||||
"version": "1.0.0",
|
||||
"description": "微信云函数 - 用户登录",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"wx-server-sdk": "~2.1.2"
|
||||
}
|
||||
}
|
||||
53
cloudfunctions/sendGreeting/index.js
Normal file
53
cloudfunctions/sendGreeting/index.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { toUserId, petId } = event
|
||||
|
||||
if (!toUserId) return { code: -1, message: '缺少目标用户' }
|
||||
if (toUserId === OPENID) return { code: -1, message: '不能给自己打招呼' }
|
||||
|
||||
try {
|
||||
// 防刷:5分钟内同一对用户只能打一次招呼
|
||||
const cooldownMs = 5 * 60 * 1000
|
||||
const recent = await db.collection('messages')
|
||||
.where({
|
||||
fromId: OPENID,
|
||||
toId: toUserId,
|
||||
type: 'greeting',
|
||||
createdAt: db.command.gt(new Date(Date.now() - cooldownMs).toISOString()),
|
||||
})
|
||||
.count()
|
||||
|
||||
if (recent.total > 0) {
|
||||
return { code: -1, message: '打招呼太频繁了,稍后再试' }
|
||||
}
|
||||
|
||||
// 获取发送者信息
|
||||
const senderRes = await db.collection('users')
|
||||
.where({ openid: OPENID })
|
||||
.field({ nickName: true, pets: true })
|
||||
.get()
|
||||
const sender = senderRes.data[0]
|
||||
const pet = sender?.pets?.find(p => p._id === petId) || sender?.pets?.[0]
|
||||
|
||||
const greeting = `${sender?.nickName || '小伙伴'} 想认识你的宠物!${pet ? `TA 的 ${pet.name} 向你打了个招呼 🐾` : '来聊聊吧 🐾'}`
|
||||
|
||||
await db.collection('messages').add({
|
||||
data: {
|
||||
fromId: OPENID,
|
||||
toId: toUserId,
|
||||
content: greeting,
|
||||
type: 'greeting',
|
||||
isRead: false,
|
||||
createdAt: db.serverDate(),
|
||||
},
|
||||
})
|
||||
|
||||
return { code: 0, data: null }
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/sendGreeting/package.json
Normal file
1
cloudfunctions/sendGreeting/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "sendGreeting", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
25
cloudfunctions/updateLocation/index.js
Normal file
25
cloudfunctions/updateLocation/index.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const cloud = require('wx-server-sdk')
|
||||
cloud.init({ env: cloud.DYNAMIC_CURRENT_ENV })
|
||||
const db = cloud.database()
|
||||
|
||||
exports.main = async (event, context) => {
|
||||
const { OPENID } = cloud.getWXContext()
|
||||
const { latitude, longitude } = event
|
||||
|
||||
if (!latitude || !longitude) return { code: -1, message: '缺少位置' }
|
||||
|
||||
try {
|
||||
await db.collection('users')
|
||||
.where({ openid: OPENID })
|
||||
.update({
|
||||
data: {
|
||||
lastLocation: { latitude, longitude },
|
||||
isOnline: true,
|
||||
lastSeen: db.serverDate(),
|
||||
},
|
||||
})
|
||||
return { code: 0, data: null }
|
||||
} catch (e) {
|
||||
return { code: -1, message: e.message }
|
||||
}
|
||||
}
|
||||
1
cloudfunctions/updateLocation/package.json
Normal file
1
cloudfunctions/updateLocation/package.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "name": "updateLocation", "version": "1.0.0", "main": "index.js", "dependencies": { "wx-server-sdk": "~2.1.2" } }
|
||||
Reference in New Issue
Block a user