学员真实学习时长记录

This commit is contained in:
none 2023-03-22 14:46:49 +08:00
parent 79297f7264
commit 39f46d5ace
16 changed files with 381 additions and 36 deletions

View File

@ -1,11 +1,17 @@
package xyz.playedu.api.bus; package xyz.playedu.api.bus;
import lombok.SneakyThrows;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import xyz.playedu.api.PlayEduFCtx;
import xyz.playedu.api.caches.UserLastLearnTimeCache;
import xyz.playedu.api.domain.Course; import xyz.playedu.api.domain.Course;
import xyz.playedu.api.domain.CourseHour; import xyz.playedu.api.domain.CourseHour;
import xyz.playedu.api.domain.User; import xyz.playedu.api.domain.User;
import xyz.playedu.api.event.UserLearnCourseUpdateEvent;
import xyz.playedu.api.exception.ServiceException;
import xyz.playedu.api.service.CourseService; import xyz.playedu.api.service.CourseService;
import xyz.playedu.api.service.UserService; import xyz.playedu.api.service.UserService;
@ -24,6 +30,12 @@ public class UserBus {
@Autowired @Autowired
private UserService userService; private UserService userService;
@Autowired
private UserLastLearnTimeCache userLastLearnTimeCache;
@Autowired
private ApplicationContext ctx;
public boolean canSeeCourse(User user, Course course) { public boolean canSeeCourse(User user, Course course) {
List<Integer> courseDepIds = courseService.getDepIdsByCourseId(course.getId()); List<Integer> courseDepIds = courseService.getDepIdsByCourseId(course.getId());
if (courseDepIds == null || courseDepIds.size() == 0) { if (courseDepIds == null || courseDepIds.size() == 0) {
@ -36,5 +48,20 @@ public class UserBus {
} }
return CollectionUtils.intersection(courseDepIds, userDepIds).size() > 0; return CollectionUtils.intersection(courseDepIds, userDepIds).size() > 0;
} }
public void userLearnDurationRecord(User user, Course course, CourseHour hour) {
Long curTime = System.currentTimeMillis();
// 最近一次学习时间
Long lastTime = userLastLearnTimeCache.get(PlayEduFCtx.getUserId());
// 最大周期为10s
if (lastTime == null || curTime - lastTime > 10000) {
lastTime = curTime - 10000;
}
userLastLearnTimeCache.put(user.getId(), curTime);
ctx.publishEvent(new UserLearnCourseUpdateEvent(this, user.getId(), course.getId(), hour.getId(), lastTime, curTime));
}
} }

View File

