一、主线规则:
- 卡片创建出来为待练习状态
- 该卡片需要在距离创建时间的第1天、第2天、第4天、第7天、第15天复习
- 如果提前背,不改变复习时间线
- 如果滞后背,会对应推迟复习时间线
比如,list在第0天创建,应该在[1,2,4,7,15]天后复习
如果他第1天没复习,在第2天开始第一次复习,则他的复习时间线变为了[2,3,5,8,16](距离创建时间的天数)
- 如果手动标记为已完成,则不会推荐复习
- 如果未学当日新学卡片,则会推至明日
二、现有数据库结构
三、须解决的逻辑清单
0. 卡片的状态
规则
1. 新学卡片:刚生成为 待练习,有一次练习为已练习,6次练习后为已完成,或者手动已完成。
2. 复习卡片:当天未复习为待练习,练习一次后为已练习/已完成。可手动标记已练习卡片为已完成。
实现
数据库只记录用户手动修改的已完成状态DELETED,其余状态动态判定。
怎么标记复习卡片的状态?
- 已完成: 已完成,
- 已练习: 练习时间 = 今天
- 待练习: 练习时间 < 今天
1. 今日须新学的卡片
规则
- 要先选择词书
- 根据当前进度 生成 今日卡片
- 可以一次生成多张(根据每日目标 )
- 分为生成与获取
今日卡片数量(日期=今日,或练习次数=0)
select (
(SELECT
COUNT(*)
FROM
card
RIGHT JOIN USER ON USER.openid = card.openid
WHERE
card.openid = 'o4nOp5Akkbm3J3aW2uNZuJp85jmg'
AND date = '20220407'
AND dict_code = USER.current_dict_code) +
(SELECT
COUNT(*)
FROM
card
RIGHT JOIN USER ON USER.openid = card.openid
RIGHT JOIN practice_record r ON r.card_id = card.id
WHERE
card.openid = 'o4nOp5Akkbm3J3aW2uNZuJp85jmg'
AND r.seq = 0
AND dict_code = USER.current_dict_code
AND date != '20220407')) as sum;
接口
流程图
https://www.processon.com/view/link/624e0f3d5653bb0743c36976
返回所有今日的卡片,如果数量 < 目标数,则生成差值
2. 今日须复习的卡片
规则
-
今日须复习且待练习的卡片,即须复习
- 卡片为当前选择的词书
- seq > 1
- 有[practice_record],且next_review_time < now()
- practice_time 不为今天
-
今日须复习且已练习的卡片,即已复习
- 卡片为当前选择的词书
- practice_time 为今天
- seq > 1
流程图
https://www.processon.com/view/link/624e0f3d5653bb0743c36976
sql
SELECT
practice_record.card_id, card.dict_code, card.CREATE_DATE AS createTimeStamp, ( SELECT DATEDIFF( practice_record.practice_time, NOW()) = 0 ) AS isTodayHasReview, practice_record.seq AS validPracticeCount, ( SELECT count(*) FROM practice_card_record WHERE practice_card_record.card_id = practice_record.card_id AND openid = USER.openid ) realPracticeCount, ( SELECT
DATE_FORMAT( card.CREATE_DATE, '%Y%m%d' )) AS date
FROM
practice_record
INNER JOIN card ON id = practice_record.card_id
INNER JOIN USER ON USER.openid = openid
WHERE
practice_record.openid = openid
AND card.DELETED = 0
AND DATEDIFF( NOW(), next_review_time ) <![ CDATA [>=]]> 0
AND card.dict_code = USER.current_dict_code
AND seq != 0
ORDER BY
card.CREATE_DATE
展示
根据创建时间分组 => 不用一次返回给前端,只返回第一张+数量
接口
todo
3. 所有卡片
4. 学习 (core)
规则
- 学习后,会增加[ practice_card_record ]的记录
- 但不一定修改[ practice_record ]的记录,除非:当前时间 > next_review_time
- 如果修改,则next_review_time = [REVIEW_ARR][seq] + [上次学习时间的0点] ,practice_time = [当前时间的0点]
- 学习后要判断是否已完成今日复习/学习任务,如果已完成,则在[ finish_record ]表里去重新增。
- 如果学习的是seq = 0 的卡片,则修改他的date为今日,这样才能在今日推荐学习中刷到他
5. 本年、本月学习天数、连续学习天数
practice_date: 20220102
practice_time: datetime
- 今年学习天数
SELECT
COUNT(
DISTINCT ( practice_date ))
FROM
practice_card_record
WHERE
LEFT ( practice_date, 4 ) = YEAR ( NOW() )
AND openid = #{openid}
- 本月学习天数
这里的RIGHT ( 100 + MONTH( NOW() ) 是为了将 1、2、3月=> 01,02,03
SELECT
COUNT( DISTINCT ( practice_date ) )
FROM
practice_card_record
WHERE
substring( practice_date, 5, 2 ) = RIGHT ( 100 + MONTH( NOW() ), 2 )
AND openid = #{openid}
- 连续学习天数
SELECT max(count)
FROM (
SELECT openid,
practice_date,
IF(@previousid = openid AND DATEDIFF(@previouspractice_date, practice_date) = 1,
@count := @Count + 1, @count := 1) as `count`,
@previousid := openid,
@previouspractice_date := practice_date
FROM practice_card_record,
(SELECT @previousid := null, @count := 1, @previouspractice_date := null) a
ORDER BY practice_date desc) a
where openid = #{openid} as 'lastDayCount'
6. 侧边栏的日历
- 计算起始日期
当前日期的周日 -> 当前日期的周六 - 得到该日的完成情况 (0,1,2)
- 补全空数据
SELECT
d.date,
IFNULL( T.STATUS, 0 ) STATUS
FROM
(
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) + 1 DAY ) AS date UNION ALL
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) + 0 DAY ) AS date UNION ALL
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) - 1 DAY ) AS date UNION ALL
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) - 2 DAY ) AS date UNION ALL
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) - 3 DAY ) AS date UNION ALL
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) - 4 DAY ) AS date UNION ALL
SELECT
DATE_SUB( CURDATE(), INTERVAL WEEKDAY( CURDATE()) - 5 DAY ) AS date
) d
LEFT JOIN ( SELECT STATUS, CREATE_DATE FROM finish_record WHERE openid = 'o4nOp5Akkbm3J3aW2uNZuJp85jmg' ) T ON T.CREATE_DATE = d.date
7. 今日X人已完成学习计划
[ finish_record ] 表
8. 随心一张
在该用户card表里,随机挑选一张,满足当前词书的卡片
9. 去除不需要背的单词
+个ignore
这里是全局删除吗?
10. 今日复习计划、今日新学计划
-
需复习卡片数:
- 卡片为当前选择的词书
- seq>=2
- 有[practice_record],且next_review_time < now()
- practice_time 不为今天
-
已完成复习数:
- 卡片为当前选择的词书
- practice_time 为今天
- seq > 1
-
需新学数:
MAX(每日计划量 / 5 - 已完成学习数,0) -
已完成学习数:
今日创建的卡片 且 seq = 1