3 Commits

Author SHA1 Message Date
xiang
0d2ae54678 perf:下单优化 2026-01-20 22:17:15 +08:00
xiang
8514eac031 feat:场地信息拉取 2026-01-20 21:49:46 +08:00
xiang
1a7569d1a1 feat:查询封禁状态 2026-01-20 21:33:12 +08:00
17 changed files with 397 additions and 45 deletions

View File

@@ -15,6 +15,9 @@ public class UrlConstant {
* 查询当天的场地信息
*/
public final static String QUERY_TODAY_SUBSCRIBE_URL = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/getSubscribeByToday";
/**
* 查询明天场地信息
*/
public final static String QUERY_TOMORROW_SUBSCRIBE_URL = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/getSubscribeByTomorrow";
/**
@@ -32,4 +35,13 @@ public class UrlConstant {
*/
public final static String HEALTH_DECLARATION = GNTYZX_BASE_URL + "/GYM-JN//busi/healthDeclaration/addUserPrivacy";
/**
* 校验会员卡状态
*/
public final static String CHECK_NUM = GNTYZX_BASE_URL + "/GYM-JN/multi/Subscribe/checkDefaultsNum";
/**
* 根据openId查询会员卡信息
*/
public final static String QUERY_BY_OPEN_ID = GNTYZX_BASE_URL + "/GYM-JN/multi/xfConsumer/queryByOpenId";
}

View File

@@ -0,0 +1,29 @@
package com.xiang.app.modules.jntyzx.entity.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("jntyzx_user_restriction")
public class UserRestrictionInfo {
private Long id;
/**
* 用户id
*/
private Long userId;
/**
* 封禁截止时间
*/
private LocalDateTime restrictionDeadline;
/**
* 封禁原因
*/
private String restrictionDesc;
}

View File

@@ -1,10 +1,13 @@
package com.xiang.app.modules.jntyzx.entity.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @Author: xiang
* @Date: 2025-12-16 09:18
@@ -34,4 +37,21 @@ public class UserTokenInfoDO {
* 状态(0:禁用 1:启用)
*/
private Integer status;
/**
* 是否可以下单 0否 1
*/
private Integer isOrder;
/**
* 会员卡号
*/
@TableField("member_card_no")
private String memberCardNo;
/**
* 是否封禁 0否 1:是
*/
@TableField("is_restriction")
private Integer isRestriction;
}

View File

@@ -0,0 +1,106 @@
package com.xiang.app.modules.jntyzx.entity.resp.query;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserInfoResponse {
/**
* id
*/
private Long id;
/**
* 会员卡号
*/
private String consCard;
/**
* 姓名
*/
private String consName;
/**
* 性别
*/
private String consSex;
/**
* 身份证号
*/
private String consIdCard;
/**
* 固定电话
*/
private String consTel;
/**
* 手机号码
*/
private String consHandSet;
/**
* 单位
*/
private String consUnit;
/**
* 照片
*/
private String consPhoto;
private Integer consWaste;
/**
* 会员卡号
*/
private String consNumber;
private BigDecimal consMin;
private Integer consProp;
/**
* 注册年
*/
private String consYear;
/**
* 注册月
*/
private String consMonth;
/**
* 注册日
*/
private String consDay;
private boolean consIflag;
/**
* 注册时间
*/
private LocalDateTime consTimes;
/**
* openId
*/
private String openId;
/**
* 头像
*/
private String photoUrl;
/**
* 会员
*/
private Integer consVip;
/**
* 会员等级号
*/
private String consVipCode;
private String eleCardNum;
private Integer appointmentEligibility;
/**
* 封禁截止日期
*/
private String restrictionDeadline;
/**
* 封禁原因
*/
private String restrictionDescription;
/**
* 封禁截止日期
*/
private String RestrictionDeadline;
}

View File

@@ -0,0 +1,9 @@
package com.xiang.app.modules.jntyzx.manage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.xiang.app.modules.jntyzx.entity.pojo.UserRestrictionInfo;
public interface IUserRestrictionManage extends IService<UserRestrictionInfo> {
UserRestrictionInfo queryByUserId(Long userId);
}

View File

@@ -12,4 +12,7 @@ import java.util.List;
public interface IUserTokenInfoManage extends IService<UserTokenInfoDO> {
List<UserTokenInfoDO> listUser();
UserTokenInfoDO getByName(String name);
List<UserTokenInfoDO> listCanOrder();
}

View File