@ -25,12 +25,13 @@ public class UserCanSeeCourseCache {
public boolean check(User user, Course course, boolean isThrow) throws ServiceException { public boolean check(User user, Course course, boolean isThrow) throws ServiceException {
boolean result; boolean result;
if (RedisUtil.exists(key(user, course))) { if (RedisUtil.exists(key(user, course))) {
result = "1".equals(RedisUtil.get(key(user, course))); String cacheResult = (String) RedisUtil.get(key(user, course));
result = "1".equals(cacheResult);
} else { } else {
result = userBus.canSeeCourse(user, course); result = userBus.canSeeCourse(user, course);
put(user, course, result); put(user, course, result);
} }
if (isThrow) { if (!result && isThrow) {
throw new ServiceException("无权限观看"); throw new ServiceException("无权限观看");
} }
return result; return result;

View File

@ -0,0 +1,24 @@
package xyz.playedu.api.caches;
import org.springframework.stereotype.Component;
import xyz.playedu.api.util.RedisUtil;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/22 13:57
*/
@Component
public class UserLastLearnTimeCache {
private final static String groupName = "user-learn-last-timestamp";
private final static int expire = 9500;//9.5s
public Long get(Integer userId) {
return (Long) RedisUtil.hGet(groupName, userId + "");
}
public void put(Integer userId, Long timestamp) {
RedisUtil.hSet(groupName, userId + "", timestamp);
}
}

View File

@ -1,17 +1,16 @@
package xyz.playedu.api.controller.frontend; package xyz.playedu.api.controller.frontend;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import xyz.playedu.api.PlayEduFCtx; import xyz.playedu.api.PlayEduFCtx;
import xyz.playedu.api.bus.UserBus;
import xyz.playedu.api.caches.CourseCache; import xyz.playedu.api.caches.CourseCache;
import xyz.playedu.api.caches.UserCanSeeCourseCache; import xyz.playedu.api.caches.UserCanSeeCourseCache;
import xyz.playedu.api.domain.*; import xyz.playedu.api.domain.*;
import xyz.playedu.api.exception.NotFoundException;
import xyz.playedu.api.exception.ServiceException;
import xyz.playedu.api.request.frontend.CourseHourRecordRequest; import xyz.playedu.api.request.frontend.CourseHourRecordRequest;
import xyz.playedu.api.service.CourseHourService; import xyz.playedu.api.service.CourseHourService;
import xyz.playedu.api.service.CourseService;
import xyz.playedu.api.service.ResourceService; import xyz.playedu.api.service.ResourceService;
import xyz.playedu.api.service.UserCourseHourRecordService; import xyz.playedu.api.service.UserCourseHourRecordService;
import xyz.playedu.api.types.JsonResponse; import xyz.playedu.api.types.JsonResponse;
@ -26,9 +25,6 @@ import java.util.HashMap;
@RequestMapping("/api/v1/course/{courseId}/hour") @RequestMapping("/api/v1/course/{courseId}/hour")
public class HourController { public class HourController {
@Autowired
private CourseService courseService;
@Autowired @Autowired
private CourseHourService hourService; private CourseHourService hourService;
@ -39,13 +35,18 @@ public class HourController {
private UserCourseHourRecordService userCourseHourRecordService; private UserCourseHourRecordService userCourseHourRecordService;
@Autowired @Autowired
private UserCanSeeCourseCache userCanSeeCourseCache; private UserBus userBus;
// ------- CACHE ----------
@Autowired
private UserCanSeeCourseCache userCanSeeCourseCache;
@Autowired @Autowired
private CourseCache courseCache; private CourseCache courseCache;
@GetMapping("/{id}/play") @GetMapping("/{id}/play")
public JsonResponse play(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) throws NotFoundException, ServiceException { @SneakyThrows
public JsonResponse play(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) {
Course course = courseCache.findOrFail(courseId); Course course = courseCache.findOrFail(courseId);
userCanSeeCourseCache.check(PlayEduFCtx.getUser(), course, true); userCanSeeCourseCache.check(PlayEduFCtx.getUser(), course, true);
CourseHour hour = hourService.findOrFail(id, courseId); CourseHour hour = hourService.findOrFail(id, courseId);
@ -60,7 +61,8 @@ public class HourController {
} }
@PostMapping("/{id}/record") @PostMapping("/{id}/record")
public JsonResponse record(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id, @RequestBody @Validated CourseHourRecordRequest req) throws NotFoundException, ServiceException { @SneakyThrows
public JsonResponse record(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id, @RequestBody @Validated CourseHourRecordRequest req) {
Integer duration = req.getDuration(); Integer duration = req.getDuration();
if (duration <= 0) { if (duration <= 0) {
return JsonResponse.error("duration参数错误"); return JsonResponse.error("duration参数错误");
@ -79,9 +81,12 @@ public class HourController {
} }
@PostMapping("/{id}/ping") @PostMapping("/{id}/ping")
public JsonResponse ping(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) throws NotFoundException, ServiceException { @SneakyThrows
public JsonResponse ping(@PathVariable(name = "courseId") Integer courseId, @PathVariable(name = "id") Integer id) {
Course course = courseCache.findOrFail(courseId); Course course = courseCache.findOrFail(courseId);
CourseHour hour = hourService.findOrFail(id, courseId);
userCanSeeCourseCache.check(PlayEduFCtx.getUser(), course, true); userCanSeeCourseCache.check(PlayEduFCtx.getUser(), course, true);
userBus.userLearnDurationRecord(PlayEduFCtx.getUser(), course, hour);
return JsonResponse.success(); return JsonResponse.success();
} }

View File

@ -6,8 +6,6 @@ import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable; import java.io.Serializable;
import java.util.Date; import java.util.Date;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data; import lombok.Data;
/** /**
@ -26,13 +24,12 @@ public class UserLearnDurationRecord implements Serializable {
/** /**
* *
*/ */
@JsonProperty("user_id")
private Integer userId; private Integer userId;
/** /**
* *
*/ */
private Date date; private Date createdDate;
/** /**
* 已学习时长[微秒] * 已学习时长[微秒]
@ -42,15 +39,23 @@ public class UserLearnDurationRecord implements Serializable {
/** /**
* 开始时间 * 开始时间
*/ */
@JsonProperty("start_at")
private Date startAt; private Date startAt;
/** /**
* 结束时间 * 结束时间
*/ */
@JsonProperty("end_at")
private Date endAt; private Date endAt;
/**
*
*/
private Integer courseId;
/**
*
*/
private Integer hourId;
@TableField(exist = false) @TableField(exist = false)
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -68,10 +73,12 @@ public class UserLearnDurationRecord implements Serializable {
UserLearnDurationRecord other = (UserLearnDurationRecord) that; UserLearnDurationRecord other = (UserLearnDurationRecord) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId())) return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId())) && (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
&& (this.getDate() == null ? other.getDate() == null : this.getDate().equals(other.getDate())) && (this.getCreatedDate() == null ? other.getCreatedDate() == null : this.getCreatedDate().equals(other.getCreatedDate()))
&& (this.getDuration() == null ? other.getDuration() == null : this.getDuration().equals(other.getDuration())) && (this.getDuration() == null ? other.getDuration() == null : this.getDuration().equals(other.getDuration()))
&& (this.getStartAt() == null ? other.getStartAt() == null : this.getStartAt().equals(other.getStartAt())) && (this.getStartAt() == null ? other.getStartAt() == null : this.getStartAt().equals(other.getStartAt()))
&& (this.getEndAt() == null ? other.getEndAt() == null : this.getEndAt().equals(other.getEndAt())); && (this.getEndAt() == null ? other.getEndAt() == null : this.getEndAt().equals(other.getEndAt()))
&& (this.getCourseId() == null ? other.getCourseId() == null : this.getCourseId().equals(other.getCourseId()))
&& (this.getHourId() == null ? other.getHourId() == null : this.getHourId().equals(other.getHourId()));
} }
@Override @Override
@ -80,10 +87,12 @@ public class UserLearnDurationRecord implements Serializable {
int result = 1; int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode()); result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode()); result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
result = prime * result + ((getDate() == null) ? 0 : getDate().hashCode()); result = prime * result + ((getCreatedDate() == null) ? 0 : getCreatedDate().hashCode());
result = prime * result + ((getDuration() == null) ? 0 : getDuration().hashCode()); result = prime * result + ((getDuration() == null) ? 0 : getDuration().hashCode());
result = prime * result + ((getStartAt() == null) ? 0 : getStartAt().hashCode()); result = prime * result + ((getStartAt() == null) ? 0 : getStartAt().hashCode());
result = prime * result + ((getEndAt() == null) ? 0 : getEndAt().hashCode()); result = prime * result + ((getEndAt() == null) ? 0 : getEndAt().hashCode());
result = prime * result + ((getCourseId() == null) ? 0 : getCourseId().hashCode());
result = prime * result + ((getHourId() == null) ? 0 : getHourId().hashCode());
return result; return result;
} }
@ -95,10 +104,12 @@ public class UserLearnDurationRecord implements Serializable {
sb.append("Hash = ").append(hashCode()); sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id); sb.append(", id=").append(id);
sb.append(", userId=").append(userId); sb.append(", userId=").append(userId);
sb.append(", date=").append(date); sb.append(", createdDate=").append(createdDate);
sb.append(", duration=").append(duration); sb.append(", duration=").append(duration);
sb.append(", startAt=").append(startAt); sb.append(", startAt=").append(startAt);
sb.append(", endAt=").append(endAt); sb.append(", endAt=").append(endAt);
sb.append(", courseId=").append(courseId);
sb.append(", hourId=").append(hourId);
sb.append(", serialVersionUID=").append(serialVersionUID); sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]"); sb.append("]");
return sb.toString(); return sb.toString();

View File

@ -0,0 +1,85 @@
package xyz.playedu.api.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.util.Date;
import lombok.Data;
/**
*
* @TableName user_learn_duration_stats
*/
@TableName(value ="user_learn_duration_stats")
@Data
public class UserLearnDurationStats implements Serializable {
/**
*
*/
@TableId(type = IdType.AUTO)
private Integer id;
/**
*
*/
private Integer userId;
/**
*
*/
private Integer duration;
/**
*
*/
private Date createdDate;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
UserLearnDurationStats other = (UserLearnDurationStats) that;
return (this.getId() == null ? other.getId() == null : this.getId().equals(other.getId()))
&& (this.getUserId() == null ? other.getUserId() == null : this.getUserId().equals(other.getUserId()))
&& (this.getDuration() == null ? other.getDuration() == null : this.getDuration().equals(other.getDuration()))
&& (this.getCreatedDate() == null ? other.getCreatedDate() == null : this.getCreatedDate().equals(other.getCreatedDate()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
result = prime * result + ((getUserId() == null) ? 0 : getUserId().hashCode());
result = prime * result + ((getDuration() == null) ? 0 : getDuration().hashCode());
result = prime * result + ((getCreatedDate() == null) ? 0 : getCreatedDate().hashCode());
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", id=").append(id);
sb.append(", userId=").append(userId);
sb.append(", duration=").append(duration);
sb.append(", createdDate=").append(createdDate);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}

View File

@ -0,0 +1,31 @@
package xyz.playedu.api.event;
import lombok.Getter;
import lombok.Setter;
import org.springframework.context.ApplicationEvent;
import java.util.Date;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/22 14:14
*/
@Setter
@Getter
public class UserLearnCourseUpdateEvent extends ApplicationEvent {
private Integer userId;
private Integer courseId;
private Integer hourId;
private Long startAt;
private Long endAt;
public UserLearnCourseUpdateEvent(Object source, Integer userId, Integer courseId, Integer hourId, Long startTime, Long endTime) {
super(source);
this.userId = userId;
this.courseId = courseId;
this.hourId = hourId;
this.startAt = startTime;
this.endAt = endTime;
}
}

View File

@ -0,0 +1,35 @@
package xyz.playedu.api.listener;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import xyz.playedu.api.event.UserLearnCourseUpdateEvent;
import xyz.playedu.api.service.UserLearnDurationRecordService;
import xyz.playedu.api.service.UserLearnDurationStatsService;
/**
* @Author 杭州白书科技有限公司
* @create 2023/3/22 14:18
*/
@Component
@Slf4j
public class UserLearnCourseUpdateListener {
@Autowired
private UserLearnDurationRecordService userLearnDurationRecordService;
@Autowired
private UserLearnDurationStatsService userLearnDurationStatsService;
@Async
@EventListener
public void storeLearnDuration(UserLearnCourseUpdateEvent event) {
// 观看时长统计
userLearnDurationStatsService.storeOrUpdate(event.getUserId(), event.getStartAt(), event.getEndAt());
// 观看记录
userLearnDurationRecordService.store(event.getUserId(), event.getCourseId(), event.getHourId(), event.getStartAt(), event.getEndAt());
}
}

View File

@ -7,7 +7,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/** /**
* @author tengteng * @author tengteng
* @description 针对表user_learn_duration_records的数据库操作Mapper * @description 针对表user_learn_duration_records的数据库操作Mapper
* @createDate 2023-03-20 16:41:12 * @createDate 2023-03-22 13:55:17
* @Entity xyz.playedu.api.domain.UserLearnDurationRecord * @Entity xyz.playedu.api.domain.UserLearnDurationRecord
*/ */
@Mapper @Mapper

View File

@ -0,0 +1,20 @@
package xyz.playedu.api.mapper;
import org.apache.ibatis.annotations.Mapper;
import xyz.playedu.api.domain.UserLearnDurationStats;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
/**
* @author tengteng
* @description 针对表user_learn_duration_stats的数据库操作Mapper
* @createDate 2023-03-22 13:55:29
* @Entity xyz.playedu.api.domain.UserLearnDurationStats
*/
@Mapper
public interface UserLearnDurationStatsMapper extends BaseMapper<UserLearnDurationStats> {
}

View File

@ -4,10 +4,10 @@ import xyz.playedu.api.domain.UserLearnDurationRecord;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
/** /**
* @author tengteng * @author tengteng
* @description 针对表user_learn_duration_records的数据库操作Service * @description 针对表user_learn_duration_records的数据库操作Service
* @createDate 2023-03-20 16:41:12 * @createDate 2023-03-20 16:41:12
*/ */
public interface UserLearnDurationRecordService extends IService<UserLearnDurationRecord> { public interface UserLearnDurationRecordService extends IService<UserLearnDurationRecord> {
void store(Integer userId, Integer courseId, Integer hourId, Long startTime, Long endTime);
} }

View File

@ -0,0 +1,13 @@
package xyz.playedu.api.service;
import xyz.playedu.api.domain.UserLearnDurationStats;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* @author tengteng
* @description 针对表user_learn_duration_stats的数据库操作Service
* @createDate 2023-03-22 13:55:29
*/
public interface UserLearnDurationStatsService extends IService<UserLearnDurationStats> {
void storeOrUpdate(Integer userId, Long startTime, Long endTime);
}

View File

@ -1,20 +1,42 @@
package xyz.playedu.api.service.impl; package xyz.playedu.api.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import xyz.playedu.api.domain.UserLearnDurationRecord; import xyz.playedu.api.domain.UserLearnDurationRecord;
import xyz.playedu.api.service.UserLearnDurationRecordService; import xyz.playedu.api.service.UserLearnDurationRecordService;
import xyz.playedu.api.mapper.UserLearnDurationRecordMapper; import xyz.playedu.api.mapper.UserLearnDurationRecordMapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
/** /**
* @author tengteng * @author tengteng
* @description 针对表user_learn_duration_records的数据库操作Service实现 * @description 针对表user_learn_duration_records的数据库操作Service实现
* @createDate 2023-03-20 16:41:12 * @createDate 2023-03-20 16:41:12
*/ */
@Service @Service
public class UserLearnDurationRecordServiceImpl extends ServiceImpl<UserLearnDurationRecordMapper, UserLearnDurationRecord> public class UserLearnDurationRecordServiceImpl extends ServiceImpl<UserLearnDurationRecordMapper, UserLearnDurationRecord>
implements UserLearnDurationRecordService{ implements UserLearnDurationRecordService {
@Override
@SneakyThrows
public void store(Integer userId, Integer courseId, Integer hourId, Long startTime, Long endTime) {
// 处理日期
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String date = simpleDateFormat.format(new Date(endTime));
UserLearnDurationRecord record = new UserLearnDurationRecord();
record.setUserId(userId);
record.setCourseId(courseId);
record.setHourId(hourId);
record.setStartAt(new Date(startTime));
record.setEndAt(new Date(endTime));
record.setDuration((int) (endTime - startTime));
record.setCreatedDate(simpleDateFormat.parse(date));
save(record);
}
} }

View File

@ -0,0 +1,50 @@
package xyz.playedu.api.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.SneakyThrows;
import xyz.playedu.api.domain.UserLearnDurationStats;
import xyz.playedu.api.service.UserLearnDurationStatsService;
import xyz.playedu.api.mapper.UserLearnDurationStatsMapper;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author tengteng
* @description 针对表user_learn_duration_stats的数据库操作Service实现
* @createDate 2023-03-22 13:55:29
*/
@Service
public class UserLearnDurationStatsServiceImpl extends ServiceImpl<UserLearnDurationStatsMapper, UserLearnDurationStats>
implements UserLearnDurationStatsService {
@Override
@SneakyThrows
public void storeOrUpdate(Integer userId, Long startTime, Long endTime) {
// 处理日期
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
String date = simpleDateFormat.format(new Date(endTime));
// duration
Long duration = endTime - startTime;
UserLearnDurationStats stats = getOne(query().getWrapper().eq("user_id", userId).eq("created_date", date));
if (stats == null) {
UserLearnDurationStats newStats = new UserLearnDurationStats();
newStats.setUserId(userId);
newStats.setDuration(Math.toIntExact(duration));
newStats.setCreatedDate(simpleDateFormat.parse(date));
save(newStats);
return;
}
UserLearnDurationStats newStats = new UserLearnDurationStats();
newStats.setId(stats.getId());
newStats.setDuration((int) (stats.getDuration() + duration));
updateById(newStats);
}
}

View File

@ -7,14 +7,17 @@
<resultMap id="BaseResultMap" type="xyz.playedu.api.domain.UserLearnDurationRecord"> <resultMap id="BaseResultMap" type="xyz.playedu.api.domain.UserLearnDurationRecord">
<id property="id" column="id" jdbcType="BIGINT"/> <id property="id" column="id" jdbcType="BIGINT"/>
<result property="userId" column="user_id" jdbcType="INTEGER"/> <result property="userId" column="user_id" jdbcType="INTEGER"/>
<result property="date" column="date" jdbcType="DATE"/> <result property="createdDate" column="created_date" jdbcType="DATE"/>
<result property="duration" column="duration" jdbcType="INTEGER"/> <result property="duration" column="duration" jdbcType="INTEGER"/>
<result property="startAt" column="start_at" jdbcType="TIMESTAMP"/> <result property="startAt" column="start_at" jdbcType="TIMESTAMP"/>
<result property="endAt" column="end_at" jdbcType="TIMESTAMP"/> <result property="endAt" column="end_at" jdbcType="TIMESTAMP"/>
<result property="courseId" column="course_id" jdbcType="INTEGER"/>
<result property="hourId" column="hour_id" jdbcType="INTEGER"/>
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
id,user_id,date, id,user_id,created_date,
duration,start_at,end_at duration,start_at,end_at,
course_id,hour_id
</sql> </sql>
</mapper> </mapper>

View File

@ -0,0 +1,18 @@
<?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="xyz.playedu.api.mapper.UserLearnDurationStatsMapper">
<resultMap id="BaseResultMap" type="xyz.playedu.api.domain.UserLearnDurationStats">
<id property="id" column="id" jdbcType="INTEGER"/>
<result property="userId" column="user_id" jdbcType="INTEGER"/>
<result property="duration" column="duration" jdbcType="INTEGER"/>
<result property="createdDate" column="created_date" jdbcType="DATE"/>
</resultMap>
<sql id="Base_Column_List">
id,user_id,duration,
created_date
</sql>
</mapper>