【微信小程序】每日心情笔记
个人团队的比赛项目,仅供学习交流使用
一、项目基本介绍
1. 项目简介
一款基于微信小程序的轻量化笔记工具,旨在帮助用户通过记录每日心情和事件,更好地管理情绪和生活。用户可以根据日期和心情分类(如开心、平静、难过等)记录笔记,并随时查看历史记录。同时,项目还包含一个后台运营端小程序,用于数据统计、用户行为分析、用户反馈收集与回复,为运营决策提供支持。代码已上传至GitHub
2. 技术栈
微信小程序原生开发(JS、WXML、WXSS)、SpringBoot、MySQL、RESTful API、Postman接口测试、Axure
3. 项目功能
(1)用户端
微信授权登录
主界面:该功能模块主要包括显示日历、显示笔记、删除单条笔记。
-
显示日历:日历供用户选择查看日期,当日总体心情为日期小角标,用户点击日期即可在页面下方显示该日笔记。
-
显示笔记:底部笔记栏呈现所选日按修改时间进行排序的笔记(默认为当日),若无笔记出现"今日还未写笔记噢!"提示。
-
删除单条笔记:右滑笔记出现删除按钮,并弹出提示框。
个人中心:该功能模块主要包括反馈、分享、我的信箱与退出登录。
-
反馈:用户在反馈渠道提出意见。
-
分享:将小程序转发给微信好友。
-
我的信箱:管理员向用户下发通知,用户在我的信箱查看
-
退出登录:退出登录后再次登录需要再次授权。
笔记记录:该功能模块主要功能包括心情选择和写笔记。
-
心情选择:用户可在心情图案列表中选择本条笔记记录时的心情;若本条笔记为当日第二条及以上,自动弹出当日总体心情选择界面;在日历上方可以改变总体心情,若没有选择总体心情则会出现"今日还未选择总体心情哦"提示。
-
写/修改笔记:用户可自由进行笔记记录或修改笔记内容及此条笔记心情。
-
(2)开发者端
微信授权与密钥登陆
官方通知:发布官方通知下发到每一个用户
反馈回执:针对不同用户的返回发送回执
二、项目代码分析
1. 项目代码架构
前端小程序项目结构(以用户端为例)
后端Java项目结构
2.前端代码分析(以用户端主页面为例)
main.js
const util = require('../../utils/util.js')
var app = getApp()
Page({data: {avatarUrl: "/images/头像.png",nickName: '未登录',date: null,daymood: '今日还未选心情',moodList: new Array(),noteList: new Array(),noteisHave: false,year: 0,month: 0,head: ['日', '一', '二', '三', '四', '五', '六'],dateArr: [],isToday: 0,pickToday: 0,isTodayWeek: false,todayIndex: 0,nottoday: false,havenote:'今日还未写笔记哦!'},onShow() {let date = new Date();let now = new Date();let year = now.getFullYear();let month = now.getMonth() + 1;console.log(0)this.dateInit();this.setData({year: year,month: month,isToday: '' + year + '-' + month + '-' + now.getDate(),avatarUrl: app.globalData.avatarUrl,nickName: app.globalData.nickName,date: util.formatDate(date)})console.log(3)this.daymoodinit(util.formatYearDate(now)[0]);console.log(5)this.noteinit(util.formatYearDate(now)[0]);},noteinit(date) {var that = this//请求获取笔记列表wx.request({url: app.globalData.url + '/getNoteList',data: {uid: wx.getStorageSync('uid'),date: date},header: {'content-type': 'application/json'},success(res) {console.log(6)//获取后端传入的笔记列表var notelist = res.data.noteList//如果笔记列表不为空if (notelist) {//创建微信小程序存储数组值的变量var noteList = new Array();//创建构造函数notefunction Note(nid, context, time, mood) {this.nid = nid;this.context = context;this.time = time;this.mood = mood;}//遍历后端传来的值,将其加到我的数据中for (var i = 0; i < notelist.length; i++) {//对心情处理notelist[i].mood = '/images/' + notelist[i].mood + '.png';//赋值var note = new Note(notelist[i].nid, notelist[i].context+'...', notelist[i].time, notelist[i].mood)noteList[i] = note}//设置值that.setData({//笔记列表noteList: noteList,noteisHave: true})}}})},daymoodinit(date) {var that = thiswx.request({//获取某日的总体心情url: app.globalData.url + '/gDateMood',data: {uid: wx.getStorageSync('uid'),date: date},header: {'content-type': 'application/json'},success(res) {console.log(4)if (res.data.daymood) {if (date == util.formatYearDate(new Date())) {that.setData({daymood: '今日心情:' + res.data.daymood})} else {that.setData({daymood: '该日心情:' + res.data.daymood})}} else {if (date == util.formatYearDate(new Date())) {that.setData({daymood: '今日还未选心情'})} else {that.setData({daymood: '该日未记录心情'})}}}//为测试所用,正式直接删掉// fail() {// that.setData({// daymood: '今日心情:开心'// })// }})},backnow() {var date = {date: util.formatYearDate(new Date()).toString()}this.date(date)//自动展示为当前月份的日历页面this.dateInit()this.setData({nottoday: false})},date(e) {//判断是从组件传的值(即点击日期)还是从函数传的值(即点击回到今日)if (e.date) {var date = e.date} else {var date = e.currentTarget.dataset.date}//转换日期var dates = date.split("-")var month = dates[1]var day = dates[2]var thisdate = month + '月' + day + '日'if(date == util.formatYearDate(new Date())){this.setData({noteisHave: false,pickToday: date,date: thisdate,nottoday: false,havenote:'今日还未写笔记哦!'})}else{this.setData({noteisHave: false,pickToday: date,date: thisdate,nottoday: true,havenote:'该日还未写笔记哦!'})}//修改页面总体心情this.daymoodinit(date)//向后端请求日期下的笔记列表this.noteinit(date)},add() {wx.navigateTo({url: '/pages/notemood/notemood'})},daymood() {wx.showModal({title: '只能修改今日总体心情',content: '确认要修改吗?',success(res) {if (res.confirm) {wx.navigateTo({url: '/pages/daymood/daymood'})}}})},mine() {wx.navigateTo({url: '/pages/mine/mine',})},changenote(e) {//需向后端传入标识,表示是第一次进入修改笔记页面,目的为保证渲染层的正确var note = e.currentTarget.dataset.notevar nid = note.nidvar time = note.timevar mood = note.moodwx.redirectTo({url: '/pages/note/note?mood=' + mood + '&nid=' + nid + '&time=' + time + '&flag=1',})},dateInit: function (setYear, setMonth) {//全部时间的月份都是按0~11基准,显示月份才+1let dateArr = []; //需要遍历的日历数组数据let arrLen = 0; //dateArr的数组长度let now = setYear ? new Date(setYear, setMonth) : new Date();let year = setYear || now.getFullYear();let nextYear = 0;let month = setMonth || now.getMonth(); //没有+1方便后面计算当月总天数let nextMonth = (month + 1) > 11 ? 1 : (month + 1);let startWeek = new Date(year + ',' + (month + 1) + ',' + 1).getDay(); //目标月1号对应的星期let dayNums = new Date(year, nextMonth, 0).getDate(); //获取目标月有多少天let obj = {};let num = 0;if (month + 1 > 11) {nextYear = year + 1;dayNums = new Date(nextYear, nextMonth, 0).getDate();}var that = thisconsole.log(1)//得到当月心情表从后端wx.request({url: app.globalData.url + '/selectAllMood',data: {uid: wx.getStorageSync('uid'),year: year,month: month + 1},header: {'content-type': 'application/json'},success(res) {console.log(2)if (!res.data) {wx.showToast({title: '日期超出可用范围咯!',icon: 'none',duration: 2000 //持续的时间})} else {that.setData({moodList: res.data.moodList,year: year,month: month + 1})arrLen = startWeek + dayNums;for (let i = 0; i < arrLen; i++) {if (i >= startWeek) {num = i - startWeek + 1;obj = {isToday: '' + year + '-' + (month + 1) + '-' + num,dateNum: num,weight: 5,mood: '/images/' + that.data.moodList[num - 1] + '色.png'}} else {obj = {};}dateArr[i] = obj;}that.setData({dateArr: dateArr})let nowDate = new Date();let nowYear = nowDate.getFullYear();let nowMonth = nowDate.getMonth() + 1;let nowWeek = nowDate.getDay();let getYear = setYear || nowYear;let getMonth = setMonth >= 0 ? (setMonth + 1) : nowMonth;if (nowYear == getYear && nowMonth == getMonth) {that.setData({isTodayWeek: true,todayIndex: nowWeek})} else {that.setData({isTodayWeek: false,todayIndex: -1})}}}//为测试所用,正式直接删掉// fail() {// arrLen = startWeek + dayNums;// for (let i = 0; i < arrLen; i++) {// if (i >= startWeek) {// num = i - startWeek + 1;// obj = {// isToday: '' + year + '-' + (month + 1) + '-' + num,// dateNum: num,// weight: 5,// mood: '/images/' + that.data.moodList[num - 1] + '色.png'// }// } else {// obj = {};// }// dateArr[i] = obj;// }// that.setData({// dateArr: dateArr// })// let nowDate = new Date();// let nowYear = nowDate.getFullYear();// let nowMonth = nowDate.getMonth() + 1;// let nowWeek = nowDate.getDay();// let getYear = setYear || nowYear;// let getMonth = setMonth >= 0 ? (setMonth + 1) : nowMonth;// if (nowYear == getYear && nowMonth == getMonth) {// that.setData({// isTodayWeek: true,// todayIndex: nowWeek// })// } else {// that.setData({// isTodayWeek: false,// todayIndex: -1// })// }// }})},lastMonth: function () {//全部时间的月份都是按0~11基准,显示月份才+1let year = this.data.month - 2 < 0 ? this.data.year - 1 : this.data.year;let month = this.data.month - 2 < 0 ? 11 : this.data.month - 2;this.dateInit(year, month);},nextMonth: function () {//全部时间的月份都是按0~11基准,显示月份才+1let year = this.data.month > 11 ? this.data.year + 1 : this.data.year;let month = this.data.month > 11 ? 0 : this.data.month;this.dateInit(year, month);},/*** 设置movable-view位移*/setXmove: function (productIndex, xmove) {let noteList = this.data.noteListnoteList[productIndex].xmove = xmovethis.setData({noteList: noteList})},/*** 处理movable-view移动事件*/handleMovableChange: function (e) {if (e.detail.source === 'friction') {if (e.detail.x < -30) {this.showDeleteButton(e)} else {this.hideDeleteButton(e)}} else if (e.detail.source === 'out-of-bounds' && e.detail.x === 0) {this.hideDeleteButton(e)}},/*** 显示删除按钮*/showDeleteButton: function (e) {let productIndex = e.currentTarget.dataset.productindexthis.setXmove(productIndex, -65)},/*** 隐藏删除按钮*/hideDeleteButton: function (e) {let productIndex = e.currentTarget.dataset.productindexthis.setXmove(productIndex, 0)},/*** 处理touchstart事件*/handleTouchStart(e) {this.startX = e.touches[0].pageX},/*** 处理touchend事件*/handleTouchEnd(e) {if (e.changedTouches[0].pageX < this.startX && e.changedTouches[0].pageX - this.startX <= -30) {this.showDeleteButton(e)} else if (e.changedTouches[0].pageX > this.startX && e.changedTouches[0].pageX - this.startX < 30) {this.showDeleteButton(e)} else {this.hideDeleteButton(e)}},/*** 删除产品*/handleDeleteProduct: function ({currentTarget: {dataset: {nid}}}) {var that = thiswx.showModal({title: '删除该条笔记',content: '确认要删除吗?',success(res) {if (res.confirm) {wx.request({//传入nid请求删除url: app.globalData.url + '/deleteNote',data: {nid: nid},header: {'content-type': 'application/json'},success() {let noteList = that.data.noteListlet productIndex = noteList.findIndex(item => item.nid === nid)noteList.splice(productIndex, 1)that.setData({noteList})if (noteList[productIndex]) {that.setXmove(productIndex, 0)}if(!that.data.noteList[0]){that.setData({noteisHave: false,})}},fail() {wx.showToast({title: '删除失败!',icon: 'none',duration: 2000 //持续的时间})}})}}})},
})
data是初始化
onshow是登录后要展示的,要对当日心情进行初始化,要对当日笔记进行初始化
noteinit要根据用户的uid和date访问后端(app.globalData.url这个是后端服务器地址),获取到笔记信息后渲染到页面上,也就是重设data里面的值
daymoodinit也是一样
backnow是什么,里面要对当月的日历进行展示,还要在日历上展示当日的总体心情角标
里面要dateInit,也是从后端获取数据
后续的方法也是点击主页上的一个按钮,需要进行的操作,有点到某一日、修改总体心情、修改笔记等,比如修改笔记,就用wx.redirectTo进行了页面跳转,需要页面间传递的数据也要在跳转的时候传递;比如修改总体心情,使用的则是navigateTo,这是为啥,还有showModal就是在上层创建新窗口吗;比如date就是点击某一日,获得该日的笔记信息和总体心情,于是要noteinit和daymoodinit;后续还有滑动删除笔记事件
- 数据初始化(data)
在data
中,我们定义了页面初始化时需要使用的各种变量。这些变量包括用户的头像、昵称、日期、心情、笔记列表等。这些数据将在页面加载时被初始化,并在用户与页面交互时动态更新。
- avatarUrl:用户的头像URL,默认值为
/images/头像.png
。 - nickName:用户的昵称,默认值为
未登录
。 - date:当前日期,初始值为
null
。 - daymood:当日的心情状态,默认值为
今日还未选心情
。 - moodList:心情列表,用于存储用户在不同日期的总体心情。
- noteList:笔记列表,用于存储用户在不同日期的笔记内容。
- noteisHave:标识当前日期是否有笔记,初始值为
false
。 - year和month:当前年份和月份,用于日历展示。
- head:日历表头的星期几标识。
- dateArr:日历数组,用于展示当前月份的日期和心情角标。
- isToday:标识当前日期是否为今天。
- pickToday:用户选择的日期。
- isTodayWeek:标识当前日期是否在本周。
- todayIndex:今天在日历中的索引。
- nottoday:标识用户选择的日期是否为今天。
- havenote:提示用户当前日期是否有笔记。
- 页面展示逻辑(onShow)
onShow
是微信小程序的生命周期函数,当页面显示时触发。在这个函数中,我们主要做了以下几件事:
- 获取当前日期:通过
new Date()
获取当前日期,并提取年份和月份。 - 初始化日历:调用
dateInit
函数,初始化当前月份的日历数据。 - 设置页面数据:将当前日期、用户头像、昵称等信息设置到页面的
data
中。 - 初始化当日心情和笔记:调用
daymoodinit
和noteinit
函数,分别获取并展示当日的心情和笔记。
- 笔记初始化(noteinit)
noteinit
函数用于根据用户的uid
和date
从后端获取笔记列表,并将获取到的笔记数据渲染到页面上。
- 请求笔记列表:通过
wx.request
向后端发送请求,获取指定日期的笔记列表。 - 处理笔记数据:如果笔记列表不为空,遍历列表并将每条笔记的数据存储到
noteList
中。 - 更新页面数据:将处理后的笔记列表设置到页面的
data
中,并更新noteisHave
状态。
- 当日心情初始化(daymoodinit)
daymoodinit
函数用于获取并展示用户当日的总体心情。
- 请求当日心情:通过
wx.request
向后端发送请求,获取指定日期的总体心情。 - 更新页面数据:根据获取到的心情数据,更新页面中的
daymood
状态。
- 返回今日(backnow)
backnow
函数用于将页面重置为当前日期的状态。
- 重置日期:将页面日期重置为当前日期。
- 初始化日历:调用
dateInit
函数,重新初始化当前月份的日历数据。 - 更新页面数据:将
nottoday
状态设置为false
,表示当前日期为今天。
- 选择日期(date)
date
函数用于处理用户选择日期的操作。
- 获取选择的日期:根据用户点击的日期或从函数传入的日期,获取用户选择的日期。
- 更新页面数据:将选择的日期设置到页面的
data
中,并更新noteisHave
和nottoday
状态。 - 初始化心情和笔记:调用
daymoodinit
和noteinit
函数,获取并展示选择日期的心情和笔记。
- 添加笔记(add)
add
函数用于跳转到添加笔记页面。
- 页面跳转:使用
wx.navigateTo
跳转到/pages/notemood/notemood
页面。
- 修改总体心情(daymood)
daymood
函数用于处理用户修改总体心情的操作。
- 弹出确认框:使用
wx.showModal
弹出确认框,询问用户是否确认修改总体心情。 - 页面跳转:如果用户确认,使用
wx.navigateTo
跳转到/pages/daymood/daymood
页面。
- 个人中心(mine)
mine
函数用于跳转到个人中心页面。
- 页面跳转:使用
wx.navigateTo
跳转到/pages/mine/mine
页面。
- 修改笔记(changenote)
changenote
函数用于处理用户修改笔记的操作。
- 获取笔记信息:从用户点击的笔记中获取笔记的
nid
、time
和mood
信息。 - 页面跳转:使用
wx.redirectTo
跳转到/pages/note/note
页面,并传递笔记的相关信息。
- 日历初始化(dateInit)
dateInit
函数用于初始化当前月份的日历数据。
- 获取当前月份的天数和起始星期:通过
new Date()
获取当前月份的天数和1号对应的星期。 - 请求当月心情数据:通过
wx.request
向后端发送请求,获取当前月份的心情数据。 - 更新页面数据:将获取到的心情数据设置到页面的
data
中,并生成日历数组dateArr
。
- 滑动删除笔记
滑动删除笔记功能通过handleMovableChange
、handleTouchStart
、handleTouchEnd
等函数实现。
- 显示删除按钮:当用户向左滑动笔记时,显示删除按钮。
- 隐藏删除按钮:当用户向右滑动或滑动距离不足时,隐藏删除按钮。
- 删除笔记:当用户点击删除按钮时,弹出确认框,确认后通过
wx.request
向后端发送删除请求,并更新页面数据。
main.wxml
<view class="data"><view class="circle" bindtap="mine"><image src="{{avatarUrl}}" class="head"></image></view><view class="date-mood"><text class="date">{{date}}</text><text class="mood" bindtap="daymood">{{daymood}}</text></view><view class="backnow"><image src="/images/今日.png" class="backnowimg" bindtap="backnow" wx:if="{{nottoday}}"></image>
</view><image src="/images/添加.png" class="add" bindtap="add"></image>
</view>
<view class="calendar"><view class='wrap'><view><view class='date-show'><view class='lt-arrow' bindtap='lastMonth'><image src='/images/左箭头.png' mode='aspectFit'></image></view>{{year}}年{{month}}月<view class='rt-arrow' bindtap='nextMonth'><image src='/images/右箭头.png' mode='aspectFit'></image></view></view></view><view class='header'><view wx:for='{{head}}' class='{{(index == todayIndex) && isTodayWeek ? "weekMark" : ""}}'>{{item}}<view></view></view></view><view class='date-box'><view wx:for='{{dateArr}}'class='{{isToday == item.isToday ? "nowDay" : (pickToday == item.isToday ? "pickDay" : "")}}'data-date='{{item.isToday}}' bindtap="date"><view class="sort"><view class="minimoodview"><image src="{{item.mood}}" class="minimood"></image></view><view class='date-head'><view>{{item.dateNum}}</view></view></view></view></view></view>
</view>
<view class="nonote" wx:if="{{!noteisHave}}"><image src="/images/铃铛.png" class="bell"></image><view class="empty">{{havenote}}</view>
</view>
<view wx:else><view class="product-list"><view class="product-item" wx:for="{{noteList}}" wx:for-index="index" wx:key="{{item.nid}}"><movable-area data-note="{{item}}" bindtap="changenote"><movable-view out-of-bounds="true" direction="horizontal" x="{{item.xmove}}" inertia="true"data-productIndex="{{index}}" bindtouchstart="handleTouchStart" bindtouchend="handleTouchEnd"bindchange="handleMovableChange"><view class="product-item-wrap"><view class="expression"><image src="{{item.mood}}" class="expressionimg"></image></view><view class="product-movable-item"><view class="product-movable-item-name">{{item.time}}</view><view class="product-movable-item-code">{{item.context}}</view></view></view><view class="blank"></view></movable-view></movable-area><view class="delete-btn" data-nid="{{item.nid}}" bindtap="handleDeleteProduct"><image src="/images/删除.png" class="delete"></image></view></view></view>
</view>
- 顶部用户信息与操作区域
- 用户头像:通过
<image>
组件展示用户头像,点击头像触发mine
函数,跳转到个人中心页面。 - 日期与心情:显示当前日期和当日心情,点击心情文本触发
daymood
函数,允许用户修改当日心情。 - 返回今日按钮:如果用户选择的日期不是今天,显示“返回今日”图标,点击触发
backnow
函数,重置为当前日期。 - 添加笔记按钮:点击“添加”图标触发
add
函数,跳转到添加笔记页面。
- 日历区域
- 日历头部:显示当前年份和月份,左右箭头分别触发
lastMonth
和nextMonth
函数,用于切换月份。 - 星期表头:显示星期几,当前星期会高亮显示。
- 日期展示:展示当前月份的日期,每个日期块显示日期数字和对应的心情图标。点击日期块触发
date
函数,加载该日期的笔记和心情。
- 笔记展示区域
- 无笔记提示:如果当前日期没有笔记,显示提示信息(如“今日还未写笔记哦!”)和一个铃铛图标。
- 笔记列表:如果有笔记,展示笔记列表。每条笔记包含心情图标、时间和内容。笔记支持左右滑动,滑动后显示删除按钮。
- 滑动删除:通过
movable-view
实现滑动功能,滑动到一定距离后显示删除按钮,点击删除按钮触发handleDeleteProduct
函数,删除对应笔记。 - 点击笔记:点击笔记内容触发
changenote
函数,跳转到修改笔记页面。
- 滑动删除:通过
3. 后端代码分析
以主页逻辑业务为例
UserDateController.java
package st.nuc.edu.cn.mood_note.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import st.nuc.edu.cn.mood_note.entity.UserDate;
import st.nuc.edu.cn.mood_note.service.UserDateService;@RestController
public class UserDateController {@AutowiredUserDateService userDateService;@RequestMapping("/gDateMood")public Object gDateMood(String uid, String date) {return userDateService.gDateMood(uid, date);}@RequestMapping("/mixOperate")public void mixOperate(UserDate userDate) {userDateService.mixOperate(userDate);}@RequestMapping("/selectAllMood")public Object selectAllMood(String uid, String year, String month) {return userDateService.selectAllMood(uid, year, month);}}
UserDateController
是处理主页相关请求的控制器,主要包含以下接口:
- gDateMood:根据用户ID和日期,获取某日的总体心情。
- mixOperate:处理用户心情的插入或更新操作。
- selectAllMood:根据用户ID、年份和月份,获取该月的所有心情数据。
这些接口通过调用UserDateService
中的方法,完成具体的业务逻辑。
UserDateServiceImpl.java
package st.nuc.edu.cn.mood_note.service.impl;import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import st.nuc.edu.cn.mood_note.entity.UserDate;
import st.nuc.edu.cn.mood_note.mapper.UserDateMapper;
import st.nuc.edu.cn.mood_note.service.UserDateService;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;@Component
public class UserDateServiceImpl implements UserDateService {@AutowiredUserDateMapper userDateMapper;@Overridepublic Object gDateMood(String uid, String date) {UserDate userDate = userDateMapper.gDateMood(uid, date);if (userDate != null) {JSONObject object = new JSONObject();object.put("daymood",userDate.getDateMood());return object;} else {return null;}}@Overridepublic boolean mixOperate(UserDate userDate) {UserDate userDate1 = userDateMapper.gDateMood(userDate.getUid(), userDate.getDate());if (userDate1 == null) {return userDateMapper.insert(userDate);} else {return userDateMapper.update(userDate);}}@Overridepublic Object selectAllMood(String uid, String year, String month) {int month1 = Integer.parseInt(month);String s = null;if ((month1>0)&&(month1<10)) {s = "0" + month1;} else {s = month;}List<UserDate> userDateList = userDateMapper.gAllDateMood(uid, year, s);if(!userDateList.isEmpty()) {List<String> moodList = new ArrayList();JSONObject object = new JSONObject();for (UserDate userDate : userDateList) {if (userDate.getDateMood() == null) {moodList.add(null);} else {moodList.add(userDate.getDateMood());}}object.put("moodList", moodList);return object;}return null;}
}
UserDateServiceImpl
是UserDateService
接口的实现类,主要功能如下:
- gDateMood:通过
UserDateMapper
查询某日的心情数据,如果存在则返回心情信息,否则返回null
。 - mixOperate:根据用户ID和日期,判断是插入新心情数据还是更新已有数据。
- selectAllMood:查询某月的所有心情数据,并将其封装为
JSON
格式返回。如果某日没有心情数据,则对应位置为null
。
UserDateMapper.java
package st.nuc.edu.cn.mood_note.mapper;import org.apache.ibatis.annotations.*;
import st.nuc.edu.cn.mood_note.entity.UserDate;import java.util.List;@Mapper
public interface UserDateMapper {@Select("select * from user_date where uid = #{uid} and date = #{date}")UserDate gDateMood(@Param("uid")String uid, @Param("date")String date);List<UserDate> gAllDateMood(@Param("uid")String uid, @Param("year")String year, @Param("month")String month);@Insert("insert into user_date (uid,date,date_mood) values (#{uid},#{date},#{dateMood})")boolean insert(UserDate userDate);@Update("update user_date set date_mood=#{dateMood} where uid = #{uid} and date = #{date}")boolean update(UserDate userDate);}
UserDateMapper
是MyBatis的Mapper接口,定义了与数据库交互的方法:
- gDateMood:根据用户ID和日期,查询某日的心情数据。
- gAllDateMood:根据用户ID、年份和月份,查询该月的所有心情数据。
- insert:插入新的心情数据。
- update:更新已有的心情数据。
UserDateMapper.xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="st.nuc.edu.cn.mood_note.mapper.UserDateMapper"><select id="gAllDateMood" resultType="userDate">SELECT a.uid,date.date,a.date_mood FROM date LEFT JOIN (SELECT * FROM user_date WHERE uid = #{uid}) a ON date.date=a.date where date.date LIKE '${year}_${month}%';</select></mapper>
UserDateMapper.xml
是MyBatis的映射文件,定义了gAllDateMood
方法的SQL查询逻辑。通过左连接查询date
表和user_date
表,获取某月的所有心情数据。
application.yaml
mybatis:type-aliases-package: st.nuc.edu.cn.mood_note.entitymapper-locations: classpath:mapper/*.xmlconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImplspring:datasource:url: jdbc:mysql://localhost:3306/mood_note?useUnicode=true&characterEncoding=utf8&useSSL=falseusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverserver:port:8080
application.yaml
是Spring Boot的配置文件,主要配置了:
- MyBatis:指定实体类包路径、Mapper文件路径、日志实现等。
- 数据源:配置MySQL数据库的连接信息,包括URL、用户名、密码等。
- 服务器端口:设置服务端口为
8080
。
三、数据库设计
1. 表结构
1.用户表(用户)
- uid:用户ID,主键,类型为
varchar(50)
。 - opend:用户开放ID,类型为
varchar(255)
。
2.用户日期表(用户日期)
- uid:用户ID,外键,关联用户表,类型为
varchar(50)
。 - date:日期,外键,关联日期表,类型为
date
。 - date_mod:用户在该日期的总体心情,类型为
varchar(10)
。
3.登记表(登记)
- uid:用户ID,主键,类型为
int
。 - uid:用户ID,外键,关联用户表,类型为
varchar(50)
。 - date:日期,外键,关联日期表,类型为
date
。 - ncontext:笔记内容,类型为
varchar(8000)
。 - ntine:笔记时间,类型为
time
。 - mod:笔记心情,类型为
varchar(5)
。
4.反馈表(反馈)
- fid:反馈ID,主键,类型为
int
。 - uid:用户ID,外键,关联用户表,类型为
varchar(50)
。 - context:反馈内容,类型为
varchar(500)
。 - lz_read:是否已读,类型为
varchar(10)
。 - rcontext:回复内容,类型为
varchar(500)
。 - rdate:回复日期,类型为
date
。 - date:反馈日期,外键,关联日期表,类型为
date
。
5.日期表(日期)
- date:日期,主键,类型为
date
。
6.管理员表(管理员)
- aid:管理员ID,主键,类型为
int
。 - opend:管理员开放ID,类型为
varchar(255)
。 - password:管理员密码,类型为
varchar(255)
。
7.官方通知表(官方通知)
- aid:管理员ID,主键,类型为
int
。 - date:通知日期,类型为
date
。 - context:通知内容,类型为
varchar(500)
。 - lz_read:是否已读,类型为
varchar(10)
。
相关文章:
【微信小程序】每日心情笔记
个人团队的比赛项目,仅供学习交流使用 一、项目基本介绍 1. 项目简介 一款基于微信小程序的轻量化笔记工具,旨在帮助用户通过记录每日心情和事件,更好地管理情绪和生活。用户可以根据日期和心情分类(如开心、平静、难过等&#…...
【leetcode hot 100 73】矩阵置零
解法一:(使用两个标记变量)用矩阵的第一行和第一列代替方法一中的两个标记数组(col、row[ ]:第几列、行出现0),以达到 O(1) 的额外空间。 这样会导致原数组的第一行和第一列被修改,…...
【Linux】自定协议和序列化与反序列化
目录 一、序列化与反序列化概念 二、自定协议实现一个加法网络计算器 (一)TCP如何保证接收方的接收到数据是完整性呢? (二)自定义协议 (三)自定义协议的实现 1、基础类 2、序列化与反序列…...
混合专家模型(MoE):高效处理复杂任务的智能架构,DeepSeek性能出色的秘诀
混合专家模型 1. 什么是混合专家模型 混合专家模型(Mixture of Experts,简称 MoE) 是一种先进的神经网络架构,旨在通过整合多个 专门化的子模型(或称为“专家”) 的预测来提升整体模型性能。其核心思想是…...
使用 Spring Boot 实现前后端分离的海康威视 SDK 视频监控
使用 Spring Boot 实现前后端分离的海康威视 SDK 视频监控系统,可以分为以下几个步骤: 1. 系统架构设计 前端:使用 Vue.js、React 或 Angular 等前端框架实现用户界面。后端:使用 Spring Boot 提供 RESTful API,负责与…...
C++ 内存序在多线程中的使用
目录 一、内存顺序 二、 指令重排在多线程中的问题 2.1 问题与原因 2.2 解决方案 三、六种内存序 3.1 memory_order_relaxed 3.2 memory_order_consume 3.3 memory_order_acquire 3.4 memory_order_release 3.5 memory_order_acq_rel 3.6 memory_order_seq_cst 一、…...
【MySQL】表的操作
文章目录 👉表的操作👈创建表查看表修改表删除表 👉表的操作👈 创建表 create tabletable_name (field1 datatype,field2 datatype,field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;说明:…...
【Flink银行反欺诈系统设计方案】3.欺诈的7种场景和架构方案、核心表设计
【Flink银行反欺诈系统设计方案】3.欺诈的7种场景和架构方案、核心表设计 1. **欺诈场景分类与案例说明**1.1 **大额交易欺诈**1.2 **异地交易欺诈**1.3 **高频交易欺诈**1.4 **异常时间交易欺诈**1.5 **账户行为异常**1.6 **设备指纹异常**1.7 **交易金额突变** 2. **普适性软…...
DeepSeek-R1本机部署(VLLM+OpenWebUI)
本文搭建环境 系统:Ubuntu 22.04.4 LTS Python版本:Python 3.10 显卡:RTX 4090D 一、DeepSeek-R1-14b原始模型和q8量化模型 1.从modelscope下载模型 官方原始模型:https://modelscope.cn/models/deepseek-ai/DeepSeek-R1-Di…...
计算机网络软考
1.物理层 1.两个主机之间发送数据的过程 自上而下的封装数据,自下而上的解封装数据,实现数据的传输 2.数据、信号、码元 码元就是数字通信里用来表示信息的基本信号单元。比如在二进制中,用高电平代表 “1”、低电平代表 “0”,…...
vscode 查看3d
目录 1. vscode-3d-preview obj查看ok 2. vscode-obj-viewer 没找到这个插件: 3. 3D Viewer for Vscode 查看obj失败 1. vscode-3d-preview obj查看ok 可以查看obj 显示过程:开始是绿屏,过了1到2秒,后来就正常看了。 2. vsc…...
HTML第三节
一.初识CSS 1.CSS定义 A.内部样式表 B.外部样式表 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title&g…...
爬虫去重:数据采集时如何进行去重,及去重优化策略
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 1. 去重的核心思路2. 常见的去重方法2.1 基于集合(Set)的去重2.2 基于布隆过滤器(Bloom Filter)的去重2.3 基于数据库的去重2.4 基于文件存储的去重2.5 基于 Redis 的去重3. 去重的优化策略3.1 URL 规范化3.2 分片去…...
IDEA集成DeepSeek,通过离线安装解决无法安装Proxy AI插件问题
文章目录 引言一、安装Proxy AI1.1 在线安装Proxy AI1.2 离线安装Proxy AI 二、Proxy AI中配置DeepSeek2.1 配置本地部署的DeepSeek(Ollama方式)2.2 通过第三方服务商提供的API进行配置 三、效果测试 引言 许多开发者尝试通过安装Proxy AI等插件将AI能力…...
【电子基础】运算放大器应用笔记(持续更新)
目录 运放应用1: 运放基础知识同相比例运算放大器计算放大倍数 电压比较器 运放应用2:500W调压器同相比例运算放大器计算放大倍数计算平衡电阻 积分电路 运放应用3:逆变电焊机电压跟随器积分电路 加油站1. 为什么比例运算放大电路要加平衡电阻…...
计算机网络核心知识点:信道容量、OSI模型与调制技术详解
目录 一、信道容量与调制技术 奈奎斯特定理(无噪声环境) 核心公式: 参数说明: 应用场景: 香农定理(有噪声环境) 核心公式: 参数说明: 应用场景: 奈奎…...
AI赋能企业协作4-NL2Sql技术路线
1.1 对话即服务的一点思考 在数智化转型的过程中,基于即时通信(IM)的协作平台正悄然成为企业智能化转型的“新基建”。协作平台天然具备高频交互、实时协同和场景化落地的特性,仿佛是为对话式AI量身定制的试验场——员工在熟悉的聊…...
如何用FFmpeg高效拉流(避坑指南)
FFmpeg作为音视频处理领域的“瑞士军刀”,其拉流功能在直播、监控、流媒体分析等场景中应用广泛。本文从实战角度出发,系统梳理FFmpeg拉流的核心工具链、协议适配技巧及高频踩坑点,助你快速掌握流媒体处理核心技能! 一、FFmpeg拉流工具链全解析 核心工具 ffplay:快速验证…...
面试基础--MySQL SQL 优化深度解析
MySQL SQL 优化深度解析:EXPLAIN、索引优化与分库分表实践 引言 在互联网大厂的高并发场景下,数据库的性能优化是至关重要的。MySQL 作为最流行的关系型数据库之一,SQL 查询的性能直接影响了系统的响应时间和吞吐量。本文将深入探讨 MySQL …...
WebRTC简介
WebRTC简介 WebRTC(Web Real-Time Communication)是一种支持浏览器之间进行实时音视频通信和数据传输的开放标准和技术。它由Google发起,现已成为W3C和IETF的标准。WebRTC允许开发者在不依赖第三方插件或软件的情况下,直接在网页…...
清北deepseek8本手册
“清北手册”通常是“清华大学和北京大学推出的DeepSeek手册”的简写。近期,随着AI技术的迅速发展,清北两高校陆续发布多本自家的DeepSeek学习手册,助力普通人学习进阶。 清华大学的DeepSeek手册已推出5册,内容丰富全面࿰…...
前后分离文件上传案例,前端HTML,后端Net6开发的webapi(完整源代码)下载
文件上传功能在项目开发中非常实用,本案例前端用HTML页面的form表单实现,后端用Net6实现。 前后分离文件上传案例,前端HTML,后端Net6(完整源代码) 下载链接https://download.csdn.net/download/luckyext/9…...
6.过拟合处理:确保模型泛化能力的实践指南——大模型开发深度学习理论基础
在深度学习开发中,过拟合是一个常见且具有挑战性的问题。当模型在训练集上表现优秀,但在测试集或新数据上性能大幅下降时,就说明模型“记住”了训练数据中的噪声而非学习到泛化规律。本文将从实际开发角度系统讲解如何应对过拟合,…...
六十天前端强化训练之第一天到第七天——综合案例:响应式个人博客项目
欢迎来到编程星辰海的博客讲解 目录 前言回顾 HTML5与CSS3基础 一、知识讲解 1. 项目架构设计(语义化HTML) 2. 响应式布局系统(Flex Grid) 3. 样式优先级与组件化设计 4. 完整响应式工作流 二、核心代码示例 完整HTML结…...
java数据结构_再谈String_10
目录 字符串常量池 1. 创建对象的思考 2. 字符串常量池(StringTable) 3. 再谈String对象创建 字符串常量池 1. 创建对象的思考 下面两种创建String对象的方式相同吗? public static void main(String[] args) {String s1 "hello&…...
MCP:重塑AI与数据交互的新标准
MCP:重塑AI与数据交互的新标准 前言 在人工智能领域,大型语言模型(LLM)的应用日益广泛,但其与外部数据源和工具的集成却一直面临复杂性和碎片化的挑战。 Anthropic提出的MCP(Model Context Protocol&…...
Cursor+Claude3.7实现从原型到app开发
最近在X上看到了一些人在用Claude 3.7 Sonnet生成 app原型图的尝试,受到启发,发现这么先生成不同界面的原型图再让Cursor基于原型图开发app会是很好的尝试。尤其是,你也可以不两步直接生成,而是在过程中更可视化地思考你要生产的原…...
洛谷P1334
题目如下 思路: 每次选择最短的两块木板进行合并,直到只剩下一块木板。使用最小堆(优先队列)来实现这一过程。使用最小堆: 将所有木板的长度放入最小堆(优先队列) 每次从堆中取出两块最短的木…...
使用wifi连接手机adb进行调试|不使用数据线adb调试手机|找应用错误日志和操作日志
手机在开发者选项里要开启无线调试 在手机设置中查看WiFi的IP地址 设置 -> WLAN -> 已连接的WiFi -> IP地址 使用手机的IP地址连接 adb connect 192.168.1.12:xxxxx 检查连接状态 adb devices 断开特定设备 adb disconnect 192.168.x.x:xxxxx 断开所有设备 …...
大语言模型中温度参数(Temperature)的核心原理
大语言模型中温度参数(Temperature)的核心原理是通过调整模型输出的概率分布,控制生成结果的随机性和多样性。以下是其原理的详细说明: 一、定义与核心作用 温度参数是生成式模型(如GPT系列)中的一个超参数…...
【AIGC】通义万相 2.1 与蓝耘智算:共绘 AIGC 未来绚丽蓝图
一、引言 在人工智能技术迅猛发展的今天,AIGC(生成式人工智能内容生成)领域正以惊人的速度改变着我们的生活和工作方式。从艺术创作到影视制作,从广告设计到智能客服,AIGC 技术的应用越来越广泛。通义万相 2.1 作为一…...
在Ubuntu上搭建Samba服务,实现与windows之间的文件共享
1.安装samba 首先切换为root账户,就是带#符号的表示当前登录的是root超级用户; su - 如果忘记密码,就输入以下命令修改密码 sudo passwd root 再切换为超级用户 然后进行更新软件列表 sudo apt update sudo apt install samba安装 whe…...
Labview培训案例3: 输出正弦波并采集显示
本案例介绍如何从板卡(USB6008)的模拟量输出端口输出一个正弦波,然后模拟量输入模块进行采样,然后显示到vi画面的‘波形图’中。 详细代码在:Labview课程3:正弦波输出&采集数据&显示资源-CSDN文库 …...
使用 Deepseek + kimi 快速生成PPT
前言 最近看到好多文章和视频都在说,使用 Deepseek 和 kimi 能快速生成精美的 ppt,毕竟那都是别人说的,只有自己尝试一次才知道结果。 具体操作 第一步:访问 deepseek 我们访问 deepseek ,把我们想要输入的内容告诉…...
图解MOE大模型的7个核心问题并探讨DeepSeekMoE的专家机制创新
原文地址:https://newsletter.maartengrootendorst.com/p/a-visual-guide-to-mixture-of-experts #mermaid-svg-FU7YUSIfuXO6EVHa {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-FU7YUSIfuXO6EVHa .error-icon{fill…...
青训营:简易分布式爬虫
一、项目介绍 该项目是一个简易分布式爬虫系统,以分布式思想为基础,通过多节点协作的方式,将大规模的网页抓取任务分解,从而高效、快速地获取网络数据 。 项目地址:https://github.com/yanchengsi/distributed_crawle…...
Scala(Array,List,Set,Map,Tuple,字符串 使用的简单介绍)
目录 Array 不可变数组 ArrayBuffer可变数组 List 不可变列表 ListBuffer 可变列表 Set 集合(可变不可变) Map映射(可变不可变)(键值对) Tuple 元组 字符串 Array 不可变数组 // Array 数组// scala 中的数组下标是()// scala 中的数组是值…...
fmql之Linux WDT
正点原子第52章。 基础知识 正点原子教程 fmql-dts 代码 APP代码(不需要编写驱动代码) static int dw_wdt_drv_probe(struct platform_device *pdev) {struct device *dev &pdev->dev;struct watchdog_device *wdd;struct dw_wdt *dw_wdt; …...
IntelliJ IDEA集成MarsCode AI
IntelliJ IDEA集成MarsCode AI IDEA中安装插件 安装完毕之后登录自己的账号 点击链接,注册账号 https://www.marscode.cn/events/s/i5DRGqqo/ 可以选择不同的模型...
python-leetcode-打家劫舍 III
337. 打家劫舍 III - 力扣(LeetCode) 这个问题可以通过动态规划解决。可以通过递归的方式来解决每个房子的最大偷窃金额,递归过程中,我们会记录每个房子是否偷或不偷时能够获得的最大金额。 思路: 对于每个房子,我们有两种选择: 偷这个房子,那么它的直接相邻(父亲和孩…...
数据结构——队列
1. 概念与结构 队列(Queue)是一种先进先出(FIFO, First In First Out)的数据结构,即最先被插入队列的数据会最先被删除。队列广泛应用于计算机科学中,特别是在任务调度、缓冲区管理、网络数据传输等领域。…...
GaussianCity:实时生成城市级数字孪生基底的技术突破
在空间智能领域,如何高效、大规模地生成高质量的3D城市模型一直是一个重大挑战。传统方法如NeRF和3D高斯溅射技术(3D-GS)在效率和规模上存在显著瓶颈。GaussianCity通过创新性的技术方案,成功突破了这些限制,为城市级数字孪生的构建提供了全新路径。 一、核心创新:突破传…...
【AGI】智谱开源2025:一场AI技术民主化的革命正在到来
智谱开源2025:一场AI技术民主化的革命正在到来 引言:开源,一场技术平权的革命一、CogView4:中文AI生成的里程碑1. 破解汉字生成的“AI魔咒”2. 开源协议与生态赋能 二、AutoGLM:人机交互的范式跃迁1. 自然语言驱动的跨…...
【算法学习之路】5.贪心算法
贪心算法 前言一.什么是贪心算法二.例题1.合并果子2.跳跳!3. 老鼠和奶酪 前言 我会将一些常用的算法以及对应的题单给写完,形成一套完整的算法体系,以及大量的各个难度的题目,目前算法也写了几篇,题单正在更新…...
C++11中的右值引用和完美转发
C11中的右值引用和完美转发 右值引用 右值引用是 C11 引入的一种新的引用类型,用 && 表示。它主要用于区分左值和右值,并且可以实现移动语义,避免不必要的深拷贝,提高程序的性能。左值通常是可以取地址的表达式…...
Leetcode 1477. 找两个和为目标值且不重叠的子数组 前缀和+DP
原题链接: Leetcode 1477. 找两个和为目标值且不重叠的子数组 class Solution { public:int minSumOfLengths(vector<int>& arr, int target) {int narr.size();int sum0;int maxnINT_MAX;vector<int> dp(n,maxn);//dp[i]表示以索引i之前的满足要求…...
koa-session设置Cookie后获取不到
在谷歌浏览器中请求获取不到cookie问题之一(谷歌安全策略) 场景 前端使用 axios 请求,项目地址:http://192.168.8.1:5173 import axios from axiosconst request axios.create({baseURL: http://127.0.0.1:3001/,timeout: 60000,…...
Linux三种网络方式
前言 发现运维啥都得会,这周就遇到了网络问题自己无法解决,因此痛定思痛学一下。 参考文献 你管这破玩意叫网络? 桥接模式、NAT模式、仅主机模式,原来是这样工作的 交换机 构成局域网,实现所有设备之间的通信。 …...
android_viewtracker 原理
一、说明 我们业务中大部分场景是用 RecyclerView 实现的列表,而 View 的曝光通常是直接写在 adapter 的 onBindViewHolder 中,这样就会导致 item 还没显示出来的时候就会触发曝光。最近业务提出需要实现根据 View 显示在屏幕上面积大于 80% 才算曝光。…...
Object.defineProperty()
**Object.defineProperty()** 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。 plain const object1 {}; Object.defineProperty(object1, ‘property1’, { value: 42, writable: false }); object1.property1 77…...