@@ -0,0 +1,18 @@
package com.xiang.app.modules.jntyzx.manage;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xiang.app.modules.jntyzx.entity.pojo.UserRestrictionInfo;
import com.xiang.app.modules.jntyzx.mapper.JntyzxUserRestrictionInfoMapper;
import org.springframework.stereotype.Service;
@Service
public class UserRestrictionManageImpl extends ServiceImpl<JntyzxUserRestrictionInfoMapper, UserRestrictionInfo> implements IUserRestrictionManage {
@Override
public UserRestrictionInfo queryByUserId(Long userId) {
LambdaQueryWrapper<UserRestrictionInfo> lambdaQueryWrapper = Wrappers.lambdaQuery();
lambdaQueryWrapper.eq(UserRestrictionInfo::getUserId, userId);
return baseMapper.selectOne(lambdaQueryWrapper);
}
}

View File

@@ -29,4 +29,13 @@ public class UserTokenInfoManageImpl extends ServiceImpl<JntyzxUserTokenInfoMapp
lambdaQueryWrapper.eq(UserTokenInfoDO::getName, name);
return baseMapper.selectOne(lambdaQueryWrapper);
}
@Override
public List<UserTokenInfoDO> listCanOrder() {
LambdaQueryWrapper<UserTokenInfoDO> lambdaQueryWrapper = Wrappers.lambdaQuery();
lambdaQueryWrapper.eq(UserTokenInfoDO::getStatus, 1);
lambdaQueryWrapper.eq(UserTokenInfoDO::getIsOrder, 1);
lambdaQueryWrapper.eq(UserTokenInfoDO::getIsRestriction, 0);
return baseMapper.selectList(lambdaQueryWrapper);
}
}

View File

@@ -0,0 +1,11 @@
package com.xiang.app.modules.jntyzx.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xiang.app.modules.jntyzx.entity.pojo.UserRestrictionInfo;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@Mapper
@Repository
public interface JntyzxUserRestrictionInfoMapper extends BaseMapper<UserRestrictionInfo> {
}

View File

@@ -5,6 +5,7 @@ import com.xiang.app.modules.jntyzx.entity.pojo.VenueInfoDO;
import com.xiang.app.modules.jntyzx.entity.resp.JntyzxResponse;
import com.xiang.app.modules.jntyzx.entity.resp.OrderCreateResp;
import com.xiang.app.modules.jntyzx.entity.resp.query.SitePositionList;
import com.xiang.app.modules.jntyzx.entity.resp.query.UserInfoResponse;
import java.util.List;
@@ -40,4 +41,20 @@ public interface IJntyzxHttpService {
* @return
*/
JntyzxResponse healthDeclaration(String token, String openId);
/**
* 根据openid查询
* @param token token
* @param openId openId
* @return
*/
JntyzxResponse<UserInfoResponse> queryByOpenId(String token, String openId);
/**
* 校验会员卡状态
* @param token token
* @param cardNo 会员卡号
* @return
*/
JntyzxResponse checkDefaultNums(String token, String cardNo);
}

View File

