考勤新增未打卡, 微信公众号通知
This commit is contained in:
parent
3d5a53ef71
commit
4e24321256
@ -229,6 +229,18 @@
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.9</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
<version>5.8.35</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
||||
@ -24,8 +24,10 @@ import com.evo.system.domain.SysStaffDetail;
|
||||
import com.evo.system.mapper.SysDeptMapper;
|
||||
import com.evo.system.mapper.SysStaffDetailMapper;
|
||||
import com.evo.system.mapper.SysStaffMapper;
|
||||
import com.evo.wechat.service.SendClientService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
@ -63,6 +65,9 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
|
||||
@Resource
|
||||
private SysStaffDetailMapper sysStaffDetailMapper;
|
||||
|
||||
@Resource
|
||||
private SendClientService sendClientService;
|
||||
|
||||
@Override
|
||||
public boolean accept(String sn) {
|
||||
//餐饮打卡机不下发按钮
|
||||
@ -222,81 +227,29 @@ public class KQDeviceExchangeProcessor implements PunchTheClockStrategyExchangeP
|
||||
RzAttendance beforeAttendance = rzAttendanceMapper.queryNowDayAttendanceByStatisticalIdAndDate(Long.valueOf(userId),DateUtils.addDays(date, -1));
|
||||
if(beforeAttendance.getWorkStartTime() != null && beforeAttendance.getWorkEndTime() == null){
|
||||
notBeforeEndTimeMessage = " - 昨天未打下班卡";
|
||||
sendAbnormalAttendance(userId, beforeAttendance.getWorkStartTime());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
log.error("查询前一天的打卡情况报错了");
|
||||
}
|
||||
|
||||
return (rzAttendanceMapper.updateRzAttendance(attendance) > 0) ? initMessage(0, "打卡成功"+notBeforeEndTimeMessage) : initMessage(1, "打卡失败"+notBeforeEndTimeMessage);
|
||||
}
|
||||
|
||||
// /*** 上班卡的下班卡
|
||||
// * @param date
|
||||
// * @param rules
|
||||
// * @return
|
||||
// */
|
||||
// public String workOffDutyCard(Date date, String rules, RzAttendance attendance){
|
||||
//
|
||||
// //计算工时
|
||||
// Long hours = (date.getTime() - attendance.getWorkStartTime().getTime())/1000/60/60;
|
||||
//
|
||||
// EqButton eqButton = eqButtonMapper.selectOne(new LambdaQueryWrapper<EqButton>().eq(EqButton::getName, rules));
|
||||
//
|
||||
// Integer maxWorkHour = ParamUtils.getTsWorkHour(attendance.getName());
|
||||
// if(maxWorkHour == null){
|
||||
// maxWorkHour = eqButton.getWorkHour();
|
||||
// }
|
||||
//
|
||||
// //判断打卡时间, 添加夜班次数 打卡时间在晚上7点以后
|
||||
// if(eqButton.getWorkHour() ==12 && hours >= 12 && attendance.getWorkStartTime().getHours() > 12){
|
||||
// attendance.setNightNumber(1);
|
||||
// }else if(eqButton.getWorkHour() == 8 && hours >= 8 && attendance.getWorkStartTime().getHours() > 18){
|
||||
// attendance.setMiddleShiftNumber(1);
|
||||
// }
|
||||
// //获取工作时长
|
||||
// BigDecimal res = new BigDecimal(hours);
|
||||
// if(hours.compareTo(Long.valueOf(maxWorkHour)) >= 0){
|
||||
// res = new BigDecimal(maxWorkHour);
|
||||
// }
|
||||
//// else if(hours.compareTo(Long.valueOf(eqButton.getWorkHour()/2)) > 0){
|
||||
//// res = new BigDecimal(eqButton.getWorkHour()/2);
|
||||
//// }
|
||||
// //检查是否异常
|
||||
// if(checkYc("下班", rules, sdf.format(date), sdfd.format(date))){
|
||||
// attendance.setYcxFlag("1");
|
||||
// }
|
||||
//
|
||||
// attendance.setWorkEndTime(date);
|
||||
// attendance.setWorkSum(res);
|
||||
// return (rzAttendanceMapper.updateRzAttendance(attendance) > 0) ? initMessage(0, "打卡成功") : initMessage(1,"打卡失败");
|
||||
// }
|
||||
@Async
|
||||
public void sendAbnormalAttendance(Long userId, Date date){
|
||||
try {
|
||||
SysStaff sysStaff = sysStaffMapper.selectById(userId);
|
||||
if(sysStaff != null && StringUtils.isNotEmpty(sysStaff.getOpenid())){
|
||||
SysDept sysDept = sysDeptMapper.selectById(sysStaff.getDeptId());
|
||||
sendClientService.sendAbnormalAttendance(sysDept != null ? sysDept.getDeptName() : "未知",sysStaff.getName(), "昨天下班未打卡", date, sysStaff.getOpenid());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("推送公众号打卡异常失败, 原因为: {}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// /*** 校验是否上班卡打卡异常
|
||||
// * @param rules
|
||||
// * @param dateTime
|
||||
// * @param date
|
||||
// * @return
|
||||
// */
|
||||
// public Boolean checkYc(String type, String rules, String dateTime, String date){
|
||||
// String script = ParamUtils.getYcRulesByButtonName(type, rules);
|
||||
// if(StringUtils.isEmpty(script)){
|
||||
// return false;
|
||||
// }
|
||||
// ScriptEngineManager manager = new ScriptEngineManager();
|
||||
// ScriptEngine engine = manager.getEngineByName("javascript");
|
||||
// try {
|
||||
// Object result = engine.eval(script.replaceAll("dkDateTime",dateTime).replaceAll("dkDate", date));
|
||||
// System.out.println("检查结果"+result);
|
||||
// return new Boolean(String.valueOf(result));
|
||||
// } catch (Exception e) {
|
||||
// logger.error("打卡交易是否异常出现错误{}", e.getMessage());
|
||||
// }
|
||||
// //默认为没有异常
|
||||
// return false;
|
||||
// }
|
||||
|
||||
/*** 撤销卡
|
||||
* @param date
|
||||
* @return
|
||||
|
||||
@ -12,6 +12,7 @@ import com.evo.personnelMatters.mapper.RzOverTimeMapper;
|
||||
import com.evo.restaurant.service.IRzRestaurantStatisticsService;
|
||||
import com.evo.system.domain.SysStaff;
|
||||
import com.evo.system.service.ISysStaffService;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@ -81,6 +82,36 @@ public class TestController {
|
||||
|
||||
|
||||
|
||||
@GetMapping("/message")
|
||||
public String message(Model model, String code) {
|
||||
|
||||
System.out.println(code);
|
||||
// String res = AccessTokenUtil.accessToken2("wx268e32962db19f5f", "84a6065165ec82862c5e03a010a6dc6c", code);
|
||||
// System.out.println(res);
|
||||
// JSONObject obj = JSONObject.parseObject(res);
|
||||
// String openid = obj.getString("openid");
|
||||
SysStaff user = new SysStaff();
|
||||
user.setOpenid(code);
|
||||
model.addAttribute("user", user);
|
||||
//model.addAttribute("openid", openid);
|
||||
// ErrorData templateData = new ErrorData();
|
||||
//
|
||||
// templateData.setThing1((JSONObject)JSON.toJSON(Collections.asMap("value", "研发部")));
|
||||
// templateData.setThing2((JSONObject)JSON.toJSON(Collections.asMap("value", "张三")));//动态跟换车牌号
|
||||
// templateData.setConst4((JSONObject)JSON.toJSON(Collections.asMap("value", "昨天下班未打卡")));//动态跟换车牌号
|
||||
// templateData.setTime6((JSONObject)JSON.toJSON(Collections.asMap("value", DateUtil.format(new Date(), "yyyy年MM月dd日"))));//动态跟换车牌号
|
||||
//
|
||||
// MessageTemplateSendData sendData = new MessageTemplateSendData();
|
||||
// sendData.setTouser(openid);
|
||||
// sendData.setTemplate_id("z9sy-38K-iC5MAWHbxcxwg1c-9oNTFWeCOoy6B6zdKY");
|
||||
// sendData.setData((JSONObject)JSON.toJSON(templateData));
|
||||
// TemplateMessageUtil.templateMessageSend(GZHAccessTokenService.gzhAccessToken(), sendData);
|
||||
|
||||
return "user_info.html";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 清洗加班
|
||||
*/
|
||||
|
||||
@ -7,7 +7,9 @@ import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import lombok.Data;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
@ -24,6 +26,7 @@ public class SysDept extends BaseEntity
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** 部门ID */
|
||||
@TableId(value = "dept_id", type = IdType.AUTO)
|
||||
private Long deptId;
|
||||
|
||||
/** 父部门ID */
|
||||
|
||||
@ -36,6 +36,28 @@ public class RedisCache
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
* @param key 缓存的键值
|
||||
* @param value 缓存的值
|
||||
* @param timeout 时间
|
||||
* @param timeUnit 时间颗粒度
|
||||
*/
|
||||
public <T> void setCacheObject(final String key, final T value, final Long timeout)
|
||||
{
|
||||
try {
|
||||
if (timeout > 0) {
|
||||
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.SECONDS);
|
||||
} else {
|
||||
setCacheObject(key, value);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 缓存基本的对象,Integer、String、实体类等
|
||||
*
|
||||
|
||||
@ -116,7 +116,8 @@ public class SecurityConfig
|
||||
// 静态资源,可匿名访问
|
||||
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
|
||||
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
|
||||
.antMatchers("/websocket/**").permitAll()
|
||||
.antMatchers("/wechat/**").permitAll()
|
||||
.antMatchers("/websocket/**").permitAll()
|
||||
.antMatchers("/test/**").permitAll()
|
||||
.antMatchers("/api/v1/verify_user").permitAll()
|
||||
.antMatchers("/api/v1/record/face").permitAll()
|
||||
|
||||
@ -2,10 +2,7 @@ package com.evo.restaurant.service.impl;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.evo.common.constant.Constants;
|
||||
import com.evo.common.core.domain.AjaxResult;
|
||||
import com.evo.common.utils.DateUtils;
|
||||
import com.evo.common.utils.ParamUtils;
|
||||
import com.evo.common.utils.SecurityUtils;
|
||||
import com.evo.common.utils.StringUtils;
|
||||
import com.evo.restaurant.domain.RzRestaurantDetail;
|
||||
import com.evo.restaurant.domain.RzRestaurantImages;
|
||||
@ -19,6 +16,7 @@ import com.evo.restaurant.service.IRzRestaurantDetailService;
|
||||
import com.evo.system.domain.SysStaff;
|
||||
import com.evo.system.mapper.SysStaffMapper;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
@ -63,6 +61,7 @@ public class RzRestaurantDetailServiceImpl implements IRzRestaurantDetailService
|
||||
@Override
|
||||
public String insertRzRestaurantDetail(String json)
|
||||
{
|
||||
System.out.println(json.toString());
|
||||
//需要返回的对象
|
||||
RestaurantVo cfiv = new RestaurantVo();
|
||||
RestaurantData cfd = new RestaurantData();
|
||||
@ -76,7 +75,8 @@ public class RzRestaurantDetailServiceImpl implements IRzRestaurantDetailService
|
||||
//if(hour!=7&&hour!=8&&hour!=11&&hour!=12&&hour!=13&&hour!=18&&hour!=19&&hour!=20){
|
||||
if(!ParamUtils.getCanteenOpenHour().contains(hour)){
|
||||
cfiv.setResult(2);
|
||||
cfiv.setMsg("{\"Result\":1,\"Msg\":\"不在打卡时间内\"}");
|
||||
cfiv.setMsg("不在打卡时间内");
|
||||
System.out.println(JSONObject.toJSONString(cfiv));
|
||||
return JSONObject.toJSONString(cfiv);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ public class RzRestaurantDetailServiceImpl implements IRzRestaurantDetailService
|
||||
RzRestaurantImages img_obj = rzRestaurantImagesMapper.selectRzRestaurantImagesById(Long.valueOf(userId));
|
||||
if(StringUtils.isNull(sysStaff) && StringUtils.isNull(img_obj)){
|
||||
cfiv.setResult(2);
|
||||
cfiv.setMsg("{\"Result\":1,\"Msg\":\"验证失败,未设置考勤权限\"}");
|
||||
cfiv.setMsg("当前人员信息不存在");
|
||||
return JSONObject.toJSONString(cfiv);
|
||||
}
|
||||
|
||||
@ -100,6 +100,9 @@ public class RzRestaurantDetailServiceImpl implements IRzRestaurantDetailService
|
||||
if(StringUtils.isNotNull(restaurantDetail) && (new Date().getTime() - restaurantDetail.getTime().getTime()) < 1000*60*60*3){
|
||||
//如果不是第一次打卡,则弹窗提示
|
||||
cfiv.setResult(1);
|
||||
//返回打卡次数
|
||||
cfd.setClock_in_count(1);
|
||||
cfiv.setContent(cfd);
|
||||
cfiv.setMsg("重复打卡!!");
|
||||
return JSONObject.toJSONString(cfiv);
|
||||
}
|
||||
@ -134,17 +137,17 @@ public class RzRestaurantDetailServiceImpl implements IRzRestaurantDetailService
|
||||
int i = rzRestaurantDetailMapper.insertRzRestaurantDetail(cyFaceInfo);
|
||||
if(i<1){
|
||||
cfiv.setResult(2);
|
||||
cfiv.setMsg("{\"Result\":1,\"Msg\":\"打卡失败\"}");
|
||||
cfiv.setMsg("打卡失败");
|
||||
return JSONObject.toJSONString(cfiv);
|
||||
}
|
||||
//反写统计次数
|
||||
i = rzRestaurantStatisticsMapper.updateRzRestaurantStatistics(restaurantStatistics);
|
||||
if(i<1){
|
||||
cfiv.setResult(2);
|
||||
cfiv.setMsg("{\"Result\":1,\"Msg\":\"打卡失败\"}");
|
||||
cfiv.setMsg("打卡失败");
|
||||
return JSONObject.toJSONString(cfiv);
|
||||
}
|
||||
cfiv.setMsg("{\"Result\":1,\"Msg\":\"打卡成功!\"}");
|
||||
cfiv.setMsg("打卡成功");
|
||||
cfd.setClock_in_count(1);
|
||||
cfiv.setContent(cfd);
|
||||
cfiv.setResult(0);
|
||||
|
||||
@ -198,6 +198,11 @@ public class SysStaff extends BaseEntity
|
||||
* 打卡位置
|
||||
*/
|
||||
private String timeClock;
|
||||
|
||||
/***
|
||||
* 公众号的openId
|
||||
*/
|
||||
private String openid;
|
||||
/***
|
||||
* 享有补贴
|
||||
*/
|
||||
|
||||
48
evo-admin/src/main/java/com/evo/wechat/AccessTokenUtil.java
Normal file
48
evo-admin/src/main/java/com/evo/wechat/AccessTokenUtil.java
Normal file
@ -0,0 +1,48 @@
|
||||
package com.evo.wechat;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 调用凭据
|
||||
*/
|
||||
public class AccessTokenUtil {
|
||||
|
||||
private static String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token";
|
||||
|
||||
|
||||
/**
|
||||
* 获取接口调用凭据
|
||||
* GET https://api.weixin.qq.com/cgi-bin/token
|
||||
* @param appid
|
||||
* @param secret
|
||||
* @return
|
||||
*/
|
||||
public static String accessToken(String appid, String secret) {
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
m.put("appid", appid);
|
||||
m.put("secret", secret);
|
||||
m.put("grant_type", "client_credential");
|
||||
String res = HttpUtil.get(accessTokenUrl, m);
|
||||
return res;
|
||||
}
|
||||
|
||||
private static String accessTokenUrl2 = "https://api.weixin.qq.com/sns/oauth2/access_token";
|
||||
/**
|
||||
* 获取接口调用凭据
|
||||
* GET https://api.weixin.qq.com/cgi-bin/token
|
||||
* @return
|
||||
*/
|
||||
public static String accessToken2(String appid, String secret, String code) {
|
||||
Map<String, Object> m = new HashMap<String, Object>();
|
||||
m.put("appid",appid);
|
||||
m.put("secret", secret);
|
||||
m.put("code", code);
|
||||
m.put("grant_type", "authorization_code");
|
||||
String res = HttpUtil.get(accessTokenUrl2, m);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.evo.wechat;
|
||||
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.evo.wechat.dto.MessageTemplate;
|
||||
|
||||
public class TemplateMessageUtil {
|
||||
|
||||
|
||||
private static String templateSendUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=";
|
||||
|
||||
/**
|
||||
* 发送模板消息 POST
|
||||
* @param accessToken
|
||||
* @param templateData
|
||||
* @return
|
||||
*/
|
||||
public static String templateMessageSend(String accessToken, MessageTemplate templateData) {
|
||||
String body = JSONObject.toJSONString(templateData);
|
||||
String res = HttpRequest.post(templateSendUrl + accessToken)
|
||||
.header("Content-Type", "application/json")
|
||||
.body(body)
|
||||
.execute()
|
||||
.body();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
67
evo-admin/src/main/java/com/evo/wechat/WeChatController.java
Normal file
67
evo-admin/src/main/java/com/evo/wechat/WeChatController.java
Normal file
@ -0,0 +1,67 @@
|
||||
package com.evo.wechat;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.evo.system.domain.SysStaff;
|
||||
import com.evo.system.service.ISysStaffService;
|
||||
import com.evo.wechat.config.GZHProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 类
|
||||
*
|
||||
* @ClassName:WeChatController
|
||||
* @date: 2025年08月29日 16:39
|
||||
* @author: andy.shi
|
||||
* @remark: 开发人员联系方式 1042025947@qq.com/微信同步
|
||||
*/
|
||||
@Slf4j
|
||||
@Controller
|
||||
@RequestMapping("/wechat")
|
||||
public class WeChatController {
|
||||
|
||||
@Resource
|
||||
private GZHProperties gzhProperties;
|
||||
@Resource
|
||||
private ISysStaffService sysStaffService;
|
||||
|
||||
|
||||
@GetMapping("/callback")
|
||||
public String message(Model model, String code) {
|
||||
log.info("获取到的code{}", code);
|
||||
String res = AccessTokenUtil.accessToken2(gzhProperties.getAppid(), gzhProperties.getAppSecret(), code);
|
||||
log.info("获取到的openId相关信息{}", code);
|
||||
JSONObject obj = JSONObject.parseObject(res);
|
||||
SysStaff user = new SysStaff();
|
||||
user.setOpenid(obj.getString("openid"));
|
||||
model.addAttribute("user", user);
|
||||
return "user_info.html";
|
||||
}
|
||||
|
||||
@PostMapping("/submit_user")
|
||||
public String submitUser(Model model, @ModelAttribute("user") SysStaff user) {
|
||||
log.info("获取到的code{}", user.toString());
|
||||
|
||||
SysStaff sysStaff = sysStaffService.getOne(new LambdaQueryWrapper<SysStaff>().eq(SysStaff::getIdCard, user.getIdCard()), false);
|
||||
if(ObjectUtils.isEmpty(sysStaff)){
|
||||
sysStaff = sysStaffService.getOne(new LambdaQueryWrapper<SysStaff>().eq(SysStaff::getPhone, user.getPhone()), false);
|
||||
}
|
||||
if(ObjectUtils.isEmpty(sysStaff)){
|
||||
model.addAttribute("message", "处理失败, 请检查你的个人信息录入是否正确");
|
||||
return "success.html";
|
||||
}
|
||||
sysStaff.setOpenid(user.getOpenid());
|
||||
sysStaffService.updateById(sysStaff);
|
||||
model.addAttribute("message", "处理成功");
|
||||
return "success.html";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.evo.wechat.config;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "yt.gzh", ignoreUnknownFields = true)
|
||||
@Data
|
||||
@Schema(description = "公众号信息")
|
||||
public class GZHProperties {
|
||||
|
||||
private String appid;
|
||||
|
||||
private String appSecret;
|
||||
//打卡异常通知
|
||||
private String abnormalAttendanceTemplateId;
|
||||
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.evo.wechat.dto;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 考勤异常消息模版
|
||||
*/
|
||||
@Data
|
||||
public class AbnormalAttendanceTemplate {
|
||||
//部门
|
||||
private JSONObject thing1;
|
||||
//姓名
|
||||
private JSONObject thing2;
|
||||
//异常类型 昨天上班未打卡 or 昨天下班未打卡
|
||||
private JSONObject const4;
|
||||
//考勤异常日期
|
||||
private JSONObject time6;
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.evo.wechat.dto;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Data
|
||||
public class MessageTemplate implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = -8911097073333862L;
|
||||
|
||||
private String touser;
|
||||
|
||||
private String template_id;
|
||||
|
||||
@Schema(description = "模板跳转链接")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "防重入id。对于同一个openid + client_msg_id, 只发送一条消息,10分钟有效")
|
||||
private String client_msg_id;
|
||||
|
||||
@Schema(description = "模板数据")
|
||||
private JSONObject data;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package com.evo.wechat.service;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.evo.common.core.redis.RedisCache;
|
||||
import com.evo.wechat.AccessTokenUtil;
|
||||
import com.evo.wechat.config.GZHProperties;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
@Service
|
||||
public class GZHAccessTokenService {
|
||||
|
||||
@Resource
|
||||
GZHProperties gzhProperties;
|
||||
@Resource
|
||||
private RedisCache redisUtil;
|
||||
|
||||
private String accessTokenPrefix = "hd:wechat:accessToken:gzh:";
|
||||
|
||||
public String gzhAccessToken() {
|
||||
|
||||
if (redisUtil.hasKey(accessTokenPrefix + gzhProperties.getAppid())) {
|
||||
return redisUtil.getCacheObject(accessTokenPrefix + gzhProperties.getAppid()).toString();
|
||||
}
|
||||
|
||||
String res = AccessTokenUtil.accessToken(gzhProperties.getAppid(),gzhProperties.getAppSecret());
|
||||
JSONObject jo = JSONObject.parseObject(res);
|
||||
// {"errcode":40013,"errmsg":"invalid appid rid: 6708ba65-0b425b74-4620599c"}
|
||||
if (StringUtils.hasText(jo.getString("errcode"))) {
|
||||
throw new RuntimeException(res);
|
||||
}
|
||||
redisUtil.setCacheObject(accessTokenPrefix + gzhProperties.getAppid(), jo.getString("access_token"), 7200L);
|
||||
return jo.getString("access_token");
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.evo.wechat.service;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.evo.common.utils.Collections;
|
||||
import com.evo.wechat.TemplateMessageUtil;
|
||||
import com.evo.wechat.config.GZHProperties;
|
||||
import com.evo.wechat.dto.AbnormalAttendanceTemplate;
|
||||
import com.evo.wechat.dto.MessageTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 类
|
||||
*
|
||||
* @ClassName:SendClientService
|
||||
* @date: 2025年08月29日 16:53
|
||||
* @author: andy.shi
|
||||
* @remark: 开发人员联系方式 1042025947@qq.com/微信同步
|
||||
*/
|
||||
@Service
|
||||
public class SendClientService {
|
||||
|
||||
@Resource
|
||||
GZHAccessTokenService gzhAccessTokenService;
|
||||
@Resource
|
||||
GZHProperties gzhProperties;
|
||||
|
||||
|
||||
public void sendAbnormalAttendance(String deptName, String userName, String info, Date date, String openId){
|
||||
AbnormalAttendanceTemplate templateData = new AbnormalAttendanceTemplate();
|
||||
templateData.setThing1((JSONObject) JSON.toJSON(Collections.asMap("value", deptName)));
|
||||
templateData.setThing2((JSONObject)JSON.toJSON(Collections.asMap("value",userName)));//动态跟换车牌号
|
||||
templateData.setConst4((JSONObject)JSON.toJSON(Collections.asMap("value", info)));//动态跟换车牌号
|
||||
templateData.setTime6((JSONObject)JSON.toJSON(Collections.asMap("value", DateUtil.format(date, "yyyy年MM月dd日"))));//动态跟换车牌号
|
||||
|
||||
MessageTemplate sendData = new MessageTemplate();
|
||||
sendData.setTouser(openId);
|
||||
sendData.setTemplate_id(gzhProperties.getAbnormalAttendanceTemplateId());
|
||||
sendData.setData((JSONObject)JSON.toJSON(templateData));
|
||||
TemplateMessageUtil.templateMessageSend(gzhAccessTokenService.gzhAccessToken(), sendData);
|
||||
}
|
||||
|
||||
}
|
||||
@ -52,7 +52,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</resultMap>
|
||||
|
||||
<sql id="selectSysStaffVo">
|
||||
select user_id,company_name, dept_id, code, name, id_card,is_leader, sex, age, phone, address, level, major, school, bank_number,social_subsidy, bank, employment_date, experience, worker_term, regular_date, quit_date, contract_start, contract_end, contract_type, social_type, seniority, is_overtime_pay, zs_flag, secrecy, injury, insurance, introducer, clock_in, status, wages_ratio_date, remarks, del_flag, create_by, create_time, update_by, update_time, image_url,time_clock,subsidys, job_code from sys_staff
|
||||
select user_id,company_name, dept_id, code, name, id_card,is_leader, sex, age, phone, address, level, major, school, bank_number,social_subsidy, bank, employment_date, experience, worker_term, regular_date, quit_date, contract_start, contract_end, contract_type, social_type, seniority, is_overtime_pay, zs_flag, secrecy, injury, insurance, introducer, clock_in, status, wages_ratio_date, remarks, del_flag, create_by, create_time, update_by, update_time, image_url,time_clock,subsidys, job_code, openid from sys_staff
|
||||
</sql>
|
||||
|
||||
<select id="selectSysStaffList" parameterType="com.evo.system.domain.SysStaff" resultMap="SysStaffResult">
|
||||
@ -134,6 +134,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="timeClock != null">time_clock,</if>
|
||||
<if test="subsidys != null">subsidys,</if>
|
||||
<if test="jobCode != null">job_code,</if>
|
||||
<if test="openid != null">openid,</if>
|
||||
|
||||
</trim>
|
||||
<trim prefix="values (" suffix=")" suffixOverrides=",">
|
||||
<if test="companyName != null">#{companyName},</if>
|
||||
@ -181,6 +183,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="timeClock != null">#{timeClock},</if>
|
||||
<if test="subsidys != null">#{subsidys},</if>
|
||||
<if test="jobCode != null">#{jobCode},</if>
|
||||
<if test="openid != null">#{openid},</if>
|
||||
</trim>
|
||||
</insert>
|
||||
|
||||
@ -232,6 +235,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<if test="timeClock != null">time_clock = #{timeClock},</if>
|
||||
<if test="subsidys != null">subsidys = #{subsidys},</if>
|
||||
<if test="jobCode != null">job_Code=#{jobCode},</if>
|
||||
<if test="openid != null">openid=#{openid},</if>
|
||||
</trim>
|
||||
where user_id = #{userId}
|
||||
</update>
|
||||
|
||||
9
evo-admin/src/main/resources/templates/success.html
Normal file
9
evo-admin/src/main/resources/templates/success.html
Normal file
@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<title>Hello</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1 th:text="${message}"></h1>
|
||||
</body>
|
||||
</html>
|
||||
143
evo-admin/src/main/resources/templates/user_info.html
Normal file
143
evo-admin/src/main/resources/templates/user_info.html
Normal file
@ -0,0 +1,143 @@
|
||||
<!DOCTYPE html>
|
||||
<html xmlns:th="http://www.thymeleaf.org">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户信息提交</title>
|
||||
<!-- 引入Tailwind CSS -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- 引入Font Awesome -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet">
|
||||
|
||||
<!-- 自定义Tailwind配置 -->
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
primary: '#3b82f6',
|
||||
secondary: '#64748b',
|
||||
accent: '#10b981',
|
||||
},
|
||||
fontFamily: {
|
||||
sans: ['Inter', 'system-ui', 'sans-serif'],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
.form-input-focus {
|
||||
@apply focus:ring-2 focus:ring-primary/50 focus:border-primary focus:outline-none;
|
||||
}
|
||||
.form-label {
|
||||
@apply block text-sm font-medium text-gray-700 mb-1;
|
||||
}
|
||||
.form-input {
|
||||
@apply w-full px-4 py-2 border border-gray-300 rounded-lg transition duration-200 form-input-focus;
|
||||
}
|
||||
.error-message {
|
||||
@apply text-red-500 text-sm mt-1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-50 min-h-screen flex items-center justify-center p-4">
|
||||
<div class="w-full max-w-md bg-white rounded-xl shadow-lg overflow-hidden">
|
||||
<div class="p-6 sm:p-8">
|
||||
<div class="text-center mb-6">
|
||||
<h2 class="text-2xl font-bold text-gray-900">用户信息信息完善</h2>
|
||||
<p class="mt-2 text-sm text-gray-500">请填写以下信息并提交</p>
|
||||
</div>
|
||||
|
||||
<!-- 表单使用Thymeleaf绑定,提交到/submit-user路径 -->
|
||||
<form th:action="@{/wechat/submit_user}" th:object="${user}" method="post" class="space-y-5">
|
||||
<!-- 用户姓名字段 -->
|
||||
<div>
|
||||
<label for="name" class="form-label">
|
||||
<i class="fa fa-user-circle mr-1"></i>用户姓名
|
||||
</label>
|
||||
<input type="text" id="name" th:field="*{name}"
|
||||
class="form-input"
|
||||
placeholder="请输入您的姓名"
|
||||
required>
|
||||
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="error-message"></p>
|
||||
</div>
|
||||
|
||||
<!-- 身份证号字段 -->
|
||||
<div>
|
||||
<label for="idCard" class="form-label">
|
||||
<i class="fa fa-id-card mr-1"></i>身份证号
|
||||
</label>
|
||||
<input type="text" id="idCard" th:field="*{idCard}"
|
||||
class="form-input"
|
||||
placeholder="请输入18位身份证号"
|
||||
maxlength="18"
|
||||
required>
|
||||
<p th:if="${#fields.hasErrors('idCard')}" th:errors="*{idCard}" class="error-message"></p>
|
||||
</div>
|
||||
|
||||
<!-- 手机号字段 -->
|
||||
<div>
|
||||
<label for="phone" class="form-label">
|
||||
<i class="fa fa-phone mr-1"></i>手机号
|
||||
</label>
|
||||
<input type="tel" id="phone" th:field="*{phone}"
|
||||
class="form-input"
|
||||
placeholder="请输入11位手机号"
|
||||
maxlength="11"
|
||||
required>
|
||||
<p th:if="${#fields.hasErrors('phone')}" th:errors="*{phone}" class="error-message"></p>
|
||||
</div>
|
||||
<!-- 隐藏文本输入框 -->
|
||||
<!-- <input type="tel" id="openid" th:field="*{openid}"-->
|
||||
<!-- th:value="${openid}"-->
|
||||
<!-- class="form-input"-->
|
||||
<!-- readonly>-->
|
||||
<input type="hidden" id="openid" th:field="*{openid}" th:value="${openid}">
|
||||
|
||||
<!-- 提交按钮 -->
|
||||
<div>
|
||||
<button type="submit" class="w-full bg-primary hover:bg-primary/90 text-white font-medium py-2.5 px-4 rounded-lg transition duration-200 flex items-center justify-center">
|
||||
<i class="fa fa-paper-plane mr-2"></i>提交信息
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- 提交成功消息(如果有) -->
|
||||
<div th:if="${successMessage}" class="mt-5 p-3 bg-green-50 border border-green-200 rounded-lg text-green-700 text-sm">
|
||||
<i class="fa fa-check-circle mr-1"></i>
|
||||
<span th:text="${successMessage}"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 简单的表单验证脚本 -->
|
||||
<script>
|
||||
// 表单提交前的基本验证
|
||||
document.querySelector('form').addEventListener('submit', function(e) {
|
||||
const idCard = document.getElementById('idCard').value;
|
||||
const phone = document.getElementById('phone').value;
|
||||
let isValid = true;
|
||||
|
||||
// 身份证号简单验证(18位)
|
||||
if (idCard.length !== 18) {
|
||||
alert('请输入18位身份证号');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// 手机号简单验证(11位数字)
|
||||
if (!/^\d{11}$/.test(phone)) {
|
||||
alert('请输入有效的11位手机号');
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
if (!isValid) {
|
||||
e.preventDefault(); // 阻止表单提交
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue
Block a user