提交 2a5d4ce4 编写于 作者: DCloud_JSON's avatar DCloud_JSON

替换 SSEChannel 为 sseChannel

上级 fe468eca
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
"vueVersion" : "3", "vueVersion" : "3",
"h5" : { "h5" : {
"unipush" : { "unipush" : {
"enable" : false "enable" : true
} }
}, },
"fallbackLocale" : "zh-Hans" "fallbackLocale" : "zh-Hans"
......
<template> <template>
<view class="page"> <view class="page">
<view class="container"> <view class="container">
<view v-if="isWidescreen" class="header">uni-ai-chat</view> <view v-if="isWidescreen" class="header">uni-ai-chat</view>
<text class="noData" v-if="msgList.length === 0">没有对话记录</text> <text class="noData" v-if="msgList.length === 0">没有对话记录</text>
<scroll-view :scroll-into-view="scrollIntoView" scroll-y="true" class="msg-list" :enable-flex="true"> <scroll-view :scroll-into-view="scrollIntoView" scroll-y="true" class="msg-list" :enable-flex="true">
<view v-for="(msg,index) in msgList" class="msg-item" :key="index"> <view v-for="(msg,index) in msgList" class="msg-item" :key="index">
<view class="create_time-box"> <view class="create_time-box">
<uni-dateformat class="create_time" :date="msg.create_time" format="MM/dd hh:mm:ss"></uni-dateformat> <uni-dateformat class="create_time" :date="msg.create_time" format="MM/dd hh:mm:ss"></uni-dateformat>
</view> </view>
<view :class="{reverse:!msg.isAi}"> <view :class="{reverse:!msg.isAi}">
<view class="userInfo"> <view class="userInfo">
<image class="avatar" :src="msg.isAi?'../../static/uni-ai.png':'../../static/avatar.png'" mode="widthFix"></image> <image class="avatar" :src="msg.isAi?'../../static/uni-ai.png':'../../static/avatar.png'" mode="widthFix"></image>
</view> </view>
<view class="content"> <view class="content">
<!-- <text class="copy" @click="copy">复制</text> --> <!-- <text class="copy" @click="copy">复制</text> -->
<uni-ai-msg :md="msg.content" :show-cursor="index == msgList.length-1 && msg.isAi && sseIndex"></uni-ai-msg> <uni-ai-msg :md="msg.content" :show-cursor="index == msgList.length-1 && msg.isAi && sseIndex"></uni-ai-msg>
<view v-if="index == msgList.length-1 && adpid && msg.insufficientPoints"> <view v-if="index == msgList.length-1 && adpid && msg.insufficientPoints">
<uni-ad-rewarded-video :adpid="adpid" @onAdClose="onAdClose"></uni-ad-rewarded-video> <uni-ad-rewarded-video :adpid="adpid" @onAdClose="onAdClose"></uni-ad-rewarded-video>
</view> </view>
</view> </view>
<uni-icons v-if="index == msgList.length-1 && !msg.isAi && msg.state != 100 && msgStateIcon(msg)" <uni-icons v-if="index == msgList.length-1 && !msg.isAi && msg.state != 100 && msgStateIcon(msg)"
@click="msg.state == -100 ? retriesSendMsg() : ''" :color="msg.state===0?'#999':'#d22'" @click="msg.state == -100 ? retriesSendMsg() : ''" :color="msg.state===0?'#999':'#d22'"
:type="msgStateIcon(msg)" class="msgStateIcon"> :type="msgStateIcon(msg)" class="msgStateIcon">
</uni-icons> </uni-icons>
</view> </view>
</view> </view>
<view class="tip-ai-ing" v-if="msgList.length && msgList.length%2 !== 0"> <view class="tip-ai-ing" v-if="msgList.length && msgList.length%2 !== 0">
<text>uni-ai正在思考中...</text> <text>uni-ai正在思考中...</text>
<view v-if="NODE_ENV == 'development' && !stream"> <view v-if="NODE_ENV == 'development' && !stream">
如需提速,请开通<uni-link class="uni-link" href="https://uniapp.dcloud.net.cn/uniCloud/uni-ai-chat.html" text="[流式响应]"></uni-link> 如需提速,请开通<uni-link class="uni-link" href="https://uniapp.dcloud.net.cn/uniCloud/uni-ai-chat.html" text="[流式响应]"></uni-link>
</view> </view>
</view> </view>
<view id="last-msg-item"></view> <view id="last-msg-item"></view>
</scroll-view> </scroll-view>
<view class="foot-box"> <view class="foot-box">
<view class="menu" v-if="isWidescreen"> <view class="menu" v-if="isWidescreen">
<view class="trash menu-item"> <view class="trash menu-item">
...@@ -53,56 +53,56 @@ ...@@ -53,56 +53,56 @@
<text v-if="isWidescreen" class="send-btn-tip">↵ 发送 / shift + ↵ 换行</text> <text v-if="isWidescreen" class="send-btn-tip">↵ 发送 / shift + ↵ 换行</text>
<button @click="beforeSendMsg" :disabled="inputBoxDisabled || !content" class="send" type="primary">发送</button> <button @click="beforeSendMsg" :disabled="inputBoxDisabled || !content" class="send" type="primary">发送</button>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import config from '@/config.js'; import config from '@/config.js';
const {adpid} = config const {adpid} = config
export default { export default {
data() { data() {
return { return {
scrollIntoView: "", scrollIntoView: "",
msgList: [], msgList: [],
content: "", content: "",
sseIndex: 0, sseIndex: 0,
stream:true, stream:true,
isWidescreen:false, isWidescreen:false,
adpid adpid
} }
}, },
computed: { computed: {
inputBoxDisabled() { inputBoxDisabled() {
if (this.sseIndex !== 0) { if (this.sseIndex !== 0) {
return true return true
} }
return !!(this.msgList.length && this.msgList.length%2 !== 0) return !!(this.msgList.length && this.msgList.length%2 !== 0)
}, },
placeholderText() { placeholderText() {
if (this.inputBoxDisabled) { if (this.inputBoxDisabled) {
return 'uni-ai正在回复中' return 'uni-ai正在回复中'
} else { } else {
// #ifdef H5 // #ifdef H5
return window.innerWidth > 960 ? '请输入内容,ctrl + enter 发送' : '请输入要发给uni-ai的内容' return window.innerWidth > 960 ? '请输入内容,ctrl + enter 发送' : '请输入要发给uni-ai的内容'
// #endif // #endif
return '请输入要发给uni-ai的内容' return '请输入要发给uni-ai的内容'
} }
}, },
NODE_ENV(){ NODE_ENV(){
return process.env.NODE_ENV return process.env.NODE_ENV
} }
}, },
watch: { watch: {
msgList:{ msgList:{
handler(msgList) { handler(msgList) {
uni.setStorageSync('uni-ai-msg', msgList) uni.setStorageSync('uni-ai-msg', msgList)
}, },
deep:true deep:true
} }
}, },
async mounted() { async mounted() {
if(this.adpid && uniCloud.getCurrentUserInfo().tokenExpired > Date.now()){ if(this.adpid && uniCloud.getCurrentUserInfo().tokenExpired > Date.now()){
let db = uniCloud.databaseForJQL(); let db = uniCloud.databaseForJQL();
...@@ -120,17 +120,17 @@ ...@@ -120,17 +120,17 @@
// console.log('score',score); // console.log('score',score);
// for (let i = 0; i < 15; i++) { // for (let i = 0; i < 15; i++) {
// this.msgList.push({ // this.msgList.push({
// isAi: i % 2 == true, // isAi: i % 2 == true,
// content: "1-" + i // content: "1-" + i
// }) // })
// } // }
this.msgList = uni.getStorageSync('uni-ai-msg') || [] this.msgList = uni.getStorageSync('uni-ai-msg') || []
// this.msgList.pop() // this.msgList.pop()
// console.log('this.msgList', this.msgList); // console.log('this.msgList', this.msgList);
this.showLastMsg() this.showLastMsg()
// #ifdef H5 // #ifdef H5
...@@ -167,7 +167,7 @@ ...@@ -167,7 +167,7 @@
this.isWidescreen = matches; this.isWidescreen = matches;
}) })
// #endif // #endif
}, },
methods: { methods: {
//检查是否开通uni-push;决定是否启用stream //检查是否开通uni-push;决定是否启用stream
async checkIsOpenPush(){ async checkIsOpenPush(){
...@@ -233,8 +233,8 @@ ...@@ -233,8 +233,8 @@
async retriesSendMsg() { async retriesSendMsg() {
// 检查是否开通uni-push;决定是否启用stream // 检查是否开通uni-push;决定是否启用stream
await this.checkIsOpenPush() await this.checkIsOpenPush()
this.send() this.send()
}, },
async beforeSendMsg() { async beforeSendMsg() {
// 如果开启了广告位需要登录 // 如果开启了广告位需要登录
if(this.adpid){ if(this.adpid){
...@@ -277,57 +277,57 @@ ...@@ -277,57 +277,57 @@
icon: 'none' icon: 'none'
}); });
} }
this.msgList.push({ this.msgList.push({
isAi: false, isAi: false,
content: this.content, content: this.content,
state: 0, state: 0,
create_time: Date.now() create_time: Date.now()
}) })
this.showLastMsg() this.showLastMsg()
// 清空文本内容 // 清空文本内容
this.$nextTick(() => { this.$nextTick(() => {
this.content = '' this.content = ''
}) })
this.send() this.send()
}, },
async send() { async send() {
let messages = [] let messages = []
// 复制一份,消息列表数据 // 复制一份,消息列表数据
let msgs = JSON.parse(JSON.stringify(this.msgList)) let msgs = JSON.parse(JSON.stringify(this.msgList))
// 带总结的消息 index // 带总结的消息 index
let findIndex = [...msgs].reverse().findIndex(item => item.summarize) let findIndex = [...msgs].reverse().findIndex(item => item.summarize)
// console.log('findIndex', findIndex) // console.log('findIndex', findIndex)
if (findIndex != -1) { if (findIndex != -1) {
let aiSummaryIndex = msgs.length - findIndex - 1 let aiSummaryIndex = msgs.length - findIndex - 1
// console.log('aiSummaryIndex', aiSummaryIndex) // console.log('aiSummaryIndex', aiSummaryIndex)
msgs[aiSummaryIndex].content = msgs[aiSummaryIndex].summarize msgs[aiSummaryIndex].content = msgs[aiSummaryIndex].summarize
// 拿最后一条带直接的消息作为与ai对话的msg body // 拿最后一条带直接的消息作为与ai对话的msg body
msgs = msgs.splice(aiSummaryIndex, msgs.length - 1) msgs = msgs.splice(aiSummaryIndex, msgs.length - 1)
} else { } else {
// 如果未总结过就直接从末尾拿10条 // 如果未总结过就直接从末尾拿10条
msgs = msgs.splice(-10) msgs = msgs.splice(-10)
} }
messages = msgs.map(item => { messages = msgs.map(item => {
let role = "user" let role = "user"
if (item.isAi) { if (item.isAi) {
role = item.summarize ? 'system' : 'assistant' role = item.summarize ? 'system' : 'assistant'
} }
return { return {
content: item.content, content: item.content,
role role
} }
}) })
console.log('send to ai messages:', messages); console.log('send to ai messages:', messages);
let SSEChannel = false; let sseChannel = false;
if(this.stream){ if(this.stream){
SSEChannel = new uniCloud.SSEChannel() // 创建消息通道 sseChannel = new uniCloud.SSEChannel() // 创建消息通道
// console.log('SSEChannel',SSEChannel); // console.log('sseChannel',sseChannel);
SSEChannel.on('message', (message) => { // 监听message事件 sseChannel.on('message', (message) => { // 监听message事件
// console.log('on message', message); // console.log('on message', message);
if (this.sseIndex === 0) { if (this.sseIndex === 0) {
this.msgList.push({ this.msgList.push({
...@@ -344,7 +344,7 @@ ...@@ -344,7 +344,7 @@
} }
this.sseIndex++ this.sseIndex++
}) })
SSEChannel.on('end', (e) => { // 监听end事件,如果云端执行end时传了message,会在客户端end事件内收到传递的消息 sseChannel.on('end', (e) => { // 监听end事件,如果云端执行end时传了message,会在客户端end事件内收到传递的消息
// console.log('on end', e); // console.log('on end', e);
if(e && (e.summarize || e.insufficientPoints)){ if(e && (e.summarize || e.insufficientPoints)){
this.updateLastMsg(lastMsg=>{ this.updateLastMsg(lastMsg=>{
...@@ -358,102 +358,102 @@ ...@@ -358,102 +358,102 @@
this.sseIndex = 0 this.sseIndex = 0
this.showLastMsg() this.showLastMsg()
}) })
await SSEChannel.open() // 等待通道开启 await sseChannel.open() // 等待通道开启
} }
const uniAiChat = uniCloud.importObject("uni-ai-chat",{ const uniAiChat = uniCloud.importObject("uni-ai-chat",{
customUI:true customUI:true
}) })
uniAiChat.send({ uniAiChat.send({
messages, messages,
SSEChannel sseChannel
}) })
.then(res => { .then(res => {
this.updateLastMsg({state:100}) this.updateLastMsg({state:100})
if (!SSEChannel) { if (!sseChannel) {
// console.log(res, res.reply); // console.log(res, res.reply);
this.msgList.push({ this.msgList.push({
isAi: true, isAi: true,
content: res.data.reply, content: res.data.reply,
summarize: res.data.summarize, summarize: res.data.summarize,
create_time: Date.now(), create_time: Date.now(),
insufficientPoints:res.data.insufficientPoints insufficientPoints:res.data.insufficientPoints
}) })
this.showLastMsg() this.showLastMsg()
} }
}) })
.catch(e => { .catch(e => {
console.log(e); console.log(e);
this.updateLastMsg({state:-100}) this.updateLastMsg({state:-100})
uni.showModal({ uni.showModal({
content: JSON.stringify(e.message), content: JSON.stringify(e.message),
showCancel: false showCancel: false
}); });
}) })
}, },
showLastMsg() { showLastMsg() {
this.$nextTick(() => { this.$nextTick(() => {
this.scrollIntoView = "last-msg-item" this.scrollIntoView = "last-msg-item"
this.$nextTick(() => { this.$nextTick(() => {
this.scrollIntoView = "" this.scrollIntoView = ""
}) })
}) })
}, },
msgStateIcon(msg) { msgStateIcon(msg) {
switch (msg.state) { switch (msg.state) {
case 0: case 0:
// 发送中 // 发送中
return 'spinner-cycle' return 'spinner-cycle'
break; break;
case -100: case -100:
// 发送失败 // 发送失败
return 'refresh-filled' return 'refresh-filled'
break; break;
case -200: case -200:
// 禁止发送(内容不合法) // 禁止发送(内容不合法)
return 'info-filled' return 'info-filled'
break; break;
default: default:
return false return false
break; break;
} }
}, },
clear() { clear() {
uni.showModal({ uni.showModal({
title: "确认要清空聊天记录?", title: "确认要清空聊天记录?",
content: '本操作不可撤销', content: '本操作不可撤销',
complete: (e) => { complete: (e) => {
if (e.confirm) { if (e.confirm) {
this.msgList = [] this.msgList = []
} }
} }
}); });
} }
} }
} }
</script> </script>
<style lang="scss"> <style lang="scss">
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
view, view,
textarea, textarea,
button, button,
.page .page
{ {
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;
} }
/* #endif */ /* #endif */
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
page, page,
/* #endif */ /* #endif */
.page, .page,
.container { .container {
background-color: #efefef; background-color: #efefef;
/* #ifdef APP-NVUE */ /* #ifdef APP-NVUE */
flex: 1; flex: 1;
/* #endif */ /* #endif */
...@@ -466,147 +466,147 @@ ...@@ -466,147 +466,147 @@
height: calc(100vh - 44px); height: calc(100vh - 44px);
/* #endif */ /* #endif */
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
} }
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
.container { .container {
background-color: #FAFAFA; background-color: #FAFAFA;
} }
/* #endif */ /* #endif */
.foot-box { .foot-box {
width: 750rpx; width: 750rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 10px 0px; padding: 10px 0px;
background-color: #FFF; background-color: #FFF;
} }
.foot-box-content{ .foot-box-content{
justify-content: space-around; justify-content: space-around;
} }
.textarea-box { .textarea-box {
padding: 8px 10px; padding: 8px 10px;
background-color: #f9f9f9; background-color: #f9f9f9;
border-radius: 5px; border-radius: 5px;
} }
.textarea-box .textarea { .textarea-box .textarea {
max-height: 100px; max-height: 100px;
font-size: 14px; font-size: 14px;
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
overflow: auto; overflow: auto;
/* #endif */ /* #endif */
width: 450rpx; width: 450rpx;
} }
/* #ifdef H5 */ /* #ifdef H5 */
/*隐藏滚动条*/ /*隐藏滚动条*/
.textarea-box .textarea::-webkit-scrollbar { .textarea-box .textarea::-webkit-scrollbar {
width: 0; width: 0;
} }
/* #endif */ /* #endif */
.input-placeholder { .input-placeholder {
color: #bbb; color: #bbb;
} }
.trash, .trash,
.send { .send {
width: 50px; width: 50px;
height: 30px; height: 30px;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
flex-shrink: 0; flex-shrink: 0;
} }
.trash { .trash {
width: 30rpx; width: 30rpx;
margin-left: 10rpx; margin-left: 10rpx;
} }
.send { .send {
color: #FFF; color: #FFF;
border-radius: 4px; border-radius: 4px;
display: flex; display: flex;
margin: 0; margin: 0;
padding: 0; padding: 0;
font-size: 14px; font-size: 14px;
margin-right: 20rpx; margin-right: 20rpx;
} }
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
.send::after { .send::after {
display: none; display: none;
} }
/* #endif */ /* #endif */
.msg-list { .msg-list {
flex: 1; flex: 1;
height: 1px; height: 1px;
width: 750rpx; width: 750rpx;
} }
.userInfo { .userInfo {
flex-direction: column; flex-direction: column;
} }
.msg-item { .msg-item {
position: relative; position: relative;
width: 750rpx; width: 750rpx;
flex-direction: column; flex-direction: column;
padding: 0 15px; padding: 0 15px;
padding-bottom: 15px; padding-bottom: 15px;
} }
.msgStateIcon { .msgStateIcon {
position: relative; position: relative;
top: 5px; top: 5px;
right: 5px; right: 5px;
align-self: center; align-self: center;
} }
.avatar { .avatar {
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 2px; border-radius: 2px;
} }
.create_time { .create_time {
font-size: 12px; font-size: 12px;
padding: 5px 0; padding: 5px 0;
padding-top: 0; padding-top: 0;
color: #aaa; color: #aaa;
text-align: center; text-align: center;
width:750rpx; width:750rpx;
/* #ifdef MP */ /* #ifdef MP */
display: flex; display: flex;
/* #endif */ /* #endif */
justify-content: center; justify-content: center;
} }
.content { .content {
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
max-width: 550rpx; max-width: 550rpx;
/* #endif */ /* #endif */
background-color: #FFF; background-color: #FFF;
border-radius: 5px; border-radius: 5px;
padding: 12px 10px; padding: 12px 10px;
margin-left: 10px; margin-left: 10px;
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
word-break: break-all; word-break: break-all;
user-select: text; user-select: text;
cursor: text; cursor: text;
/* #endif */ /* #endif */
} }
/* #ifndef APP-NVUE */ /* #ifndef APP-NVUE */
.content { .content {
display: inline; display: inline;
} }
.content ::v-deep rich-text{ .content ::v-deep rich-text{
...@@ -619,29 +619,29 @@ ...@@ -619,29 +619,29 @@
.content * { .content * {
display: inline; display: inline;
} }
/* #endif */ /* #endif */
.reverse { .reverse {
flex-direction: row-reverse; flex-direction: row-reverse;
} }
.reverse .content { .reverse .content {
margin-left: 0; margin-left: 0;
margin-right: 10px; margin-right: 10px;
} }
.reverse-align { .reverse-align {
align-items: flex-end; align-items: flex-end;
} }
.noData { .noData {
margin-top: 15px; margin-top: 15px;
text-align: center; text-align: center;
width: 750rpx; width: 750rpx;
color: #aaa; color: #aaa;
font-size: 12px; font-size: 12px;
justify-content: center; justify-content: center;
} }
.tip-ai-ing { .tip-ai-ing {
align-items: center; align-items: center;
......
...@@ -61,8 +61,8 @@ module.exports = { ...@@ -61,8 +61,8 @@ module.exports = {
requestId: this.getUniCloudRequestId() requestId: this.getUniCloudRequestId()
}) })
this.textSecCheck = async (content)=>{ this.textSecCheck = async (content)=>{
let {SSEChannel} = this.getParams()[0]||{} let {sseChannel} = this.getParams()[0]||{}
if(SSEChannel){ if(sseChannel){
throw "流式响应模式,内容安全识别功能无效" throw "流式响应模式,内容安全识别功能无效"
} }
// 检测文本 // 检测文本
...@@ -109,9 +109,9 @@ module.exports = { ...@@ -109,9 +109,9 @@ module.exports = {
} }
}else if(error == 'insufficientPoints'){ }else if(error == 'insufficientPoints'){
let reply = "积分不足,请看完激励视频广告后再试" let reply = "积分不足,请看完激励视频广告后再试"
let {SSEChannel} = this.getParams()[0]||{} let {sseChannel} = this.getParams()[0]||{}
if(SSEChannel){ if(sseChannel){
const channel = uniCloud.deserializeSSEChannel(SSEChannel) const channel = uniCloud.deserializeSSEChannel(sseChannel)
await channel.write(reply) await channel.write(reply)
await channel.end({ await channel.end({
"insufficientPoints":true "insufficientPoints":true
...@@ -147,7 +147,7 @@ module.exports = { ...@@ -147,7 +147,7 @@ module.exports = {
}, },
async send({ async send({
messages, messages,
SSEChannel sseChannel
}) { }) {
// 初次调试时,可不从客户端获取数据,直接使用下面写死在云函数里的数据 // 初次调试时,可不从客户端获取数据,直接使用下面写死在云函数里的数据
// messages = [{ // messages = [{
...@@ -165,13 +165,13 @@ module.exports = { ...@@ -165,13 +165,13 @@ module.exports = {
let {llm,chatCompletionOptions} = config let {llm,chatCompletionOptions} = config
return await chatCompletion({ return await chatCompletion({
messages, //消息内容 messages, //消息内容
SSEChannel, //sse渠道对象 sseChannel, //sse渠道对象
llm llm
}) })
async function chatCompletion({ async function chatCompletion({
messages, messages,
summarize = false, summarize = false,
SSEChannel = false, sseChannel = false,
llm llm
}) { }) {
...@@ -179,13 +179,13 @@ module.exports = { ...@@ -179,13 +179,13 @@ module.exports = {
let res = await llmManager.chatCompletion({ let res = await llmManager.chatCompletion({
...chatCompletionOptions, ...chatCompletionOptions,
messages, messages,
stream: SSEChannel !== false stream: sseChannel !== false
}) })
if (SSEChannel) { if (sseChannel) {
let reply = "" let reply = ""
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const channel = uniCloud.deserializeSSEChannel(SSEChannel) const channel = uniCloud.deserializeSSEChannel(sseChannel)
res.on('message', async (message) => { res.on('message', async (message) => {
// await channel.write(message) // await channel.write(message)
// console.log('---message----', message) // console.log('---message----', message)
...@@ -256,7 +256,7 @@ module.exports = { ...@@ -256,7 +256,7 @@ module.exports = {
messages, messages,
summarize: true, summarize: true,
stream: false, stream: false,
SSEChannel: false sseChannel: false
}) })
return res.reply return res.reply
} }
......
{
"bsonType": "object",
"required": [
"_id",
"value"
],
"properties": {
"_id": {
"bsonType": "string",
"description": "自动生成的id"
},
"app_id": {
"bsonType": "string",
"description": "客户端DCloud AppId"
},
"device_id": {
"bsonType": "string",
"description": "客户端设备id"
},
"private_key": {
"bsonType": "string",
"description": "私钥,仅保存在云端"
},
"public_key": {
"bsonType": "string",
"description": "公钥,下发给客户端使用"
},
"create_date": {
"bsonType": "timestamp",
"description": "创建时间"
}
},
"version": "0.0.1"
}
\ No newline at end of file
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
"bsonType": "object",
"required": [],
"permission": {
"read": false,
"create": false,
"update": false,
"delete": false
},
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"user_id":{
"bsonType": "string",
"description": "用户id"
},
"balance":{
"bsonType": "int",
"description": "剩余可对话的次数"
},
"update_time":{
"bsonType": "timestamp",
"forceDefaultValue":{
"$env": "now"
}
}
}
}
\ No newline at end of file
{
"bsonType": "object",
"required": [
"user_id",
"score",
"balance"
],
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"user_id": {
"bsonType": "string",
"description": "用户id,参考uni-id-users表"
},
"score": {
"bsonType": "int",
"description": "本次变化的积分"
},
"type": {
"bsonType": "int",
"enum": [
1,
2
],
"description": "积分类型 1:收入 2:支出"
},
"balance": {
"bsonType": "int",
"description": "变化后的积分余额"
},
"comment": {
"bsonType": "string",
"description": "备注,说明积分新增、消费的缘由",
"trim": "both"
},
"create_date": {
"bsonType": "timestamp",
"description": "创建时间",
"forceDefaultValue": {
"$env": "now"
}
}
},
"version": "0.0.1"
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册