短信打卡提醒

pull/445/head
wei 2025-08-12 17:49:54 +08:00
parent 41661e5ac4
commit 692f6225a3
8 changed files with 316 additions and 0 deletions

View File

@ -75,6 +75,18 @@
<groupId>de.codecentric</groupId> <groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId> <artifactId>spring-boot-admin-starter-client</artifactId>
</dependency> </dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.12.13</version>
</dependency>
<dependency>
<groupId>com.ujcms</groupId>
<artifactId>ujcms-commons</artifactId>
<version>9.6.0.0.1</version>
<scope>compile</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -0,0 +1,23 @@
package com.ruoyi.job.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;
/**
*
*
* @author ruoyi
*/
@Data
@Configuration
@RefreshScope
@ConfigurationProperties(prefix = "security.sms.aliyuncs")
public class SmsConfig
{
private String accessKeyId;
private String accessKeySecret;
private String signName;
private String templateCode;
}

View File

@ -0,0 +1,78 @@
package com.ruoyi.job.domain;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.ruoyi.common.core.web.domain.BaseEntity;
import com.ruoyi.job.util.ProgressFormatterUtil;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.data.annotation.Id;
import java.math.BigDecimal;
/**
*
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ScClassUserInfo extends BaseEntity {
@Id
private Long id;
//@ExcelIgnore // 不导出该字段
private Long userId;
//@ExcelIgnore // 不导出该字段
private Long classId;
//@ColumnWidth(25)
// @ExcelProperty("名称(报名时)")
private String name;
//@ColumnWidth(25)
//@ExcelProperty("单位")
private String firm;
//@ColumnWidth(25)
//@ExcelProperty("专业")
private String specialized;
//@ColumnWidth(35)
//@ExcelProperty("手机号")
private String phonenumber;
//@ColumnWidth(35)
//@ExcelProperty("昵称")
private String nickName;
/**
*
*/
//@ExcelProperty("客户订单编号")
private String userSn;
//@ExcelIgnore
private Boolean todayAttendanceIs;
@JsonIgnore
// @ExcelProperty("是否打卡")
private String attendanceIs;
//@ExcelProperty("签到次数")
private Integer attendanceCount;
//@ExcelIgnore
private BigDecimal progress;
//@ExcelProperty("总学习进度")
private String progressPercent;
public String getAttendanceIs() {
return attendanceCount.compareTo(0) > 0 ? "是" : "否";
}
public String getProgressPercent() {
return ProgressFormatterUtil.format(this.progress.multiply(BigDecimal.valueOf(100)));
}
}

View File

@ -0,0 +1,16 @@
package com.ruoyi.job.service;
import com.ruoyi.common.core.exception.CaptchaException;
/**
*
*
* @author ruoyi
*/
public interface SmsService
{
/**
*
*/
public void sendStudyNoticeSms(String phone) throws CaptchaException;
}

View File

@ -0,0 +1,35 @@
package com.ruoyi.job.service.impl;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.exception.CaptchaException;
import com.ruoyi.job.config.SmsConfig;
import com.ruoyi.job.service.SmsService;
import com.ujcms.commons.sms.AliyunUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
*
*
* @author ruoyi
*/
@Service
public class SmsServiceImpl implements SmsService
{
@Autowired
private SmsConfig smsConfig;
/**
*
*/
@Override
public void sendStudyNoticeSms(String phone) throws CaptchaException {
AliyunUtils.sendSms(smsConfig.getAccessKeyId(),
smsConfig.getAccessKeySecret(),
smsConfig.getSignName(),
smsConfig.getTemplateCode(),
JSONObject.of(),
phone
);
}
}

View File

@ -0,0 +1,34 @@
package com.ruoyi.job.task;
import com.ruoyi.job.config.SmsConfig;
import com.ruoyi.job.domain.ScClassUserInfo;
import com.ruoyi.job.mapper.ScClassUserInfoMapper;
import com.ruoyi.job.service.SmsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.List;
@Slf4j
@Component("liveTask")
public class LiveTask {
@Resource
ScClassUserInfoMapper scClassUserInfoMapper;
@Autowired
private SmsService smsAliyuncs;
@Autowired
private SmsConfig smsConfig;
/**
*
*/
public void CourseSmsNotice(){
List<ScClassUserInfo> list = scClassUserInfoMapper.waitNoticeCourseUser();
list.forEach(item->{
log.info("发送打卡提醒",item.getUserId(),item.getNickName(),item.getPhonenumber());
smsAliyuncs.sendStudyNoticeSms(item.getPhonenumber());
});
}
}