@@ -11,6 +11,7 @@ import java.util.List;
public interface IUserTokenInfoService {
List<UserTokenInfoDO> getAvailableUser();
List<UserTokenInfoDO> getCanOrderUser();
String getToken(String name);
boolean flushSingleToken(String name);
boolean flushToken();

View File

@@ -14,6 +14,7 @@ import com.xiang.app.modules.jntyzx.entity.req.SubscribeVo;
import com.xiang.app.modules.jntyzx.entity.resp.JntyzxResponse;
import com.xiang.app.modules.jntyzx.entity.resp.OrderCreateResp;
import com.xiang.app.modules.jntyzx.entity.resp.query.SitePositionList;
import com.xiang.app.modules.jntyzx.entity.resp.query.UserInfoResponse;
import com.xiang.app.modules.jntyzx.entity.resp.query.VenueList;
import com.xiang.app.modules.jntyzx.manage.IOrderCreateInfoManage;
import com.xiang.app.modules.jntyzx.service.IJntyzxHttpService;
@@ -179,6 +180,37 @@ public class JntyzxHttpServiceImpl implements IJntyzxHttpService {
return JSON.parseObject(respStr, JntyzxResponse.class);
}
@Override
public JntyzxResponse<UserInfoResponse> queryByOpenId(String token, String openId) {
Map<String, String> params = Maps.newHashMap();
params.put("openId", openId);
Map<String, String> headers = Maps.newHashMap();
headers.put("X-Access-Token", token);
String resp = HttpHelper.doGet(UrlConstant.QUERY_BY_OPEN_ID, headers, params);
JntyzxResponse<UserInfoResponse> response = JSON.parseObject(resp, new TypeReference<JntyzxResponse<UserInfoResponse>>() {
});
if (Objects.isNull(response)) {
log.info("请求结果为空!");
return null;
}
return response;
}
@Override
public JntyzxResponse checkDefaultNums(String token, String cardNo) {
Map<String, String> params = Maps.newHashMap();
params.put("consNumber", cardNo);
Map<String, String> headers = Maps.newHashMap();
headers.put("X-Access-Token", token);
String resp = HttpHelper.doGet(UrlConstant.CHECK_NUM, headers, params);
if (StringUtils.isBlank(resp)) {
return null;
}
return JSON.parseObject(resp, JntyzxResponse.class);
}
private static JSONObject buildParamJsonObj(String openId) {
JSONObject jsonObject = new JSONObject();
jsonObject.put("id", "1702581215097257986");

View File

@@ -12,6 +12,7 @@ import com.xiang.app.modules.jntyzx.manage.IOrderCreateInfoManage;
import com.xiang.app.modules.jntyzx.service.IJntyzxHttpService;
import com.xiang.app.modules.jntyzx.service.IJtOrderService;
import com.xiang.xmc.service.cache.service.IRedisService;
import com.xiang.xservice.basic.exception.BusinessException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -74,10 +75,12 @@ public class OrderInfoServiceImpl implements IJtOrderService {
} else {
dingTalkFactory.sendMsg("用户" + userTokenInfoDO.getName() + "预订场地号:" + venueInfoDOS.get(0).getPlaceName() + "结果返回:" + JSON.toJSONString(orderResp));
if (orderResp.getMessage().contains("锁卡")) {
return true;
log.info("有锁卡风险,不在请求,用户:{}", userTokenInfoDO.getName());
throw new BusinessException("即将锁卡,不再请求");
}
if (orderResp.getMessage().contains("限制")) {
return true;
log.info("改会员卡被限制,不在请求,用户:{}", userTokenInfoDO.getName());
throw new BusinessException("会员卡被限制,不在请求");
}
return false;
}

View File

@@ -1,11 +1,15 @@
package com.xiang.app.modules.jntyzx.service.impl;
import com.xiang.app.common.service.dingtalk.JtDingTalkFactory;
import com.xiang.app.modules.jntyzx.entity.pojo.UserRestrictionInfo;
import com.xiang.app.modules.jntyzx.entity.pojo.UserTokenInfoDO;
import com.xiang.app.modules.jntyzx.entity.resp.JntyzxResponse;
import com.xiang.app.modules.jntyzx.entity.resp.query.UserInfoResponse;
import com.xiang.app.modules.jntyzx.manage.IUserRestrictionManage;
import com.xiang.app.modules.jntyzx.manage.IUserTokenInfoManage;
import com.xiang.app.modules.jntyzx.service.IJntyzxHttpService;
import com.xiang.app.modules.jntyzx.service.IUserTokenInfoService;
import com.xiang.xservice.basic.utils.DateUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
@@ -27,6 +31,7 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService {
private final IUserTokenInfoManage userTokenInfoManage;
private final IJntyzxHttpService jntyzxHttpService;
private final JtDingTalkFactory jtDingTalkFactory;
private final IUserRestrictionManage userRestrictionManage;
@Override
@@ -43,6 +48,11 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService {
return userTokenInfoDO.getToken();
}
@Override
public List<UserTokenInfoDO> getCanOrderUser() {
return userTokenInfoManage.listCanOrder();
}
@Override
public boolean flushSingleToken(String name) {
UserTokenInfoDO userTokenInfoDO = userTokenInfoManage.getByName(name);
@@ -62,6 +72,9 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService {
return true;
}
userTokenInfoDOS.parallelStream().forEach(this::healthDeclaration);
// 信息更新
userTokenInfoDOS = userTokenInfoManage.list();
userTokenInfoDOS.parallelStream().forEach(this::queryMemberCardInfo);
return true;
}
@@ -94,4 +107,42 @@ public class UserTokenInfoServiceImpl implements IUserTokenInfoService {
}
return flag;
}
/**
* 查询用户信息
*
* @param userTokenInfoDO 用户
*
* @return
*/
private void queryMemberCardInfo(UserTokenInfoDO userTokenInfoDO) {
JntyzxResponse<UserInfoResponse> response = jntyzxHttpService.queryByOpenId(userTokenInfoDO.getToken(), userTokenInfoDO.getOpenId());
if (Objects.isNull(response)) {
return;
}
if (response.getSuccess()) {
UserInfoResponse userInfoResponse = response.getResult();
userTokenInfoDO.setMemberCardNo(userInfoResponse.getConsCard());
if (StringUtils.isNotBlank(userInfoResponse.getRestrictionDeadline())) {
userTokenInfoDO.setIsRestriction(1);
userTokenInfoDO.setIsOrder(0);
UserRestrictionInfo userRestrictionInfo = userRestrictionManage.queryByUserId(userTokenInfoDO.getId());
if (Objects.isNull(userRestrictionInfo)) {
userRestrictionInfo = new UserRestrictionInfo();
userRestrictionInfo.setUserId(userTokenInfoDO.getId());
userRestrictionInfo.setRestrictionDeadline(DateUtils.getDateTimeFromStr(userInfoResponse.getRestrictionDeadline()));
userRestrictionInfo.setRestrictionDesc(userInfoResponse.getRestrictionDescription());
userRestrictionManage.save(userRestrictionInfo);
} else {
userRestrictionInfo.setRestrictionDeadline(DateUtils.getDateTimeFromStr(userInfoResponse.getRestrictionDeadline()));
userRestrictionInfo.setRestrictionDesc(userInfoResponse.getRestrictionDescription());
userRestrictionManage.updateById(userRestrictionInfo);
}
} else {
userTokenInfoDO.setIsRestriction(0);
userTokenInfoDO.setIsOrder(1);
}
userTokenInfoManage.updateById(userTokenInfoDO);
}
}
}

View File

@@ -1,5 +1,6 @@
package com.xiang.app.schedule.jntyzx;
import com.google.common.collect.Maps;
import com.xiang.app.common.service.dingtalk.JtDingTalkFactory;
import com.xiang.app.modules.jntyzx.entity.pojo.UserTokenInfoDO;
import com.xiang.app.modules.jntyzx.entity.resp.query.SitePositionList;
@@ -21,6 +22,7 @@ import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
@@ -88,6 +90,12 @@ public class JtVenuePullTask {
.filter(item -> !item.getPlaceName().contains("小馆"))
.toList();
}
return sitePositionLists;
Map<String, SitePositionList> mapByName = Maps.newLinkedHashMap();
for (SitePositionList sitePositionList : sitePositionLists) {
if (!mapByName.containsKey(sitePositionList.getPlaceName())) {
mapByName.put(sitePositionList.getPlaceName(), sitePositionList);
}
}
return mapByName.values().stream().toList();
}
}

