六厂财务系统离线功能完善

This commit is contained in:
hejiayang
2025-11-20 18:30:45 +08:00
parent ac6bfdf437
commit 02f96eecc3
12 changed files with 345 additions and 30 deletions
@@ -0,0 +1,58 @@
package org.jeecg.emp.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class StUserBalanceVo {
/**
* 用户id
*/
private String id;
/**
* 姓名
*/
private String realname;
/**
* 卡号
*/
private String cardCode;
/**
* 卡状态
*/
private String cardCodeStatus;
/**
* 卡状态
*/
private String cardCodeStatusName;
/**
* 电话
*/
private String phone;
/**
* 工号
*/
private String workNo;
/**
* 身份证号
*/
private String cardNo;
/**
* 部门id
*/
private String departIds;
/**
* 用户类型 0正式 1非正式 2家庭成员
*/
private String userType;
/**
* 食堂id
*/
private String canteenId;
/**
* 账户余额
*/
private BigDecimal balance;
}
@@ -9,8 +9,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.Data;
import org.jeecg.common.system.base.entity.JeecgEntity;
import org.jeecg.restaurant.entity.SlBaseDinnerTime;
import java.io.Serializable;
import java.util.List;
/**
* 扣费规则
@@ -66,4 +68,6 @@ public class StClientMemberLevelDeductRuleVo extends JeecgEntity implements Seri
@Schema(title = "是否支持透支消费(0:不支持,1:支持)")
private Integer isOverdraftDine;
private List<SlBaseDinnerTime> dinnerTimes;
}
@@ -34,4 +34,13 @@ public class StRfidVo {
//本次扣费金额(后端根据规则计算)
private BigDecimal spendMoney;
/**
* 二次扣费标记 1 是; 0 否
*/
private Integer secondChargeFlag;
/**
* 自定义扣费标记 1 是 ; 0 否
*/
private Integer isCustom;
}
@@ -249,4 +249,85 @@ public class CqBaseEmployeePayController extends JeecgController<CqBaseEmployee,
return Result.OK("删除成功!");
}
@AutoLog(value = " 非正式+正式员工绑卡")
@Operation(summary = " 非正式+正式员工绑卡", description = " 非正式+正式员工绑卡")
@PostMapping(value = "/editBuildCardCode")
public Result<?> editBuildCardCode(@RequestBody SysUserSub sysUserSub) {
if(StringUtils.isBlank(sysUserSub.getId())){
return Result.error("用户ID不能为空");
}
if(StringUtils.isBlank(sysUserSub.getCardCode())){
return Result.error("卡号不能为空");
}
SysUserSub sysUserSub1 = sysUserSubService.getById(sysUserSub.getId());
if(null == sysUserSub1){
return Result.error("用户不存在");
}
//家庭用户
if(sysUserSub1.getUserType() == 2){
return Result.error("家庭用户不支持操作");
}
sysUserSub.setCardCodeStatus(1);
Result<?> result = cqBaseEmployeeService.updateEmpSub(sysUserSub);
return result;
}
/**
* 非正式+正式员工解绑卡
*
* @return
*/
@AutoLog(value = " 非正式+正式员工解绑卡")
@Operation(summary = " 非正式+正式员工解绑卡", description = " 非正式+正式员工解绑卡")
@PostMapping(value = "/editUnBuildCardCode")
public Result<?> editUnBuildCardCode(@RequestBody SysUserSub sysUserSub) {
if(StringUtils.isBlank(sysUserSub.getId())){
return Result.error("用户ID不能为空");
}
if(StringUtils.isBlank(sysUserSub.getCardCode())){
return Result.error("卡号不能为空");
}
SysUserSub sysUserSub1 = sysUserSubService.getById(sysUserSub.getId());
if(null == sysUserSub1){
return Result.error("用户不存在");
}
//家庭用户
if(sysUserSub1.getUserType() == 2){
return Result.error("家庭用户不支持操作");
}
sysUserSub.setCardCodeStatus(3);
sysUserSub.setCardCode("");
Result<?> result = cqBaseEmployeeService.updateEmpSub(sysUserSub);
return result;
}
/**
* 非正式+正式员工挂失
*
* @return
*/
@AutoLog(value = " 非正式+正式员工挂失")
@Operation(summary = " 非正式+正式员工挂失", description = " 非正式+正式员工挂失")
@PostMapping(value = "/editBuildCardCodeLoss")
public Result<?> editBuildCardCodeLoss(@RequestBody SysUserSub sysUserSub) {
if(StringUtils.isBlank(sysUserSub.getId())){
return Result.error("用户ID不能为空");
}
if(StringUtils.isBlank(sysUserSub.getCardCode())){
return Result.error("卡号不能为空");
}
SysUserSub sysUserSub1 = sysUserSubService.getById(sysUserSub.getId());
if(null == sysUserSub1){
return Result.error("用户不存在");
}
//家庭用户
if(sysUserSub1.getUserType() == 2){
return Result.error("家庭用户不支持操作");
}
sysUserSub.setCardCodeStatus(2);
Result<?> result = cqBaseEmployeeService.updateEmpSub(sysUserSub);
return result;
}
}
@@ -1,15 +1,13 @@
package org.jeecg.emp.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.jeecg.emp.entity.CqBaseEmployee;
import org.jeecg.emp.vo.CqBaseEmpVo;
import org.jeecg.emp.vo.DepartVo;
import org.jeecg.emp.vo.EmpInfoVo;
import org.jeecg.emp.vo.UserInfoVo;
import org.jeecg.emp.vo.*;
import org.jeecg.user.entity.SysUser;
import java.util.List;
@@ -109,4 +107,8 @@ public interface CqBaseEmployeeMapper extends BaseMapper<CqBaseEmployee> {
EmpInfoVo getEmpInfoVoByUserId(String userId);
IPage<StUserBalanceVo> getUserBalanceList(@Param("page") Page<StUserBalanceVo> page, @Param("timestamp") Long timestamp);
List<StUserBalanceVo> getUserBalanceList(@Param("timestamp") Long timestamp);
}
@@ -520,4 +520,25 @@
AND u.del_flag = 0
limit 1
</select>
<select id="getUserBalanceList" resultType="org.jeecg.emp.vo.StUserBalanceVo">
SELECT
us.id,
us.realname,
us.card_code,
us.card_code_status,
IF(us.card_code_status = 1,'正常',IF(us.card_code_status = 2, '挂失','未绑定/已解绑')) card_code_status_name,
us.phone,
us.work_no,
us.card_no,
us.depart_ids,
us.user_type,
us.canteen_id,
IFNULL(( mb.top_up_balance + mb.reward_balance ),0) balance
FROM
sys_user_sub us
LEFT JOIN st_client_member_balance mb ON us.id = mb.member_id
<if test="timestamp != null and timestamp !='' ">
AND (mb.create_time > FROM_UNIXTIME(#{timestamp} / 1000) or mb.last_consumption_time > FROM_UNIXTIME(#{timestamp} / 1000))
</if>
</select>
</mapper>
@@ -1,11 +1,13 @@
package org.jeecg.emp.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.common.api.vo.Result;
import org.jeecg.emp.entity.CqBaseEmployee;
import org.jeecg.emp.vo.CqBaseEmpVo;
import org.jeecg.emp.vo.DepartVo;
import org.jeecg.emp.vo.StUserBalanceVo;
import org.jeecg.user.entity.SysUser;
import org.jeecg.user.entity.SysUserSub;
import org.jeecg.user.vo.SysUserSubVo;
@@ -40,4 +42,7 @@ public interface ICqBaseEmployeeService extends IService<CqBaseEmployee> {
public List<DepartVo> getSecondDepartList();
List<SysUser> getUserList(List<String> idCardList);
IPage<StUserBalanceVo> getUserBalanceList(Integer pageNo, Integer pageSize, Long timestamp);
List<StUserBalanceVo> getUserBalanceList(Long timestamp);
}
@@ -1,6 +1,7 @@
package org.jeecg.emp.service.impl;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
@@ -13,6 +14,7 @@ import org.jeecg.emp.mapper.CqBaseEmployeeMapper;
import org.jeecg.emp.service.ICqBaseEmployeeService;
import org.jeecg.emp.vo.CqBaseEmpVo;
import org.jeecg.emp.vo.DepartVo;
import org.jeecg.emp.vo.StUserBalanceVo;
import org.jeecg.payment.entity.StClientMemberBalance;
import org.jeecg.payment.service.IStClientMemberBalanceService;
import org.jeecg.system.service.ISysPayDepartService;
@@ -208,4 +210,16 @@ public class CqBaseEmployeeServiceImpl extends ServiceImpl<CqBaseEmployeeMapper,
public List<SysUser> getUserList(List<String> idCardList) {
return baseMapper.getUserList(idCardList);
}
//分页获取用户余额信息
@Override
public IPage<StUserBalanceVo> getUserBalanceList(Integer pageNo, Integer pageSize, Long timestamp){
Page<StUserBalanceVo> page = new Page<>(pageNo,pageSize);
return baseMapper.getUserBalanceList(page, timestamp);
}
@Override
public List<StUserBalanceVo> getUserBalanceList(Long timestamp){
return baseMapper.getUserBalanceList(timestamp);
}
}
@@ -3,6 +3,7 @@ package org.jeecg.pay.controller;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
@@ -12,6 +13,8 @@ import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.util.RedisUtil;
import org.jeecg.emp.service.ICqBaseEmployeeService;
import org.jeecg.emp.vo.StUserBalanceVo;
import org.jeecg.equipment.entity.StEquipment;
import org.jeecg.equipment.service.IStEquipmentService;
import org.jeecg.payment.entity.StBbzxOrderInfo;
@@ -39,7 +42,7 @@ import java.util.concurrent.CompletableFuture;
*/
@Tag(name = "0.食堂API接口支付相关 ")
@RestController
@RequestMapping("/zhstapi/pay/")
@RequestMapping("/zhstapi/pay")
@Slf4j
public class StPayController {
// @Autowired
@@ -59,6 +62,8 @@ public class StPayController {
@Resource
private ISlBaseDinnerTimeService slBaseDinnerTimeService;
@Resource
private ICqBaseEmployeeService cqBaseEmployeeService;
/**
* 根据设备号获取token
@@ -75,20 +80,6 @@ public class StPayController {
if(CollectionUtils.isEmpty(stEquipmentList)){
log.error("根据设备号获取token,异常:设备编号不存在,qrcodeId="+qrcodeId);
return Result.error("设备编号不存在");
} else {
// try {
// //查询设备编码
// StEquipment stEquipment = stEquipmentList.get(0);
// String canteenId = slBaseRestaurantService.getCanteenId(stEquipment.getCanteenId());
// SlBaseDinnerTime slBaseDinnerTime = stBaseService.getCanteenDinnerType(canteenId);
// if(null != slBaseDinnerTime && StringUtils.isNotBlank(slBaseDinnerTime.getDinnerType())){
// String timesName = slBaseDinnerTime.getDinnerType();
// //异步上传设备统计信息
// iStRfidDeviceLogService.syncUploadDeviceInfo(qrcodeId,canteenId,timesName,null,slBaseDinnerTime,null);
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
String token = JwtUtil.signUserId("0000000000000000001", qrcodeId, qrcodeId,"4");
@@ -99,7 +90,7 @@ public class StPayController {
@Operation(summary = "", description = "提供根据食堂ID获取食堂配置信息")
@GetMapping(value = "/getDinnerType/byCanteenId")
@AutoLog(value = "提供根据食堂ID获取食堂配置信息")
public Result<?> getCanteenInfoByCanteenId(@RequestParam(name = "restId", required = true) String restId) {
public Result<StClientMemberLevelDeductRuleVo> getCanteenInfoByCanteenId(@RequestParam(name = "restId", required = true) String restId) {
SlBaseRestaurant slBaseRestaurant = slBaseRestaurantService.getById(restId);
if(null == slBaseRestaurant){
return Result.error("食堂未找到,请检查参数");
@@ -109,11 +100,25 @@ public class StPayController {
if(null == levelDeductRuleVo || null == levelDeductRuleVo.getDeductionType()){
return Result.error("食堂未设置扣费规则");
}
List<SlBaseDinnerTime> list = slBaseDinnerTimeService.list(new LambdaQueryWrapper<SlBaseDinnerTime>().eq(SlBaseDinnerTime::getRestId, restId));
levelDeductRuleVo.setDinnerTimes(list);
levelDeductRuleVo.setIsOverdraftDine(slBaseRestaurant.getIsOverdraftDine());
levelDeductRuleVo.setIsExternalDineFlag(slBaseRestaurant.getIsExternalDine());
return Result.OK(levelDeductRuleVo);
}
@Operation(summary = "获取用户类型,余额信息", description = "获取用户类型,余额信息")
@GetMapping(value = "/getUserBalanceList")
@AutoLog(value = "获取用户类型,余额信息")
public Result<?> getUserBalanceList(@RequestParam(name = "pageNo", required = false) Integer pageNo,
@RequestParam(name = "pageSize", required = false) Integer pageSize,
@RequestParam(name = "timestamp", required = false) Long timestamp){
if(null == pageNo || pageSize == 0){
return Result.OK(cqBaseEmployeeService.getUserBalanceList(timestamp));
}else {
return Result.OK(cqBaseEmployeeService.getUserBalanceList(pageNo, pageSize, timestamp));
}
}
@@ -247,7 +252,8 @@ public class StPayController {
@PostMapping(value = "/payUser/offline/v3")
@AutoLog(value = "离线人脸扣费v3")
public Result<StClientMemberSpendVO> bindUserOfflineV3(@RequestBody StRfidoffLineVo stRfidoffLineVo) {
return iStClientMemberLevelDeductRuleService.bindUserOfflineCardCode(stRfidoffLineVo);
return iStClientMemberLevelDeductRuleService.
bindUserOfflineCardCode(stRfidoffLineVo);
}
@@ -298,6 +298,73 @@ public class StClientMemberLevelDeductRuleServiceImpl extends JeecgServiceImpl<S
return new ArrayList<>(uniqueMap.values());
}
//用户一天 一餐 不是二次扣费的数据 , 只留一条
public List<StRfidVo> processUserMealDataSimplified(List<StRfidVo> dataList) {
if (dataList == null || dataList.isEmpty()) {
return new ArrayList<>();
}
String restId = dataList.get(0).getRestId();
List<SlBaseDinnerTime> dinnerTimeList = stBaseDinnerTimeService.list(new LambdaQueryWrapper<SlBaseDinnerTime>()
.eq(SlBaseDinnerTime::getRestId, restId)
.orderByAsc(SlBaseDinnerTime::getEndTime));
// 按用户ID、日期、餐次分组,然后处理每组数据
return dataList.stream()
.collect(Collectors.groupingBy(vo ->
vo.getUserId() + "_" + DateUtils.formatDate(new Date(Long.parseLong(vo.getDateTime())), "yyyy-MM-dd") + "_" + getDinnerType(dinnerTimeList,vo.getDateTime())))
.values()
.stream()
.flatMap(group -> {
// 分离二次扣费和非二次扣费数据
List<StRfidVo> secondCharge = group.stream()
.filter(vo -> vo.getSecondChargeFlag() != null && vo.getSecondChargeFlag() == 1)
.collect(Collectors.toList());
List<StRfidVo> nonSecondCharge = group.stream()
.filter(vo -> vo.getSecondChargeFlag() == null || vo.getSecondChargeFlag() == 0)
.collect(Collectors.toList());
// 非二次扣费数据只保留一条
List<StRfidVo> resultGroup = new ArrayList<>();
if (!nonSecondCharge.isEmpty()) {
StRfidVo stRfidVo = nonSecondCharge.stream()
.min((vo1, vo2) -> compareByDateTime(vo1.getDateTime(), vo2.getDateTime()))
.orElse(nonSecondCharge.get(0));
resultGroup.add(stRfidVo);
}
resultGroup.addAll(secondCharge);
return resultGroup.stream();
})
.collect(Collectors.toList());
}
/**
* 比较两个时间戳
*/
private int compareByDateTime(String dateTime1, String dateTime2) {
try {
long time1 = Long.parseLong(dateTime1);
long time2 = Long.parseLong(dateTime2);
return Long.compare(time1, time2);
} catch (Exception e) {
return 0;
}
}
/**
* 获取餐次类型
*/
private String getDinnerType(List<SlBaseDinnerTime> dinnerTimeList,String dateTime) {
try {
SlBaseDinnerTime dinnerTime = stBaseDinnerTimeService.getCanteenDinnerTypeByDateTime(dinnerTimeList, dateTime);
return dinnerTime != null ? dinnerTime.getDinnerType() : "UNKNOWN";
} catch (Exception e) {
return "UNKNOWN";
}
}
/**
* 食堂扣费主接口
*/
@@ -309,8 +376,10 @@ public class StClientMemberLevelDeductRuleServiceImpl extends JeecgServiceImpl<S
if(CollectionUtils.isEmpty(voListAll)){
return Result.error("扣费信息不能为空");
}
//去重
voListAll = filterDuplicateTimestamp(voListAll);
// //去重
// voListAll = filterDuplicateTimestamp(voListAll);
//数据预处理: 一人 一餐 除二次扣费外 只有一条扣费数据
voListAll = processUserMealDataSimplified(voListAll);
Map<String, List<StRfidVo>> userListMaps = voListAll.stream().collect(Collectors.groupingBy(StRfidVo::getUserId));
for (Map.Entry<String, List<StRfidVo>> userListMap : userListMaps.entrySet()) {
@@ -322,18 +391,18 @@ public class StClientMemberLevelDeductRuleServiceImpl extends JeecgServiceImpl<S
return null;
}
try {
//根据用户获取之后一次扣费时间
Date maxTime = baseMapper.getMaxTimeByUserId(userId);
// //根据用户获取之后一次扣费时间
// Date maxTime = baseMapper.getMaxTimeByUserId(userId);
if(CollectionUtils.isEmpty(value)){
return Result.error("扣费信息不能为空");
}
//扣费规则(扣费类型(1:固定扣费,2:固定+补贴扣费,3:按量扣费,4:自定义金额))
StClientMemberLevelDeductRuleVo levelDeductRuleVo = this.getDeductRuleByCanteenId(restId);
value = filterRecentThanOneMinute(value,maxTime.getTime());
// //过滤时间小于1分钟的记录
// value = filterRecentThanOneMinute(value,maxTime.getTime());
if(value.size() > 1){//离线扣费
//加异步处理
List<StRfidVo> finalVoListAll = value;
//过滤时间小于1分钟的记录
CompletableFuture.runAsync(() -> {
unifiedDeductionInterface(finalVoListAll,restId,deviceCode,levelDeductRuleVo,false);
});
@@ -378,6 +447,20 @@ public class StClientMemberLevelDeductRuleServiceImpl extends JeecgServiceImpl<S
List<StRfidVo> voList = entry.getValue();
for (StRfidVo stRfidVo : voList) { // 改用普通for循环,方便控制流程
//判断用户本餐是否已经有数据
if(stRfidVo.getSecondChargeFlag() == null || stRfidVo.getSecondChargeFlag() == 0){
long count = iStBbzxOrderInfoService.count(new LambdaQueryWrapper<StBbzxOrderInfo>()
.eq(StBbzxOrderInfo::getMemberId, stRfidVo.getUserId())
.eq(StBbzxOrderInfo::getDinnerType, entry.getKey())
.eq(StBbzxOrderInfo::getCanteenId, restId)
.eq(StBbzxOrderInfo::getStatus, 5)
.eq(StBbzxOrderInfo::getEatDay, DateUtils.formatDate(new Date(Long.parseLong(stRfidVo.getDateTime())), "yyyy-MM-dd"))
);
if(count > 0){
errorMessages = "用户本餐已扣费";
continue;
}
}
stRfidVo.setDeviceCode(deviceCode);
stRfidVo.setRestId(restId);
if(!online){
@@ -409,12 +492,12 @@ public class StClientMemberLevelDeductRuleServiceImpl extends JeecgServiceImpl<S
// 预算本次扣费金额
BigDecimal spendMoneyBudgeted = BigDecimal.ZERO;
BigDecimal moneyCustom = stRfidVo.getSpendMoney();
Integer deductionType;
if (moneyCustom != null && moneyCustom.compareTo(BigDecimal.ZERO) > 0) {
Integer isCustom = stRfidVo.getIsCustom();
if (isCustom != null && isCustom == 1) {
// 自定义金额方式
spendMoneyBudgeted = moneyCustom;
spendMoneyBudgeted = stRfidVo.getSpendMoney();
deductionType = 4;
} else {
deductionType = 1;
@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import org.jeecg.restaurant.entity.SlBaseDinnerTime;
import java.util.Date;
import java.util.List;
/**
* @Description: 食堂餐次定义
@@ -16,4 +17,12 @@ public interface ISlBaseDinnerTimeService extends IService<SlBaseDinnerTime> {
SlBaseDinnerTime getCanteenDinnerType(String canteenId, Date dataTime);
SlBaseDinnerTime getCanteenDinnerType(String canteenId);
/**
* 根据时间戳获取对应餐次
* @param list 餐次列表(食堂对应已排序处理好的)
* @param dataTime 时间戳
* @return 对应餐次
*/
SlBaseDinnerTime getCanteenDinnerTypeByDateTime(List<SlBaseDinnerTime> list, String dataTime);
}
@@ -5,11 +5,14 @@ import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.util.DateUtils;
import org.jeecg.restaurant.entity.SlBaseDinnerTime;
import org.jeecg.restaurant.mapper.SlBaseDinnerTimeMapper;
import org.jeecg.restaurant.service.ISlBaseDinnerTimeService;
import org.springframework.stereotype.Service;
import java.time.*;
import java.util.Date;
import java.util.List;
@@ -61,4 +64,24 @@ public class SlBaseDinnerTimeServiceImpl extends ServiceImpl<SlBaseDinnerTimeMap
}
return null;
}
@Override
public SlBaseDinnerTime getCanteenDinnerTypeByDateTime(List<SlBaseDinnerTime> list, String dataTime) {
for (SlBaseDinnerTime slBaseDinnerTime : list) {
DateTime endTime = DateUtil.parse(DateUtil.today() + " " + slBaseDinnerTime.getEndTime());
Date date = new Date();
if(StringUtils.isNotBlank(dataTime)){
long timestamp = Long.parseLong(dataTime);
date = new Date(timestamp);
endTime = DateUtil.parse(DateUtils.formatDate(date,"yyyy-MM-dd") + " " + slBaseDinnerTime.getEndTime());
}
if (DateUtil.compare(date, endTime) <= 0) {
return slBaseDinnerTime;
}
}
return list.get(list.size() - 1);
}
}