View File

@ -0,0 +1,18 @@
package com.ruoyi.job.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
public class ProgressFormatterUtil {
public static String format(BigDecimal progress ) {
if (progress == null) return "0";
// 保留两位小数(四舍五入)
BigDecimal scaled = progress.setScale(2, RoundingMode.HALF_UP);
// 去除末尾零和小数点
NumberFormat nf = NumberFormat.getInstance();
nf.setMaximumFractionDigits(2);
nf.setMinimumFractionDigits(0);
return nf.format(scaled) + "%";
}
}

View File

@ -0,0 +1,100 @@
<?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="com.ruoyi.job.mapper.ScClassUserInfoMapper">
<resultMap id="scClassUserInfo" type="com.ruoyi.job.domain.ScClassUserInfo">
<id column="id" property="id"/>
<result column="name" property="name"/>
<result column="firm" property="firm"/>
<result column="specialized" property="specialized"/>
<result column="nick_name" property="nickName"/>
<result column="todayAttendanceIs" property="todayAttendanceIs"/>
<result column="attendanceCount" property="attendanceCount"/>
<result column="progress" property="progress"/>
</resultMap>
<select id="waitNoticeCourseUser" resultMap="scClassUserInfo">
SELECT
progress.id,
progress.userId,
progress.classId,
progress.`name`,
progress.email,
progress.phonenumber,
progress.userSn,
progress.firm,
progress.posts,
progress.areas_of_focus,
progress.issue,
progress.specialized,
progress.nick_name,
attendance.todayAttendanceIs,
attendance.attendanceCount,
progress.progress
FROM
(
SELECT
scui.id,
COUNT( sua_today.id ) AS todayAttendanceIs,
COUNT( sua_count.id ) AS attendanceCount
FROM
sc_class_user_info scui
LEFT JOIN sc_user_attendance sua_today ON (
sua_today.class_info_id = scui.class_id
AND sua_today.user_id = scui.user_id
AND sua_today.`status` = 1
AND to_days( sua_today.create_time ) = to_days(
now())
)
LEFT JOIN sc_user_attendance sua_count ON ( sua_count.class_info_id = scui.class_id AND sua_count.user_id = scui.user_id AND sua_count.`status` = 1 )
WHERE
scui.`status` = 1
AND scui.end_time > now()
AND ( scui.delete_flag != scui.id OR scui.delete_flag IS NULL )
GROUP BY
scui.id
) attendance
INNER JOIN (
SELECT
scui.id,
scui.user_id AS userId,
scui.class_id AS classId,
scui.`name`,
scui.email,
scui.phonenumber,
scui.user_sn AS userSn,
scui.firm,
scui.posts,
scui.areas_of_focus,
scui.issue,
scui.specialized,
y.nick_name,
CASE
WHEN COUNT( DISTINCT video.id ) = 0 THEN
0 ELSE (
COUNT(
DISTINCT
CASE
WHEN ( TIME_TO_SEC( video.duration ) = 0 AND record.type_id IS NOT NULL )
OR (
record.total_duration >= 0.8 * TIME_TO_SEC( video.duration )) THEN
video.id
END
)
) / COUNT(DISTINCT video.id )
END AS progress
FROM
sc_class_user_info scui
INNER JOIN sys_user y ON scui.user_id = y.user_id
LEFT JOIN sc_video video ON ( video.class_info_id = scui.class_id AND video.`status` = 1 )
LEFT JOIN sc_class_user_record_info record ON ( record.type = 'ScVideo' AND record.type_id = video.id AND scui.user_id = record.user_id )
WHERE
scui.`status` = 1
AND scui.end_time > now()
AND ( scui.delete_flag != scui.id OR scui.delete_flag IS NULL )
GROUP BY
scui.id
) progress ON attendance.id = progress.id WHERE progress.progress &lt; 1 GROUP BY progress.userId
</select>
</mapper>