View File

@@ -1,5 +1,6 @@
package com.xiang.app.schedule.jntyzx;
import com.xiang.app.common.service.dingtalk.JtDingTalkFactory;
import com.xiang.app.modules.jntyzx.entity.pojo.UserTokenInfoDO;
import com.xiang.app.modules.jntyzx.entity.pojo.VenueInfoDO;
import com.xiang.app.modules.jntyzx.service.IJtOrderService;
@@ -9,6 +10,8 @@ import com.xiang.app.modules.jntyzx.utils.VenueInfoUtils;
import com.xiang.core.quartz.annotation.XxzJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@@ -26,30 +29,42 @@ public class JtVenueSubscribeTask {
private final IUserTokenInfoService userTokenInfoService;
private final IJtOrderService jtOrderService;
private final IVenueService venueService;
private final JtDingTalkFactory jtDingTalkFactory;
@XxzJob(name = "jtVenueSubscribeTask")
@GetMapping("/jtVenueSubscribeTask")
public void handle() {
log.info("【Subscribe】 江体场地预定定时任务启动!!! time:{}", System.currentTimeMillis());
List<UserTokenInfoDO> users = userTokenInfoService.getAvailableUser();
List<UserTokenInfoDO> users = userTokenInfoService.getCanOrderUser();
if (CollectionUtils.isEmpty(users)) {
log.info("暂无可下单用户, time:{}", System.currentTimeMillis());
jtDingTalkFactory.sendMsg("暂无可下单用户, time:" + System.currentTimeMillis());
return;
}
List<VenueInfoDO> venueInfoDOS = venueService.queryTomorrowCanBuyVenue();
Map<String, List<VenueInfoDO>> venueInfoMap = venueInfoDOS.stream().filter(VenueInfoUtils::get8210VenueInfo).collect(Collectors.groupingByConcurrent(VenueInfoDO::getPlaceName));
if (MapUtils.isEmpty(venueInfoMap)) {
log.info("暂无可下单场地time:{}", System.currentTimeMillis());
return;
}
venueInfoMap.keySet().parallelStream().forEach(placeName -> {
List<VenueInfoDO> venueInfoDOList = venueInfoMap.get(placeName);
users.forEach(user -> {
for (int i = 0; i < 10; i++) {
boolean order = jtOrderService.createOrder(venueInfoDOList, user);
if (order) {
return;
users.parallelStream().forEach(user -> {
try {
for (String placeName : venueInfoMap.keySet()) {
List<VenueInfoDO> venueInfoDOList = venueInfoMap.get(placeName);
for (int i = 0; i < 10; i++) {
boolean order = jtOrderService.createOrder(venueInfoDOList, user);
if (order) {
return;
}
}
}
});
} catch (Exception e) {
// 关键点:异常只影响当前 user
log.error("createOrder 异常user={}", user.getId(), e);
return; // 结束这个 user不影响其他 user
}
});
}
}

View File

@@ -4,6 +4,7 @@ package com.xiang.app.schedule.jntyzx;
import com.google.common.collect.Maps;
import com.xiang.app.common.service.dingtalk.JtDingTalkFactory;
import com.xiang.app.modules.jntyzx.entity.pojo.UserTokenInfoDO;
import com.xiang.app.modules.jntyzx.entity.resp.JntyzxResponse;
import com.xiang.app.modules.jntyzx.entity.resp.query.SitePositionList;
import com.xiang.app.modules.jntyzx.service.IJntyzxHttpService;
import com.xiang.app.modules.jntyzx.service.IUserTokenInfoService;
@@ -44,37 +45,44 @@ public class JtVenueTomorrowPullTask {
log.info("当前无可用用户查询场地信息!");
return;
}
String token;
for (UserTokenInfoDO userTokenInfoDO : availableUser) {
if (Objects.isNull(userTokenInfoDO)) {
continue;
}
token = userTokenInfoDO.getToken();
if (StringUtils.isBlank(token)) {
continue;
}
List<SitePositionList> sitePositionLists = jntyzxHttpService.queryAvailableTomorrow(WeekendUtils.isWeekend(), token);
if (CollectionUtils.isEmpty(sitePositionLists)) {
continue;
}
venueService.saveTomorrowVenueInfo(sitePositionLists);
sitePositionLists = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList();
if (CollectionUtils.isEmpty(sitePositionLists)) {
return;
}
Map<String, SitePositionList> map = Maps.newLinkedHashMap();
for (SitePositionList sitePositionList : sitePositionLists) {
if (map.containsKey(sitePositionList.getPlaceName())) {
continue;
// 用户信息
StringBuffer userMsg = new StringBuffer();
availableUser.forEach(item -> {
JntyzxResponse jntyzxResponse = jntyzxHttpService.checkDefaultNums(item.getToken(), item.getMemberCardNo());
if (Objects.nonNull(jntyzxResponse)) {
if (jntyzxResponse.getSuccess()) {
userMsg.append("订购人:").append(item.getName()).append("正常下单\n");
} else {
userMsg.append("订购人:").append(item.getName()).append(jntyzxResponse.getMessage()).append("\n");
}
map.put(sitePositionList.getPlaceName(), sitePositionList);
}
StringBuffer msg = new StringBuffer("查询江体场地信息===>时间:\n" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + " 20:00-22:00\n");
map.forEach((placeName, sitePositionList) -> {
msg.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n");
});
jtDingTalkFactory.sendMsg(msg.toString());
});
jtDingTalkFactory.sendMsg(userMsg.toString());
// 场地信息
UserTokenInfoDO userTokenInfoDO = availableUser.get(0);
String token = userTokenInfoDO.getToken();
List<SitePositionList> sitePositionLists = jntyzxHttpService.queryAvailableTomorrow(WeekendUtils.isWeekend(), token);
if (CollectionUtils.isEmpty(sitePositionLists)) {
return;
}
venueService.saveTomorrowVenueInfo(sitePositionLists);
sitePositionLists = sitePositionLists.stream().filter(VenueInfoUtils::get8210VenueInfo).toList();
if (CollectionUtils.isEmpty(sitePositionLists)) {
return;
}
Map<String, SitePositionList> map = Maps.newLinkedHashMap();
for (SitePositionList sitePositionList : sitePositionLists) {
if (map.containsKey(sitePositionList.getPlaceName())) {
continue;
}
map.put(sitePositionList.getPlaceName(), sitePositionList);
}
StringBuffer msg = new StringBuffer("查询江体场地信息===>时间:\n" + DateUtils.getDateFromDate(LocalDate.now().plusDays(1)) + " 20:00-22:00\n");
map.forEach((placeName, sitePositionList) -> {
msg.append(placeName).append("订购人:").append(sitePositionList.getContacts()).append("\n");
});
jtDingTalkFactory.sendMsg(msg.toString());
}
}