Merge remote-tracking branch 'origin/1.0' into 1.0
| | |
| | | } |
| | | return R.ok(null); |
| | | } |
| | | /** |
| | | * 获取即将过期的优惠卷 |
| | | */ |
| | | @GetMapping("/api/v1/mini-program/coupon/findListExpirationTime") |
| | | @ApiOperation(value = "获取即将过期的优惠卷", tags = {"小程序-获取即将过期的优惠卷"}) |
| | | @XpClientCheck(checkToken = false, checkOtpToken = true) |
| | | public R<List<XpAppUserCoupon>> findListExpirationTime(HttpServletRequest request) { |
| | | String uid = request.getHeader("XP-Uid"); |
| | | List<XpAppUserCoupon> listExpirationTime = xpAppUserCouponService.findListExpirationTime(uid); |
| | | return R.ok(listExpirationTime); |
| | | } |
| | | |
| | | /** |
| | | * 获取优惠券列表total |
| | |
| | | } |
| | | |
| | | |
| | | @PostMapping("/api/v1/appUserCoupon/saveInfo") |
| | | @ApiOperation(value = "活动发放优惠券", tags = {"管理后台-活动发放优惠券"}) |
| | | public R<String> sendSave(@RequestBody UseUserCouponDto dto) { |
| | | xpAppUserCouponService.saveInfo(dto); |
| | | return R.ok(); |
| | | } |
| | | /** |
| | | * 发放优惠券 |
| | | */ |
| | |
| | | |
| | | @ApiModelProperty("优惠券id") |
| | | private Long couponId; |
| | | |
| | | @ApiModelProperty(value = "活动id") |
| | | private Long activityId; |
| | | } |
| | |
| | | package com.xiaopeng.account.model.vo; |
| | | |
| | | import com.xiaopeng.api.model.account.XpAppUserCoupon; |
| | | import com.xiaopeng.api.model.operation.XpCoupon; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | |
| | | private Integer total; |
| | | |
| | | private List<XpAppUserCouponListVo> list; |
| | | |
| | | @ApiModelProperty(value = "用户优惠券") |
| | | private XpAppUserCoupon userCoupon; |
| | | |
| | | @ApiModelProperty(value = "优惠券") |
| | | private XpCoupon xpCoupon; |
| | | |
| | | |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.account.schedule; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import com.xiaopeng.account.model.vo.XpAppUserCouponVo; |
| | | import com.xiaopeng.account.service.IXpAppUserCouponService; |
| | | import com.xiaopeng.api.feignClient.pushMessage.SubscribeMessageClient; |
| | | import com.xiaopeng.api.model.account.XpAppUserCoupon; |
| | | import com.xiaopeng.api.vo.pushMessage.SendSubscribeMessageVo; |
| | | import com.xiaopeng.common.core.enums.MessageTemplateEnum; |
| | | import com.xiaopeng.common.redis.service.RedisService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.redisson.api.RLock; |
| | | import org.redisson.api.RedissonClient; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | @Slf4j |
| | | @Component |
| | | public class AppUserCouponTask { |
| | | |
| | | @Resource |
| | | private IXpAppUserCouponService xpAppUserCouponService; |
| | | @Resource |
| | | private SubscribeMessageClient subscribeMessageClient; |
| | | |
| | | @Resource |
| | | private RedissonClient redissonClient; |
| | | |
| | | /** |
| | | * 优惠卷过期 前 24 小时 发送微信通知 |
| | | * 每5分钟执行一次 |
| | | */ |
| | | @Scheduled(fixedRate = 60*1000) |
| | | public void appUserCoupon_expired() throws InterruptedException { |
| | | List<XpAppUserCoupon> list = xpAppUserCouponService.findListExpirationTime(null); |
| | | |
| | | |
| | | |
| | | if(CollUtil.isNotEmpty( list)){ |
| | | |
| | | Map<Long, List<XpAppUserCoupon>> groupedByUserId = list.stream() |
| | | .collect(java.util.stream.Collectors.groupingBy(XpAppUserCoupon::getAppUserId)); |
| | | |
| | | for (Map.Entry<Long, List<XpAppUserCoupon>> entry : groupedByUserId.entrySet()) { |
| | | Long appUserId = entry.getKey(); |
| | | RLock lock = redissonClient.getLock("app_user_coupon_expired_" + appUserId ); |
| | | if(lock.tryLock(0, 10, TimeUnit.SECONDS)){ |
| | | try { |
| | | List<Long> ids = new ArrayList<>(); |
| | | BigDecimal money = BigDecimal.ZERO; |
| | | String couponName = ""; |
| | | for (XpAppUserCoupon xpAppUserCoupon : entry.getValue()) { |
| | | ids.add(xpAppUserCoupon.getId()); |
| | | if(money.compareTo(xpAppUserCoupon.getMoney())<0){ |
| | | money = xpAppUserCoupon.getMoney(); |
| | | couponName = xpAppUserCoupon.getCouponName(); |
| | | } |
| | | } |
| | | //发送微信通知 |
| | | SendSubscribeMessageVo msgVo = new SendSubscribeMessageVo(); |
| | | msgVo.setUserId(appUserId); |
| | | msgVo.setTemplateId(MessageTemplateEnum.C.getTemplateId()); |
| | | String json = MessageTemplateEnum.C.getRemarks(); |
| | | json = json.replace("优惠券名称", couponName); |
| | | json = json.replace("XXX", money.toPlainString() ); |
| | | msgVo.setJson(json); |
| | | subscribeMessageClient.sendSubscribeMessage(msgVo); |
| | | |
| | | xpAppUserCouponService.updateExpirationTime(ids); |
| | | |
| | | } catch (Exception e) { |
| | | log.error("优惠券过期发送微信通知失败", e); |
| | | } finally { |
| | | lock.unlock(); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | import com.xiaopeng.account.model.vo.XpAppUserCouponVo; |
| | | import com.xiaopeng.account.vo.UserCouponListVo; |
| | | import com.xiaopeng.api.dto.account.NavigationCouponPickerRequest; |
| | | import com.xiaopeng.api.dto.account.UseUserCouponDto; |
| | | import com.xiaopeng.api.model.account.XpAppUserCoupon; |
| | | import com.xiaopeng.api.query.account.XpAppUserCouponPageListQuery; |
| | | import com.xiaopeng.api.vo.account.NavigationCouponPickerVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | |
| | | public interface IXpAppUserCouponService extends IService<XpAppUserCoupon> { |
| | | |
| | | PageInfo<XpAppUserCouponListVo> pageList(XpAppUserCouponPageListQuery query); |
| | | |
| | | List<XpAppUserCoupon> findListExpirationTime(String appUserId); |
| | | |
| | | boolean updateExpirationTime(List<Long> appUserIds); |
| | | |
| | | /** |
| | | * 获取用户优惠券列表 |
| | |
| | | */ |
| | | NavigationCouponPickerVo navigationCouponPicker(Long userId, NavigationCouponPickerRequest request); |
| | | |
| | | @Async("couponSendExecutor") |
| | | boolean saveInfo(UseUserCouponDto dto); |
| | | |
| | | /** |
| | | * 发送优惠券 |
| | | * |
| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.account.mapper.XpAppUserCouponMapper; |
| | |
| | | import com.xiaopeng.account.vo.UserCouponListVo; |
| | | import com.xiaopeng.api.dto.account.NavigationCouponPickerRequest; |
| | | import com.xiaopeng.api.dto.account.UcUserInfo; |
| | | import com.xiaopeng.api.dto.account.UseUserCouponDto; |
| | | import com.xiaopeng.api.enums.NavigationCouponBubbleType; |
| | | import com.xiaopeng.api.feignClient.pushMessage.SubscribeMessageClient; |
| | | import com.xiaopeng.api.query.account.XpAppUserCouponPageListQuery; |
| | | import com.xiaopeng.api.vo.account.NavigationCouponItemVo; |
| | | import com.xiaopeng.api.vo.account.NavigationCouponPickerVo; |
| | |
| | | import com.xiaopeng.api.feignClient.operation.XpWhiteListClient; |
| | | import com.xiaopeng.api.model.account.XpAppUserCoupon; |
| | | import com.xiaopeng.api.model.operation.XpCoupon; |
| | | import com.xiaopeng.api.vo.pushMessage.SendSubscribeMessageVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.enums.MessageTemplateEnum; |
| | | import com.xiaopeng.common.core.utils.DateUtils; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Async; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | |
| | | |
| | | @Autowired |
| | | private UserResourcesClient userResourcesClient; |
| | | @Resource |
| | | private SubscribeMessageClient subscribeMessageClient; |
| | | |
| | | |
| | | @Override |
| | |
| | | List<XpAppUserCouponListVo> list = this.baseMapper.pageList(pageInfo,query); |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | | |
| | | /** |
| | | * 查询未使用、还有24小时就要过期的数据 |
| | | * @return |
| | | */ |
| | | @Override |
| | | public List<XpAppUserCoupon> findListExpirationTime(String appUserId){ |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | LocalDateTime expirationThreshold = now.plusHours(24); |
| | | |
| | | LambdaQueryWrapper<XpAppUserCoupon> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(XpAppUserCoupon::getDeleted, 0); |
| | | wrapper.eq(XpAppUserCoupon::getStatus, 1); |
| | | if(appUserId != null){ |
| | | wrapper.eq(XpAppUserCoupon::getAppUserId, appUserId); |
| | | } |
| | | // - 券失效前 24 小时 |
| | | wrapper.ge(XpAppUserCoupon::getExpirationTime, now); |
| | | wrapper.le(XpAppUserCoupon::getExpirationTime, expirationThreshold); |
| | | // 过滤 sendMsgTimeWx 不是当天发送的消息,避免重复发送 |
| | | wrapper.isNull(XpAppUserCoupon::getSendMsgTimeWx) |
| | | .or() |
| | | .lt(XpAppUserCoupon::getSendMsgTimeWx, now.toLocalDate().atStartOfDay()); |
| | | |
| | | return super.list(wrapper); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateExpirationTime(List<Long> appUserIds){ |
| | | |
| | | LambdaUpdateWrapper<XpAppUserCoupon> updateWrapper = new LambdaUpdateWrapper<>(); |
| | | updateWrapper.in(XpAppUserCoupon::getId, appUserIds); |
| | | updateWrapper.set(XpAppUserCoupon::getSendMsgTimeWx, LocalDateTime.now()); |
| | | return this.update(updateWrapper); |
| | | } |
| | | |
| | | |
| | |
| | | return xpAppUserCoupons; |
| | | } |
| | | |
| | | |
| | | @Async("couponSendExecutor") |
| | | @Override |
| | | public boolean saveInfo(UseUserCouponDto dto) { |
| | | R<XpCoupon> coupon = xpCouponClient.getCouponById(dto.getCouponId()); |
| | | if (coupon != null) { |
| | | XpCoupon xpCoupon = coupon.getData(); |
| | | for (Long userId : dto.getUserIds()) { |
| | | UcUserInfo userByMobile = userResourcesClient.getBaseUserByUid(String.valueOf(userId)); |
| | | XpAppUserCoupon xpAppUserCoupon = new XpAppUserCoupon(); |
| | | xpAppUserCoupon.setFullMoney(xpCoupon.getConditionalAmount()); |
| | | xpAppUserCoupon.setMoney(xpCoupon.getAmount()); |
| | | xpAppUserCoupon.setCouponId(dto.getCouponId()); |
| | | xpAppUserCoupon.setActivityId(dto.getActivityId()); |
| | | xpAppUserCoupon.setAppUserId(userId); |
| | | xpAppUserCoupon.setExpirationTime(LocalDateTime.now().plusDays(7)); |
| | | xpAppUserCoupon.setStatus(1); |
| | | xpAppUserCoupon.setCouponType(xpCoupon.getCouponType()); |
| | | xpAppUserCoupon.setPhone(userByMobile.getMobile()); |
| | | |
| | | xpAppUserCoupon.setCouponName(xpCoupon.getCouponName()); |
| | | xpAppUserCoupon.setOpenCityId("0"); |
| | | xpAppUserCoupon.setTimeJson("[{\"time\": [\"00:00-23:59\"], \"type\": 3, \"typeStr\": 0}]"); |
| | | baseMapper.insert(xpAppUserCoupon); |
| | | sendMsg(xpAppUserCoupon.getAppUserId(), xpCoupon, xpAppUserCoupon.getExpirationTime()); |
| | | } |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | /** |
| | | * 发送优惠券 |
| | | * |
| | |
| | | xpAppUserCoupon.setFullMoney(xpCoupon.getConditionalAmount()); |
| | | xpAppUserCoupon.setMoney(xpCoupon.getAmount()); |
| | | xpAppUserCoupon.setCouponId(dto.getCouponId()); |
| | | xpAppUserCoupon.setActivityId(dto.getActivityId()); |
| | | xpAppUserCoupon.setAppUserId(uid); |
| | | xpAppUserCoupon.setExpirationTime(LocalDateTime.now().plusDays(7)); |
| | | xpAppUserCoupon.setStatus(1); |
| | |
| | | xpAppUserCoupon.setOpenCityId("0"); |
| | | xpAppUserCoupon.setTimeJson("[{\"time\": [\"00:00-23:59\"], \"type\": 3, \"typeStr\": 0}]"); |
| | | baseMapper.insert(xpAppUserCoupon); |
| | | |
| | | sendMsg(uid, xpCoupon, xpAppUserCoupon.getExpirationTime()); |
| | | } |
| | | } |
| | | } |
| | |
| | | return null; |
| | | } |
| | | |
| | | private void sendMsg(Long uid, XpCoupon xpCoupon, LocalDateTime expirationTime ) { |
| | | //发送微信通知 |
| | | SendSubscribeMessageVo vo = new SendSubscribeMessageVo(); |
| | | vo.setUserId(uid); |
| | | vo.setTemplateId(MessageTemplateEnum.D.getTemplateId()); |
| | | String json = MessageTemplateEnum.D.getRemarks(); |
| | | json = json.replace("优惠券名称", xpCoupon.getCouponName()); |
| | | json = json.replace("券有效期", expirationTime.toString() ); |
| | | json = json.replace("使用规则", xpCoupon.getCouponDescription()); |
| | | vo.setJson(json); |
| | | subscribeMessageClient.sendSubscribeMessage(vo); |
| | | } |
| | | |
| | | @Override |
| | | public Integer getUserCouponNum(String uid) { |
| | | if (uid == null) { |
| | |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @ApiModel(value = "使用用户优惠券参数") |
| | | @AllArgsConstructor |
| | |
| | | public class UseUserCouponDto { |
| | | @ApiModelProperty(value = "用户ID") |
| | | private Long userId; |
| | | |
| | | @ApiModelProperty(value = "用户IDs") |
| | | private List<Long> userIds; |
| | | |
| | | |
| | | @ApiModelProperty(value = "订单ID") |
| | | private Long orderId; |
| | | @ApiModelProperty(value = "用户优惠券id") |
| | |
| | | |
| | | @ApiModelProperty(value = "开通城市id") |
| | | private Long openCityId; |
| | | |
| | | |
| | | @ApiModelProperty("优惠券id") |
| | | private Long couponId; |
| | | |
| | | @ApiModelProperty(value = "活动id") |
| | | private Long activityId; |
| | | } |
| | |
| | | return false; |
| | | } |
| | | |
| | | @Override |
| | | public R<String> sendSave(UseUserCouponDto dto) { |
| | | log.error("优惠券服务繁忙,原因: {}", cause.getMessage()); |
| | | return null; |
| | | } |
| | | |
| | | }; |
| | | } |
| | | } |
| | |
| | | import com.xiaopeng.api.vo.account.UserCouponVo; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | |
| | | |
| | | @GetMapping("/logan/inner/appUserCoupon/holding") |
| | | boolean isHolding(Long id); |
| | | |
| | | |
| | | /** |
| | | * userIds 、couponId、activityId |
| | | * @param dto |
| | | * @return |
| | | */ |
| | | @PostMapping("/api/v1/appUserCoupon/saveInfo") |
| | | @ApiOperation(value = "活动发放优惠券", tags = {"管理后台-活动发放优惠券"}) |
| | | R<String> sendSave(@RequestBody UseUserCouponDto dto) ; |
| | | |
| | | } |
| | |
| | | |
| | | @ApiModelProperty(value = "使用时间规则") |
| | | private String timeJson; |
| | | |
| | | |
| | | @ApiModelProperty(value = "发送微信通知时间") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | private LocalDateTime sendMsgTimeWx; |
| | | |
| | | |
| | | } |
| | |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | private LocalDateTime failureTime; |
| | | |
| | | //[{"name": "策略组1", "time": ["04:00-10:00", "14:00-23:00"], "type": 0, "typeStr": []}] |
| | | @ApiModelProperty(value = "运营策略") |
| | | private String operationStrategy; |
| | | @ApiModelProperty(value = "城市code") |
| | |
| | | @ApiModelProperty(value = "禁行区域数量") |
| | | @TableField(exist = false) |
| | | private Long prohibitedAreaCount; |
| | | @ApiModelProperty(value = "点位数量") |
| | | @TableField(exist = false) |
| | | private Long pointCount; |
| | | @ApiModelProperty(value = "场站数量") |
| | | @TableField(exist = false) |
| | | private Long stationCount; |
| | | } |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.io.Serializable; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.fasterxml.jackson.databind.annotation.JsonSerialize; |
| | | import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
| | | import com.xiaopeng.common.core.web.domain.BaseModel; |
| | |
| | | @ApiModelProperty(value = "生效周期 1=“每日生效”、2=“仅工作日生效”、3=“仅非工作日生效”、4=“仅节假日生效”") |
| | | private Integer effectivePeriod; |
| | | |
| | | @ApiModelProperty(value = "生效时间段") |
| | | @ApiModelProperty(value = "自定义运营生效时间段") |
| | | private String effectiveTimePeriod; |
| | | |
| | | @ApiModelProperty(value = "点位地址") |
| | |
| | | @TableField("district_code") |
| | | private String districtCode; |
| | | |
| | | |
| | | @ApiModelProperty(value = "所属场站id,为空表示运营区直属点位") |
| | | @TableField("station_id") |
| | | private Long stationId; |
| | |
| | | @ApiModelProperty(value = "点位上下车 1仅上车点/ 2 仅下车/ 3上下车均可") |
| | | private Integer pointOnOff; |
| | | |
| | | public String getPointOnOffName() { |
| | | if(pointOnOff != null){ |
| | | if(pointOnOff == 1){ |
| | | return "仅上车点"; |
| | | }else if(pointOnOff == 2){ |
| | | return "仅下车点"; |
| | | }else if(pointOnOff == 3){ |
| | | return "上下车均可"; |
| | | } |
| | | } |
| | | return ""; |
| | | } |
| | | |
| | | |
| | | @ApiModelProperty(value = "电子围栏名称") |
| | | @TableField(exist = false) |
| | | private String electronicName; |
| | | |
| | | @ApiModelProperty(value = "所属场站名称") |
| | | @TableField(exist = false) |
| | | private String stationName; |
| | | |
| | | /** |
| | | * hover在状态标签时,展示信息 |
| | | * - 已停用:展示停用原因;比如“因所属场站 [白云机场] 已关闭,该点位同步失效” |
| | | * - 测试不通过:展示不通过原因 |
| | | * - 待生效:展示生效时间 |
| | | */ |
| | | @ApiModelProperty(value = "状态描述") |
| | | @TableField(exist = false) |
| | | private String hoverStatusDes; |
| | | @ApiModelProperty(value = "场站的生效时间") |
| | | @TableField(exist = false) |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | private LocalDateTime electronicEffectTime; |
| | | |
| | | //[{"name": "策略组1", "time": ["04:00-10:00", "14:00-23:00"], "type": 0, "typeStr": []}] |
| | | @ApiModelProperty(value = "场站运营策略") |
| | | @TableField(exist = false) |
| | | private String electronicOperationStrategy; |
| | | |
| | | |
| | | } |
| | |
| | | @ApiModelProperty(value = "创建结束时间 yyyy-MM-dd HH:mm:ss") |
| | | private String endTime; |
| | | |
| | | @ApiModelProperty(value = "城市编码") |
| | | private String cityCode; |
| | | |
| | | @ApiModelProperty(value = "点位经度") |
| | | private String pointLon; |
| | | |
| | | @ApiModelProperty(value = "点位纬度") |
| | | private String pointLat; |
| | | |
| | | |
| | | @ApiModelProperty(value = "类型 1围栏 2场站 ") |
| | | private Integer idType; |
| | | |
| | | @ApiModelProperty(value = "fenceAndStationIds") |
| | | private String fenceAndStationIds; |
| | | |
| | | |
| | | } |
| | |
| | | "{\"thing3\":{\"value\":\"活动名称\"},\"time1\":{\"value\":\"开始时间\"},\"time4\":{\"value\":\"结束时间\"},\"thing6\":{\"value\":\"温馨提示\"}}"), |
| | | |
| | | C(1,"微信","ANtToR63JPCV_ujV69gmO15xCa8O3elnLWB52HLdcHM","券效期通用模板", |
| | | "{\"thing1\":{\"value\":\"优惠券名称\"},\"thing2\":{\"value\":\"备注\"}}"), |
| | | "{\"thing1\":{\"value\":\"优惠券名称\"},\"thing2\":{\"value\":\"今日出行最高立减XXX元,点击领取\"}}"), |
| | | |
| | | D(1,"微信","P7z_ksdyzpiiOBO07EJ1AZ6WeHlWs4XveQYjmt9JRE8","券到账通用模板", |
| | | "{\"thing1\":{\"value\":\"优惠券名称\"},\"time2\":{\"value\":\"券有效期\"},\"thing3\":{\"value\":\"使用规则\"}}"), |
| | |
| | | import com.xiaopeng.map.constant.I18nKeyConstant; |
| | | import com.xiaopeng.map.service.*; |
| | | import com.xiaopeng.map.utils.OperationStrategyIndex; |
| | | import com.xiaopeng.map.vo.FenceAndStationVo; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | return R.ok(regionService.getXpRegion()); |
| | | } |
| | | |
| | | @ApiOperation(value = "获取围栏和场站数据",tags = {"管理后台-电子围栏"}) |
| | | @GetMapping(value = "/findListFenceAndStation") |
| | | public R<List<FenceAndStationVo>> findListFenceAndStation() { |
| | | return R.ok(xpPointService.findListFenceAndStation()); |
| | | } |
| | | |
| | | @ApiOperation(value = "获取不分页的电子围栏列表",tags = {"管理后台-电子围栏"}) |
| | | @PostMapping("/list") |
| | |
| | | */ |
| | | @PostMapping("/add") |
| | | @ApiOperation(value = "新增电子围栏") |
| | | public R add(@Validated @RequestBody ElectronicFenceAddDto dto) { |
| | | public R add(@Valid @RequestBody ElectronicFenceAddDto dto) { |
| | | validateFenceTime(dto.getStatus(), dto.getEffectType(), dto.getEffectTime(), dto.getFailureType(), dto.getFailureTime()); |
| | | return xpElectronicFenceService.addElectronic(dto); |
| | | } |
| | |
| | | public static final String failure = "electronicFailure_"; |
| | | |
| | | @Override |
| | | public void execute(JobExecutionContext context) |
| | | throws JobExecutionException { |
| | | public void execute(JobExecutionContext context) throws JobExecutionException { |
| | | JobDataMap maps = context.getMergedJobDataMap(); |
| | | Long electronicId = maps.getLong("id"); |
| | | Long type = maps.getLong("type"); |
| | | try { |
| | | if (type==1L){ |
| | | // 电子围栏生效 |
| | | XpElectronicFence electronicFence = xpElectronicFenceService.getById(electronicId); |
| | | electronicFence.setStatus(2); |
| | | xpElectronicFenceService.updateById(electronicFence); |
| | | List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpElectronicFenceArea::getElectronicId, electronicId).list(); |
| | | // 设置为生效 |
| | | for(XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList){ |
| | | electronicFenceArea.setAreaStatus(1); |
| | | } |
| | | xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList); |
| | | xpElectronicFenceService.enableFence(electronicId); |
| | | |
| | | // 推送 FBS 到绑定车辆 |
| | | try { |
| | |
| | | } |
| | | }else{ |
| | | // 电子围栏失效 |
| | | XpElectronicFence electronicFence = xpElectronicFenceService.getById(electronicId); |
| | | electronicFence.setStatus(3); |
| | | xpElectronicFenceService.updateById(electronicFence); |
| | | List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpElectronicFenceArea::getElectronicId, electronicId).list(); |
| | | // 设置为禁用 |
| | | for(XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList){ |
| | | electronicFenceArea.setAreaStatus(2); |
| | | } |
| | | xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList); |
| | | |
| | | xpStationService.setStatusFailure(electronicId);//场站失效 |
| | | xpElectronicFenceService.expireFence(electronicId); |
| | | // 清除绑定车辆的 FBS 数据 |
| | | try { |
| | | adcFencePushService.deactivateFenceForVehicles(electronicId); |
| | |
| | | * @since 2026-02-03 |
| | | */ |
| | | public interface XpElectronicFenceMapper extends BaseMapper<XpElectronicFence> { |
| | | |
| | | Long pageListCount(@Param("query") ElectronicPageListQuery query); |
| | | |
| | | List<XpElectronicFence> pageList(@Param("pageInfo") PageInfo<XpElectronicFence> pageInfo, @Param("query") ElectronicPageListQuery query); |
| | | |
| | | } |
| | |
| | | import com.xiaopeng.api.vo.order.XpPointsListVo; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.map.query.PointByNameQuery; |
| | | import com.xiaopeng.map.vo.FenceAndStationVo; |
| | | import org.apache.ibatis.annotations.Select; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | |
| | | */ |
| | | public interface XpPointMapper extends BaseMapper<XpPoint> { |
| | | |
| | | List<FenceAndStationVo> findListFenceAndStation(); |
| | | |
| | | List<XpPoint> pointMap(@Param("query") PointMapQuery query); |
| | | |
| | | List<XpPoint> listAll(@Param("query") PointListQuery query); |
| | |
| | | public class ElectronicFenceFailureCompensationTask { |
| | | |
| | | private static final String LOCK_KEY = "MAP:ELECTRONIC_FENCE:FAILURE_COMPENSATE"; |
| | | private static final String LOCK_KEY_ENABLE = "MAP:ELECTRONIC_FENCE:ENABLE_COMPENSATE"; |
| | | private static final int LOCK_WAIT_SECONDS = 0; |
| | | private static final int LOCK_LEASE_SECONDS = 15; |
| | | |
| | |
| | | @Resource |
| | | private RedissonClient redissonClient; |
| | | |
| | | |
| | | /** |
| | | * 定时任务 围栏失效补偿定时任务 |
| | | */ |
| | | @Scheduled(fixedDelay = 10 * 1000L) |
| | | public void compensateExpiredFence() { |
| | | RLock lock = redissonClient.getLock(LOCK_KEY); |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 围栏启动偿定时任务 |
| | | */ |
| | | @Scheduled(fixedDelay = 10 * 1000L) |
| | | public void compensateEnableFence() { |
| | | RLock lock = redissonClient.getLock(LOCK_KEY_ENABLE); |
| | | boolean locked = false; |
| | | try { |
| | | locked = lock.tryLock(LOCK_WAIT_SECONDS, LOCK_LEASE_SECONDS, TimeUnit.SECONDS); |
| | | if (!locked) { |
| | | return; |
| | | } |
| | | List<XpElectronicFence> expiredFenceList = xpElectronicFenceService.lambdaQuery() |
| | | .eq(XpElectronicFence::getDeleted, 0) |
| | | .eq(XpElectronicFence::getStatus, 1)//围栏状态 0=草稿 1=待启用 2=启用中 3=已停用 |
| | | .ge(XpElectronicFence::getFailureTime, LocalDateTime.now())//当前时间小于生效时间 |
| | | .list(); |
| | | if (expiredFenceList == null || expiredFenceList.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> electronicIds = expiredFenceList.stream() |
| | | .map(XpElectronicFence::getId) |
| | | .collect(Collectors.toList()); |
| | | log.info("电子围栏失效补偿开始, count={}, ids={}", electronicIds.size(), electronicIds); |
| | | for (Long electronicId : electronicIds) { |
| | | try { |
| | | xpElectronicFenceService.enableFence(electronicId); |
| | | } catch (Exception e) { |
| | | log.error("电子围栏失效补偿单条处理异常, electronicId={}", electronicId, e); |
| | | } |
| | | } |
| | | log.info("电子围栏失效补偿完成, count={}, ids={}", electronicIds.size(), electronicIds); |
| | | } catch (InterruptedException e) { |
| | | Thread.currentThread().interrupt(); |
| | | log.warn("电子围栏失效补偿任务获取锁被中断", e); |
| | | } catch (Exception e) { |
| | | log.error("电子围栏失效补偿任务执行异常", e); |
| | | } finally { |
| | | try { |
| | | if (locked && lock.isHeldByCurrentThread()) { |
| | | lock.unlock(); |
| | | } |
| | | } catch (Exception e) { |
| | | log.warn("释放电子围栏失效补偿锁失败 lockKey={}", LOCK_KEY, e); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.xiaopeng.api.query.operation.ElectronicPageListQuery; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; |
| | | import org.apache.shardingsphere.transaction.core.TransactionType; |
| | | import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | */ |
| | | List<XpElectronicFence> getList(ElectronicPageListQuery query); |
| | | |
| | | @Transactional |
| | | @ShardingSphereTransactionType(TransactionType.BASE) |
| | | void enableFence(Long electronicId); |
| | | |
| | | /** |
| | | * 将围栏置为已停用,并同步禁用围栏区域 |
| | | * @param electronicId 围栏ID |
| | |
| | | import com.xiaopeng.map.dto.NearestDto; |
| | | import com.xiaopeng.map.dto.StationDetailQueryDto; |
| | | import com.xiaopeng.map.query.PointByNameQuery; |
| | | import com.xiaopeng.map.vo.FenceAndStationVo; |
| | | import com.xiaopeng.map.vo.NearestVo; |
| | | import com.xiaopeng.map.vo.OrderDistanceVo; |
| | | import com.xiaopeng.map.vo.StationDetailVo; |
| | |
| | | boolean getInTime(LocalTime now, Integer effectivePeriod, String effectiveTimePeriod); |
| | | List<XpPoint> pointMap(PointMapQuery query); |
| | | |
| | | List<FenceAndStationVo> findListFenceAndStation(); |
| | | |
| | | List<XpPoint> findList(PointListQuery query); |
| | | |
| | | List<XpPoint> listAll(PointListQuery query); |
| | |
| | | import com.xiaopeng.map.service.XpElectronicFenceAreaService; |
| | | import com.xiaopeng.map.service.XpElectronicFenceService; |
| | | import com.xiaopeng.map.service.XpPointService; |
| | | import com.xiaopeng.map.service.XpStationService; |
| | | import com.xiaopeng.map.utils.OperationStrategyIndex; |
| | | import com.xiaopeng.map.utils.GDMapGeocodingUtil; |
| | | import com.xiaopeng.map.utils.PolygonOverlapService; |
| | |
| | | import org.locationtech.jts.geom.Polygon; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.ApplicationEventPublisher; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.data.geo.Point; |
| | | import org.springframework.data.mongodb.core.MongoTemplate; |
| | | import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; |
| | |
| | | private XpElectronicFenceAreaService xpElectronicFenceAreaService; |
| | | @Autowired |
| | | private ApplicationEventPublisher applicationEventPublisher; |
| | | @Autowired |
| | | @Lazy |
| | | protected XpStationService xpStationService; |
| | | |
| | | @Override |
| | | public PageInfo<XpElectronicFence> pageList(ElectronicPageListQuery query) { |
| | |
| | | } |
| | | normalizeProhibitedAreaCountFilter(query); |
| | | PageInfo<XpElectronicFence> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); |
| | | List<XpElectronicFence> list = this.baseMapper.pageList(pageInfo, query); |
| | | if (list.isEmpty()) { |
| | | pageInfo.setSearchCount(false); |
| | | Long total = baseMapper.pageListCount(query); |
| | | pageInfo.setTotal(total); |
| | | if (total == 0L) { |
| | | return new PageInfo<>(); |
| | | } |
| | | List<XpElectronicFence> list = this.baseMapper.pageList(pageInfo, query); |
| | | List<Long> electronicIds = list.stream().map(XpElectronicFence::getId).collect(Collectors.toList()); |
| | | Map<Long, List<XpElectronicFenceArea>> electronicFenceAreaMap = xpElectronicFenceAreaMapper.selectList(new LambdaQueryWrapper<XpElectronicFenceArea>() |
| | | .in(XpElectronicFenceArea::getElectronicId, electronicIds)).stream() |
| | |
| | | } |
| | | |
| | | //根据经纬度获取当前城市code |
| | | XpElectronicFenceArea xpElectronicFenceArea = dto.getElectronicFenceAreas().get(0); |
| | | String[] split = xpElectronicFenceArea.getFencePoints().split(","); |
| | | Coordinate[] coordinates = new Coordinate[split.length + 1]; |
| | | for (int i = 0; i < split.length; i++) { |
| | | String[] split1 = split[i].split("_"); |
| | | coordinates[i] = new Coordinate(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])); |
| | | } |
| | | org.locationtech.jts.geom.Point point = calculateCentroid(coordinates); |
| | | Map<String, String> geocode = gdMapGeocodingUtil.geocode(point.getX() + "", point.getY() + ""); |
| | | if (null == geocode) { |
| | | return R.fail("经纬度转换城市code失败"); |
| | | } |
| | | String cityCode = geocode.get("cityCode"); |
| | | dto.setCityCode(cityCode); |
| | | // XpElectronicFenceArea xpElectronicFenceArea = dto.getElectronicFenceAreas().get(0); |
| | | // String[] split = xpElectronicFenceArea.getFencePoints().split(","); |
| | | // Coordinate[] coordinates = new Coordinate[split.length + 1]; |
| | | // for (int i = 0; i < split.length; i++) { |
| | | // String[] split1 = split[i].split("_"); |
| | | // coordinates[i] = new Coordinate(Double.parseDouble(split1[0]), Double.parseDouble(split1[1])); |
| | | // } |
| | | // org.locationtech.jts.geom.Point point = calculateCentroid(coordinates); |
| | | // Map<String, String> geocode = gdMapGeocodingUtil.geocode(point.getX() + "", point.getY() + ""); |
| | | // if (null == geocode) { |
| | | // return R.fail("经纬度转换城市code失败"); |
| | | // } |
| | | // String cityCode = geocode.get("cityCode"); |
| | | // dto.setCityCode(cityCode); |
| | | |
| | | xpElectronicFence.setCityCode(dto.getCityCode()); |
| | | |
| | | // 生成围栏id 规则:城市首字母-类型简写-4位自增流水号(如 SZ-OP-001),支持复制;数据停用或删除后,流水号不回收 |
| | | XpRegion xpRegion = xpRegionMapper.selectOne(new LambdaQueryWrapper<XpRegion>() |
| | | .eq(XpRegion::getCode, dto.getCityCode())); |
| | |
| | | QuartzManager.removeJob((ElectronicEffectJob.effect + xpElectronicFence.getId()).toUpperCase(), TimeJobType.ELECTRONIC_EFFECT); |
| | | QuartzManager.removeJob((ElectronicEffectJob.failure + xpElectronicFence.getId()).toUpperCase(), TimeJobType.ELECTRONIC_FAILURE); |
| | | } |
| | | /* |
| | | //根据经纬度获取当前城市code |
| | | XpElectronicFenceArea electronicFenceArea = dto.getElectronicFenceAreas().get(0); |
| | | String[] split = electronicFenceArea.getFencePoints().split(","); |
| | |
| | | } |
| | | String cityCode = geocode.get("cityCode"); |
| | | dto.setCityCode(cityCode); |
| | | */ |
| | | |
| | | xpElectronicFence.setCityCode(dto.getCityCode()); |
| | | |
| | | this.updateById(xpElectronicFence); |
| | |
| | | XpGeoFencingPolygon xpGeoFencingPolygon = new XpGeoFencingPolygon(); |
| | | xpGeoFencingPolygon.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | xpGeoFencingPolygon.setName(xpElectronicFence.getElectronicName()); |
| | | xpGeoFencingPolygon.setCityCode(cityCode); |
| | | xpGeoFencingPolygon.setCityCode(dto.getCityCode()); |
| | | xpGeoFencingPolygon.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | xpGeoFencingPolygon.setOperationStrategy(xpElectronicFence.getOperationStrategy()); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | |
| | | // 如果存在,则更新已有文档 |
| | | old.setFenceId(String.valueOf(xpElectronicFence.getId())); |
| | | old.setName(xpElectronicFence.getElectronicName()); |
| | | old.setCityCode(cityCode); |
| | | old.setCityCode(dto.getCityCode()); |
| | | old.setAreaId(String.valueOf(newElectronicFenceArea.getId())); |
| | | old.setOperationStrategy(xpElectronicFence.getOperationStrategy()); |
| | | // 将围栏点转换为GeoJSON多边形 |
| | |
| | | queryWrapper.like(query.getCityCode() != null, XpElectronicFence::getCityCode, query.getCityCode()); |
| | | return list(queryWrapper); |
| | | } |
| | | @Transactional |
| | | @ShardingSphereTransactionType(TransactionType.BASE) |
| | | @Override |
| | | public void enableFence(Long electronicId) { |
| | | // 电子围栏生效 |
| | | XpElectronicFence electronicFence = super.getById(electronicId); |
| | | if(electronicFence != null){ |
| | | electronicFence.setStatus(2); |
| | | super.updateById(electronicFence); |
| | | List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery().eq(XpElectronicFenceArea::getElectronicId, electronicId).list(); |
| | | if(!electronicFenceAreaList.isEmpty()){ |
| | | // 设置为生效 |
| | | for(XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList){ |
| | | electronicFenceArea.setAreaStatus(1); |
| | | } |
| | | xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList); |
| | | } |
| | | } |
| | | |
| | | } |
| | | @Override |
| | | @Transactional |
| | | @ShardingSphereTransactionType(TransactionType.BASE) |
| | |
| | | electronicFence.setStatus(3); |
| | | this.updateById(electronicFence); |
| | | |
| | | List<String> toDeleteAreaIdStrings = new ArrayList<>(); |
| | | |
| | | List<XpElectronicFenceArea> electronicFenceAreaList = xpElectronicFenceAreaService.lambdaQuery() |
| | | .eq(XpElectronicFenceArea::getElectronicId, electronicId) |
| | | .list(); |
| | | if (CollUtil.isEmpty(electronicFenceAreaList)) { |
| | | return; |
| | | if (!CollUtil.isEmpty(electronicFenceAreaList)) { |
| | | for (XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList) { |
| | | electronicFenceArea.setAreaStatus(2); |
| | | toDeleteAreaIdStrings.add(String.valueOf(electronicFenceArea.getId())); |
| | | } |
| | | |
| | | xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList); |
| | | } |
| | | for (XpElectronicFenceArea electronicFenceArea : electronicFenceAreaList) { |
| | | electronicFenceArea.setAreaStatus(2); |
| | | |
| | | xpStationService.setStatusFailure(electronicId);//场站失效 |
| | | |
| | | // 删除旧的区域数据,然后保存新的区域数据 |
| | | mongoTemplate.remove(new Query(Criteria.where("fenceId").is(String.valueOf(electronicId))), XpGeoFencingPolygon.class); |
| | | if(CollUtil.isNotEmpty(toDeleteAreaIdStrings)){ |
| | | Query delMongoQuery = new Query(); |
| | | delMongoQuery.addCriteria(Criteria.where("fenceId").is(String.valueOf(electronicId))); |
| | | delMongoQuery.addCriteria(Criteria.where("areaId").in(toDeleteAreaIdStrings)); |
| | | mongoTemplate.remove(delMongoQuery, XpGeoFencingProhibitedPolygon.class); |
| | | } |
| | | xpElectronicFenceAreaService.updateBatchById(electronicFenceAreaList); |
| | | } |
| | | |
| | | |
| | |
| | | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | |
| | | import com.xiaopeng.map.utils.TimeRangeChecker; |
| | | import com.xiaopeng.map.utils.holiday.HolidayUtil; |
| | | import com.xiaopeng.map.utils.map.map.FastDistanceCalculator; |
| | | import com.xiaopeng.map.vo.NearestNextVo; |
| | | import com.xiaopeng.map.vo.NearestVo; |
| | | import com.xiaopeng.map.vo.OrderDistanceVo; |
| | | import com.xiaopeng.map.vo.StationDetailVo; |
| | | import com.xiaopeng.map.vo.StationPointItemVo; |
| | | import com.xiaopeng.map.vo.*; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import net.sourceforge.pinyin4j.PinyinHelper; |
| | | import org.apache.shardingsphere.transaction.annotation.ShardingSphereTransactionType; |
| | |
| | | List<XpPoint> list = this.baseMapper.pointMap(query); |
| | | return list; |
| | | } |
| | | |
| | | @Override |
| | | public List<FenceAndStationVo> findListFenceAndStation(){ |
| | | return baseMapper.findListFenceAndStation(); |
| | | } |
| | | |
| | | @Override |
| | | public List<XpPoint> findList(PointListQuery query) { |
| | | return this.baseMapper.listAll(query); |
| | |
| | | } |
| | | } |
| | | List<XpPoint> list = this.baseMapper.pageList(pageInfo,query); |
| | | if(CollUtil.isNotEmpty(list)){ |
| | | /** |
| | | * hover在状态标签时,展示信息 |
| | | * - 已停用:展示停用原因;比如“因所属场站 [白云机场] 已关闭,该点位同步失效” |
| | | * - 测试不通过:展示不通过原因 |
| | | * - 待生效:展示生效时间 |
| | | */ |
| | | for (XpPoint point : list) { |
| | | String hoverStatusDes = ""; |
| | | if(point.getVerificationStatus()==4){//验证状态 1=待验证、2=验证中、3=已验证 4验证失败 5待生效 |
| | | hoverStatusDes = (point.getRemarks() == null ?"":point.getRemarks()); |
| | | } |
| | | if(point.getOperationStatus() == 3){//运营状态 1=待启用 2=启用中 3=已停用 4=已失效 |
| | | hoverStatusDes = (point.getRemarks()==null?"":point.getRemarks()); |
| | | } |
| | | if(point.getVerificationStatus()==5 && point.getOperationStatus() == 1){ |
| | | if(point.getOperationType() == 1){//运营类型 1=与运营区同步 2=自定义时间段 |
| | | ; |
| | | //[{"name": "策略组1", "time": ["06:00-12:00", "13:00-22:59"], "type": 0, "typeStr": []}, |
| | | // {"name": "策略组2", "time": ["05:00-12:00", "12:00-16:00"], "type": 2, "typeStr": ""}, |
| | | // {"name": "策略组3", "time": ["09:00-21:59"], "type": 1, "typeStr": ""}] |
| | | JSONArray electronicOperationStrategys = JSONArray.parseArray(point.getElectronicOperationStrategy()); |
| | | if(!electronicOperationStrategys.isEmpty()){ |
| | | StringJoiner joiner = new StringJoiner(";"); |
| | | for (Object electronicOperationStrategy : electronicOperationStrategys) { |
| | | JSONObject obj = (JSONObject) electronicOperationStrategy; |
| | | |
| | | List<String> times = obj.getList("time",String.class); |
| | | joiner.add((String.join(";", times))); |
| | | } |
| | | hoverStatusDes = joiner.toString() ; |
| | | } |
| | | }else{ |
| | | if(StrUtil.isNotBlank(point.getEffectiveTimePeriod()) ){ |
| | | List<String> effectiveTimePeriods = JSONArray.parseArray(point.getEffectiveTimePeriod(), String.class); |
| | | hoverStatusDes = String.join(";", effectiveTimePeriods); |
| | | // hoverStatusDes = "待生效:"+point.getElectronicEffectTime().toString() ; |
| | | } |
| | | } |
| | | } |
| | | point.setHoverStatusDes(hoverStatusDes); |
| | | } |
| | | } |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | |
| | | if (list.isEmpty()) { |
| | | return new PageInfo<>(); |
| | | } |
| | | for (XpStation xpStation : list) { |
| | | xpStation.setPointCount(0); |
| | | xpStation.setOperatingPointCount(0); |
| | | List<XpPoint> xpPoints = xpPointService.listByStationId((xpStation.getId())); |
| | | if(CollUtil.isNotEmpty(xpPoints)){ |
| | | xpStation.setPointCount(xpPoints.size()); |
| | | xpStation.setOperatingPointCount((int) xpPoints.stream().filter(p -> p.getOperationStatus() != null && p.getOperationStatus() == 2).count()); |
| | | } |
| | | } |
| | | pageInfo.setRecords(list); |
| | | return pageInfo; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 设置场站状态 0=草稿 1=待启用 2=启用中 3=已停用 4=失效 |
| | | * 并操作 mongo |
| | | * @param bean |
| | | * @param status |
| | | * @return |
| | | */ |
| | | @Override |
| | | public boolean setStatus(XpStation bean, Integer status){ |
| | | |
| New file |
| | |
| | | package com.xiaopeng.map.vo; |
| | | |
| | | |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class FenceAndStationVo { |
| | | |
| | | // 1 as type, a.electronic_name as name, |
| | | // GROUP_CONCAT(b.id) ids |
| | | |
| | | @ApiModelProperty(value = "类型 1围栏 2场站 ") |
| | | private Integer type; |
| | | @ApiModelProperty(value = "名称") |
| | | private String name; |
| | | @ApiModelProperty(value = "ids") |
| | | private String ids; |
| | | |
| | | } |
| | |
| | | <sql id="Base_Column_List"> |
| | | id, electronic_name, electronic_number, status, effect_type, effect_time, failure_type, failure_time, operation_strategy, is_deleted, create_time, update_time, create_by, update_by |
| | | </sql> |
| | | |
| | | <select id="pageListCount" resultType="Long"> |
| | | select count(f.id) as count from ( |
| | | select ef.id |
| | | <include refid="pageListWhere"/> |
| | | ) f |
| | | </select> |
| | | <select id="pageList" resultType="com.xiaopeng.api.model.operation.XpElectronicFence"> |
| | | select ef.id, ef.electronic_name as electronicName, ef.electronic_number as electronicNumber, ef.status, ef.effect_type as effectType, |
| | | ef.city_code, |
| | | ef.effect_time as effectTime, ef.failure_type as failureType, ef.failure_time as failureTime, ef.operation_strategy as operationStrategy |
| | | , ef.create_time as createTime, ef.update_time as updateTime, ef.create_by as createBy, ef.update_by as updateBy |
| | | select ef.*, |
| | | (SELECT COUNT(xs.id) FROM xp_station xs WHERE xs.is_deleted=0 and xs.electronic_id = ef.id) stationCount, |
| | | SUM((SELECT COUNT(xp.id) FROM xp_point xp WHERE xp.is_deleted=0 and xp.electronic_area_id = efa.id ))as pointCount |
| | | <include refid="pageListWhere"/> |
| | | <choose> |
| | | <when test="query.createSort == 1 or query.createSort == 2 or query.updateSort == 1 or query.updateSort == 2"> |
| | | <trim prefix="order by" suffixOverrides=","> |
| | | <if test="query.createSort == 1"> |
| | | ef.create_time asc, |
| | | </if> |
| | | <if test="query.createSort == 2"> |
| | | ef.create_time desc, |
| | | </if> |
| | | <if test="query.updateSort == 1"> |
| | | ef.update_time asc, |
| | | </if> |
| | | <if test="query.updateSort == 2"> |
| | | ef.update_time desc, |
| | | </if> |
| | | </trim> |
| | | </when> |
| | | <otherwise> |
| | | order by ef.update_time desc |
| | | </otherwise> |
| | | </choose> |
| | | </select> |
| | | |
| | | <sql id="pageListWhere" > |
| | | |
| | | from xp_electronic_fence ef |
| | | left join xp_electronic_fence_area efa on efa.electronic_id = ef.id and efa.is_deleted = 0 |
| | | where ef.is_deleted = 0 |
| | |
| | | having sum(case when efa.area_type = 2 then 1 else 0 end) <= #{query.prohibitedAreaCountMax} |
| | | </when> |
| | | </choose> |
| | | <choose> |
| | | <when test="query.createSort == 1 or query.createSort == 2 or query.updateSort == 1 or query.updateSort == 2"> |
| | | <trim prefix="order by" suffixOverrides=","> |
| | | <if test="query.createSort == 1"> |
| | | ef.create_time asc, |
| | | </if> |
| | | <if test="query.createSort == 2"> |
| | | ef.create_time desc, |
| | | </if> |
| | | <if test="query.updateSort == 1"> |
| | | ef.update_time asc, |
| | | </if> |
| | | <if test="query.updateSort == 2"> |
| | | ef.update_time desc, |
| | | </if> |
| | | </trim> |
| | | </when> |
| | | <otherwise> |
| | | order by ef.update_time desc |
| | | </otherwise> |
| | | </choose> |
| | | |
| | | |
| | | </select> |
| | | </sql> |
| | | |
| | | </mapper> |
| | |
| | | <result column="update_by" property="updateBy" /> |
| | | <result column="station_id" property="stationId" /> |
| | | <result column="station_sort_no" property="stationSortNo" /> |
| | | <result column="point_on_off" property="pointOnOff" /> |
| | | </resultMap> |
| | | |
| | | <!-- 通用查询结果列 --> |
| | | <sql id="Base_Column_List"> |
| | | id,electronic_area_id, point_name, point_poi, point_number, operation_status, point_type, verification_status, parking_time_limit, operation_type, effective_period, effective_time_period, remarks, point_pictures, point_lon, point_lat, is_deleted, create_time, update_time, create_by, update_by, station_id, station_sort_no |
| | | id,electronic_area_id, point_name, point_poi, point_number, operation_status, point_type, verification_status, parking_time_limit, operation_type, effective_period, |
| | | effective_time_period, remarks, point_pictures, point_lon, point_lat, is_deleted, create_time, update_time, create_by, update_by, station_id, station_sort_no,point_on_off |
| | | </sql> |
| | | <select id="pointMap" resultType="com.xiaopeng.api.model.operation.XpPoint"> |
| | | select t1.*, |
| | |
| | | </if> |
| | | order by distance asc |
| | | </select> |
| | | |
| | | <select id="findListFenceAndStation" resultType="com.xiaopeng.map.vo.FenceAndStationVo"> |
| | | SELECT 1 as type, a.electronic_name as name, GROUP_CONCAT(b.id) ids |
| | | FROM xp_electronic_fence a |
| | | INNER JOIN xp_electronic_fence_area b on b.electronic_id = a.id and b.is_deleted = 0 |
| | | WHERE a.is_deleted = 0 |
| | | GROUP BY a.id |
| | | UNION ALL |
| | | SELECT 2 as type ,s.station_name as name,s.id as ids FROM xp_station s WHERE s.is_deleted = 0 |
| | | |
| | | </select> |
| | | |
| | | <select id="listAll" resultType="com.xiaopeng.api.model.operation.XpPoint"> |
| | | select t1.* |
| | | from xp_point t1 |
| | |
| | | |
| | | |
| | | <select id="queryPointByName" resultType="com.xiaopeng.api.vo.order.XpPointsListVo"> |
| | | select p.id,p.electronic_area_id as electronicAreaId,p.point_name as name,p.point_address as address,p.point_lon as longitude,p.point_lat as latitude,p.effective_period,p.effective_time_period |
| | | select p.id,p.electronic_area_id as electronicAreaId,p.point_name as name,p.point_address as address, |
| | | p.point_lon as longitude,p.point_lat as latitude,p.effective_period,p.effective_time_period,p.point_on_off, |
| | | from xp_electronic_fence_area xefa |
| | | left join xp_point p on p.electronic_area_id = xefa.id |
| | | where p.is_deleted = 0 and p.operation_status =2 and xefa.is_deleted = 0 |
| | |
| | | </select> |
| | | <select id="pageList" resultType="com.xiaopeng.api.model.operation.XpPoint"> |
| | | select xp.id,xp.point_number as pointNumber,xp.point_name as pointName,xp.point_poi as pointPoi, |
| | | xp.point_address as pointAddress,xp.city_code, |
| | | xp.point_address as pointAddress,xp.city_code,xp.point_on_off,xp.operation_type,xp.effective_time_period, |
| | | xp.point_lon as pointLon,xp.point_lat as pointLat,xp.create_time as createTime,xp.create_by as createBy |
| | | ,xp.parking_time_limit as parkingTimeLimit,xp.verification_status as verificationStatus,xp.operation_status as operationStatus |
| | | ,xp.station_id as stationId,xp.station_sort_no as stationSortNo |
| | | ,xp.station_id as stationId,xp.station_sort_no as stationSortNo,xp.remarks, |
| | | xef.electronic_name as electronicName,xs.station_name as stationName,xef.effect_time, |
| | | xef.operation_strategy electronicOperationStrategy |
| | | from xp_point xp |
| | | left join xp_electronic_fence_area xefa on xefa.id = xp.electronic_area_id |
| | | left join xp_electronic_fence xef on xef.id = xefa.electronic_id |
| | | left join xp_station xs on xs.id = xp.station_id |
| | | where xp.is_deleted = 0 |
| | | |
| | | <if test="query.idType != null"> |
| | | <choose> |
| | | <when test="query.idType == 1"> |
| | | and xefa.id in (${query.fenceAndStationIds}) |
| | | </when> |
| | | <when test="query.idType == 2"> |
| | | and xs.id =#{query.fenceAndStationIds} |
| | | </when> |
| | | </choose> |
| | | </if> |
| | | <if test="query.cityCode != null and query.cityCode != ''"> |
| | | and xp.city_code = #{query.cityCode} |
| | | </if> |
| | | |
| | | <if test="query.pointName != null and query.pointName != ''"> |
| | | and xp.point_name like concat('%',#{query.pointName},'%') |
| | | </if> |
| | |
| | | <mapper namespace="com.xiaopeng.map.mapper.XpStationMapper"> |
| | | |
| | | |
| | | |
| | | |
| | | <select id="pageList" resultType="com.xiaopeng.api.model.operation.XpStation"> |
| | | select a.*, |
| | | (select count(1) from xp_point p where p.station_id = a.id) point_count, |
| | | (select count(1) from xp_point p where p.station_id = a.id and p.operation_status = 2) operatingPointCount |
| | | select a.* |
| | | from xp_station a |
| | | inner join xp_electronic_fence b on a.electronic_id = b.id and b.is_deleted = 0 |
| | | where a.is_deleted = 0 |
| | | |
| | | <if test="query.ids != null"> |
| | | and a.id in |
| | | <foreach item="id" collection="query.ids" open="(" separator="," close=")"> |
| | |
| | | ef.effect_time as effectTime, ef.failure_type as failureType, ef.failure_time as failureTime, ef.operation_strategy as operationStrategy |
| | | , ef.create_time as createTime, ef.update_time as updateTime, ef.create_by as createBy, ef.update_by as updateBy |
| | | from xp_test_electronic_fence ef |
| | | left join xp_test_electronic_fence_area efa on efa.electronic_id = ef.id and efa.is_deleted = 0 |
| | | left join xp_test_electronic_fence_area efa on efa.test_electronic_id = ef.id and efa.is_deleted = 0 |
| | | where ef.is_deleted = 0 |
| | | <if test="query.electronicNumberOrName != null and query.electronicNumberOrName != ''"> |
| | | and( ef.electronic_name like concat('%',#{query.electronicNumberOrName},'%') |
| | |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Arrays; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | |
| | | SendSubscribeMessageVo vo = new SendSubscribeMessageVo(); |
| | | vo.setUserId(order.getAppUserId()); |
| | | String json = null; |
| | | if(order.getStatus() == OrderStatusEnum.PENDING_ORDER.getCode()){ |
| | | // status = 状态 0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中,7=待客下车,8=待付款,9=待评价,10=已完成 |
| | | if(order.getStatus() == OrderStatusEnum.PICK_UP.getCode()){//车辆接单/改派通知 |
| | | String plateNumber = null; |
| | | // 查询车辆 |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); |
| | |
| | | vo.setTemplateId(MessageTemplateEnum.E.getTemplateId()); |
| | | json = MessageTemplateEnum.E.getRemarks(); |
| | | json = json.replace("车牌号", plateNumber); |
| | | json = json.replace("报警时间", LocalDateTime.now().toString()); |
| | | json = json.replace("预计到达时间", LocalDateTime.now().toString()); |
| | | } |
| | | }else{ |
| | | }else if(order.getStatus() == OrderStatusEnum.PICK_UP_GUESTS.getCode()) { //车辆到达上车点通知 |
| | | String plateNumber = null; |
| | | // 查询车辆 |
| | | XpVehicle xpVehicle = xpVehicleClient.queryById(order.getVehicleId()).getData(); |
| | | if(xpVehicle != null){ |
| | | plateNumber = xpVehicle.getPlateNumber(); |
| | | } |
| | | if(StrUtil.isNotBlank(plateNumber)){ |
| | | vo.setTemplateId(MessageTemplateEnum.E.getTemplateId()); |
| | | json = MessageTemplateEnum.E.getRemarks(); |
| | | json = json.replace("车牌号", plateNumber); |
| | | json = json.replace("预计驶离时间", LocalDateTime.now().toString()); |
| | | } |
| | | |
| | | vo.setTemplateId(MessageTemplateEnum.A.getTemplateId()); |
| | | json = MessageTemplateEnum.A.getRemarks(); |
| | | json = json.replace("订单编号", order.getOrderNumber()); |
| | | json = json.replace("订单状态", status.getDesc() ); |
| | | json = json.replace("订单状态", OrderStatusEnum.getDescByCode(order.getStatus())); |
| | | json = json.replace("备注", ""); |
| | | json = json.replace("时间", LocalDateTime.now().toString()); |
| | | vo.setPage(""); |
| | | }else{ |
| | | // status = 状态 0=已取消,1=待接单,2=待出发,3=接驾中,4=待客上车,5=乘客已上车,6=服务中,7=待客下车,8=待付款,9=待评价,10=已完成 |
| | | List<Integer> statusList = Arrays.asList(OrderStatusEnum.CANCELED.getCode(),OrderStatusEnum.PASSENGER_BOARDED.getCode(), |
| | | OrderStatusEnum.SEE_OFF_GUESTS.getCode(),OrderStatusEnum.APPROVAL_REJECTION.getCode(),OrderStatusEnum.PENDING_PAYMENT.getCode(), |
| | | OrderStatusEnum.COMPLETED.getCode()) ; |
| | | if(statusList.contains(order.getStatus())){ |
| | | String bz = getBz(order); |
| | | vo.setTemplateId(MessageTemplateEnum.A.getTemplateId()); |
| | | json = MessageTemplateEnum.A.getRemarks(); |
| | | json = json.replace("订单编号", order.getOrderNumber()); |
| | | json = json.replace("订单状态", status.getDesc() ); |
| | | json = json.replace("订单状态", OrderStatusEnum.getDescByCode(order.getStatus())); |
| | | json = json.replace("备注", bz); |
| | | json = json.replace("时间", LocalDateTime.now().toString()); |
| | | vo.setPage(""); |
| | | } |
| | | |
| | | } |
| | | if(StrUtil.isNotBlank(json)){ |
| | | vo.setJson(json); |
| | | subscribeMessageClient.sendSubscribeMessage(vo); |
| | | } |
| | | |
| | | } |
| | | |
| | | private static String getBz(XpOrder order) { |
| | | String bz = ""; |
| | | if(order.getStatus() == OrderStatusEnum.CANCELED.getCode()){ |
| | | bz = "订单已"+OrderStatusEnum.CANCELED.getDesc(); |
| | | }else if(order.getStatus() == OrderStatusEnum.PASSENGER_BOARDED.getCode()){ |
| | | bz = OrderStatusEnum.PASSENGER_BOARDED.getDesc(); |
| | | }else if(order.getStatus() == OrderStatusEnum.SEE_OFF_GUESTS.getCode()){ |
| | | bz = OrderStatusEnum.SEE_OFF_GUESTS.getDesc(); |
| | | }else if(order.getStatus() == OrderStatusEnum.APPROVAL_REJECTION.getCode()){ |
| | | bz = "订单"+OrderStatusEnum.APPROVAL_REJECTION.getDesc(); |
| | | }else if(order.getStatus() == OrderStatusEnum.PENDING_PAYMENT.getCode()){ |
| | | bz = "订单"+OrderStatusEnum.PENDING_PAYMENT.getDesc(); |
| | | }else if(order.getStatus() == OrderStatusEnum.COMPLETED.getCode()){ |
| | | bz = "订单"+OrderStatusEnum.COMPLETED.getDesc(); |
| | | } |
| | | return bz; |
| | | } |
| | | |
| | | |
| | | } |