Merge remote-tracking branch 'origin/master'
| | |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | |
| | | } |
| | | return R.ok(xpAppUserService.listByIds(ids)); |
| | | } |
| | | |
| | | /** |
| | | * 根据ID查询用户信息 |
| | | * |
| | | * @param id 用户ID |
| | | * @return 用户信息 |
| | | */ |
| | | @GetMapping("/inner/appUser/queryOperationsUserById") |
| | | public R<XpAppUser> queryOperationsUserById(@RequestParam Long id){ |
| | | return R.ok(xpAppUserService.getById(id)); |
| | | } |
| | | } |
| | |
| | | import com.xiaopeng.account.service.IXpOperationsUserService; |
| | | import com.xiaopeng.api.dto.account.XpOperationsUserQueryDTO; |
| | | import com.xiaopeng.api.model.account.XpOperationsUser; |
| | | import com.xiaopeng.api.vo.account.XpOperationsUserProfileVO; |
| | | import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.common.security.service.TokenService; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.util.CollectionUtils; |
| | |
| | | |
| | | private final XpOperationsRoleMapper xpOperationsRoleMapper; |
| | | |
| | | private final TokenService tokenService; |
| | | |
| | | @Autowired |
| | | public XpOperationsUserController(IXpOperationsUserService xpOperationsUserService, XpOperationsRoleMapper xpOperationsRoleMapper) { |
| | | public XpOperationsUserController(IXpOperationsUserService xpOperationsUserService, XpOperationsRoleMapper xpOperationsRoleMapper, TokenService tokenService) { |
| | | this.xpOperationsUserService = xpOperationsUserService; |
| | | this.xpOperationsRoleMapper = xpOperationsRoleMapper; |
| | | this.tokenService = tokenService; |
| | | } |
| | | |
| | | /** |
| | |
| | | public R<PageInfo<XpOperationsUserWithRoleVO>> getList(@RequestBody XpOperationsUserPageQuery query) { |
| | | return R.ok(xpOperationsUserService.selectUserPageByConditions(query)); |
| | | } |
| | | |
| | | /** |
| | | * 获取运维人员个人中心首页信息 |
| | | * |
| | | * @return 个人中心首页信息 |
| | | */ |
| | | @ApiOperation(value = "获取个人中心首页信息", tags = {"运维APP-个人中心"}) |
| | | @GetMapping("/api/v1/userInfo") |
| | | public R<XpOperationsUserProfileVO> getProfile() { |
| | | // 通过TokenService获取当前登录用户ID |
| | | Long currentUserId = tokenService.getLoginOperationUser().getId(); |
| | | |
| | | // 查询个人中心信息 |
| | | XpOperationsUserProfileVO profile = xpOperationsUserService.getUserProfile(currentUserId); |
| | | |
| | | return R.ok(profile); |
| | | } |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.xiaopeng.account.model.query.XpOperationsUserPageQuery; |
| | | import com.xiaopeng.api.model.account.XpOperationsUser; |
| | | import com.xiaopeng.api.vo.account.XpOperationsUserProfileVO; |
| | | import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | |
| | |
| | | * @return |
| | | */ |
| | | PageInfo<XpOperationsUserWithRoleVO> selectUserPageByConditions(XpOperationsUserPageQuery query); |
| | | |
| | | /** |
| | | * 获取运维人员个人中心首页信息 |
| | | * |
| | | * @param userId 运维人员ID |
| | | * @return 个人中心首页信息 |
| | | */ |
| | | XpOperationsUserProfileVO getUserProfile(Long userId); |
| | | } |
| | |
| | | LoginUser userInfo = new LoginUser(); |
| | | userInfo.setSysUser(user); |
| | | userInfo.setRoles(roles); |
| | | userInfo.setPermissions(menuPermission.stream().map(SysMenu::getPath).collect(Collectors.toSet())); |
| | | userInfo.setPermissions(menuPermission.stream().map(item->item.getId().toString()).collect(Collectors.toSet())); |
| | | |
| | | if (StringUtils.isNull(user)) { |
| | | recordLogService.recordLogininfor(request, null, username, Constants.LOGIN_FAIL_STATUS, "登录用户不存在"); |
| | |
| | | package com.xiaopeng.account.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.xiaopeng.account.mapper.XpOperationsRoleMapper; |
| | | import com.xiaopeng.account.mapper.XpOperationsUserMapper; |
| | | import com.xiaopeng.account.model.query.XpOperationsUserPageQuery; |
| | | import com.xiaopeng.account.service.IXpOperationsUserService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.feignClient.maintenance.XpClockInClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient; |
| | | import com.xiaopeng.api.model.account.XpOperationsUser; |
| | | import com.xiaopeng.api.model.vehicleConvoy.XpFleet; |
| | | import com.xiaopeng.api.vo.account.FleetInfoVO; |
| | | import com.xiaopeng.api.vo.account.XpOperationsUserProfileVO; |
| | | import com.xiaopeng.api.vo.account.XpOperationsUserWithRoleVO; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.exception.ServiceException; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | * @author mitao |
| | | * @since 2026-02-03 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class XpOperationsUserServiceImpl extends ServiceImpl<XpOperationsUserMapper, XpOperationsUser> implements IXpOperationsUserService { |
| | | |
| | | @Autowired |
| | | private XpFleetOperationsClient xpFleetOperationsClient; |
| | | |
| | | @Autowired |
| | | private XpFleetClient xpFleetClient; |
| | | |
| | | @Autowired |
| | | private XpOperationsRoleMapper xpOperationsRoleMapper; |
| | | |
| | | @Autowired |
| | | private XpClockInClient xpClockInClient; |
| | | |
| | | @Override |
| | | public List<XpOperationsUser> listByName(String nickName) { |
| | | if (StringUtils.isBlank(nickName)) { |
| | |
| | | pageInfo.setTotal(page.getTotal()); |
| | | return pageInfo; |
| | | } |
| | | |
| | | @Override |
| | | public XpOperationsUserProfileVO getUserProfile(Long userId) { |
| | | // 1. 查询运维人员基础信息 |
| | | XpOperationsUser user = this.getById(userId); |
| | | if (user == null) { |
| | | throw new ServiceException("运维人员不存在"); |
| | | } |
| | | |
| | | XpOperationsUserProfileVO vo = new XpOperationsUserProfileVO(); |
| | | BeanUtils.copyProperties(user, vo); |
| | | |
| | | // 2. 查询关联的车队ID列表(一个运维人员只能关联一个车队) |
| | | R<List<Long>> fleetIdsResult = xpFleetOperationsClient.getFleetIdsByUserId(userId); |
| | | List<Long> fleetIds = fleetIdsResult.getData(); |
| | | |
| | | if (!CollectionUtils.isEmpty(fleetIds)) { |
| | | // 3. 获取第一个车队ID(因为只能关联一个车队) |
| | | Long fleetId = fleetIds.get(0); |
| | | |
| | | // 4. 查询车队信息 |
| | | R<List<XpFleet>> fleetsResult = xpFleetClient.listByIds(Collections.singletonList(fleetId)); |
| | | List<XpFleet> fleets = fleetsResult.getData(); |
| | | |
| | | if (!CollectionUtils.isEmpty(fleets)) { |
| | | XpFleet fleet = fleets.get(0); |
| | | |
| | | // 5. 组装车队信息VO |
| | | FleetInfoVO fleetVO = new FleetInfoVO(); |
| | | fleetVO.setFleetId(fleet.getId()); |
| | | fleetVO.setFleetName(fleet.getFleetName()); |
| | | fleetVO.setProvinceName(fleet.getProvinceName()); |
| | | fleetVO.setCityName(fleet.getCityName()); |
| | | fleetVO.setDistrictName(fleet.getDistrictName()); |
| | | |
| | | // 6. 查询并填充车队队长信息 |
| | | if (fleet.getOperationsUserId() != null) { |
| | | XpOperationsUser captain = this.getById(fleet.getOperationsUserId()); |
| | | if (captain != null) { |
| | | fleetVO.setCaptainId(captain.getId()); |
| | | fleetVO.setFleetLeaderName(captain.getNickName()); |
| | | fleetVO.setFleetLeaderAvatar(captain.getAvatar()); |
| | | } |
| | | } |
| | | |
| | | vo.setFleet(fleetVO); |
| | | } |
| | | } |
| | | |
| | | // 7. 查询是否上班中 |
| | | try { |
| | | R<Boolean> workStatusResult = xpClockInClient.checkWorkStatus(userId); |
| | | if (workStatusResult != null && workStatusResult.getCode() == 200) { |
| | | vo.setWorkFlag(workStatusResult.getData()); |
| | | } else { |
| | | // 调用失败,默认 false |
| | | vo.setWorkFlag(false); |
| | | log.warn("查询用户上班状态失败, userId={}, result={}", userId, workStatusResult); |
| | | } |
| | | } catch (Exception e) { |
| | | // 异常情况,默认 false |
| | | vo.setWorkFlag(false); |
| | | log.error("查询用户上班状态异常, userId={}", userId, e); |
| | | } |
| | | |
| | | // 8. 查询角色信息 |
| | | List<XpOperationsUserWithRoleVO> userWithRoles = xpOperationsRoleMapper.selectUsersWithRoleByIds(Collections.singletonList(userId)); |
| | | if (!CollectionUtils.isEmpty(userWithRoles)) { |
| | | List<XpOperationsUserProfileVO.RoleInfoVO> roleVOs = userWithRoles.stream() |
| | | .filter(r -> r.getRoleId() != null) |
| | | .map(r -> { |
| | | XpOperationsUserProfileVO.RoleInfoVO roleVO = new XpOperationsUserProfileVO.RoleInfoVO(); |
| | | roleVO.setRoleId(r.getRoleId()); |
| | | roleVO.setRoleName(r.getRoleName()); |
| | | return roleVO; |
| | | }) |
| | | .collect(Collectors.toList()); |
| | | vo.setRoles(roleVOs); |
| | | } |
| | | |
| | | return vo; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.factory.maintenance; |
| | | |
| | | import com.xiaopeng.api.feignClient.maintenance.XpClockInClient; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * 打卡服务降级工厂 |
| | | * |
| | | * @author mitao |
| | | * @date 2026-02-14 |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class XpClockInClientFallbackFactory implements FallbackFactory<XpClockInClient> { |
| | | |
| | | @Override |
| | | public XpClockInClient create(Throwable cause) { |
| | | return new XpClockInClient() { |
| | | @Override |
| | | public R<Boolean> checkWorkStatus(Long appUserId) { |
| | | log.error("调用打卡服务检查上班状态失败, appUserId={}, 异常: {}", |
| | | appUserId, cause.getMessage(), cause); |
| | | // 降级策略: 返回 false (未上班状态) |
| | | return R.ok(false); |
| | | } |
| | | }; |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | @Override |
| | | public R<XpPricing> getPricingByCityId(Long cityId) { |
| | | public R<XpPricing> getPricingByCid(Long cityId) { |
| | | log.error("根据开通城市获取定价失败", cause); |
| | | return R.ok(null); |
| | | } |
| | |
| | | package com.xiaopeng.api.factory.vehicleConvoy; |
| | | |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetOperationsClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.cloud.openfeign.FallbackFactory; |
| | |
| | | log.error("根据人员id 获取所属车队下关联的车辆ids,人员ID: {},原因: {}", vehicleId, cause.getMessage()); |
| | | return R.ok(new ArrayList<>()); |
| | | } |
| | | |
| | | @Override |
| | | public R<List<Long>> getFleetIdsByUserId(Long userId) { |
| | | log.error("根据人员ID获取车队信息失败 {}", cause.getMessage()); |
| | | return R.fail("根据人员ID获取车队信息失败"); |
| | | } |
| | | }; |
| | | } |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.feignClient.maintenance; |
| | | |
| | | import com.xiaopeng.api.factory.maintenance.XpClockInClientFallbackFactory; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.dto.ServiceNameConstantsEnum; |
| | | import org.springframework.cloud.openfeign.FeignClient; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | |
| | | /** |
| | | * 打卡服务 Feign 客户端 |
| | | * |
| | | * @author mitao |
| | | * @date 2026-02-14 |
| | | */ |
| | | @FeignClient( |
| | | contextId = "XpClockInClient", |
| | | value = ServiceNameConstantsEnum.XP_MAINTENANCE_SERVICE, |
| | | fallbackFactory = XpClockInClientFallbackFactory.class |
| | | ) |
| | | public interface XpClockInClient { |
| | | |
| | | /** |
| | | * 检查用户是否上班中 |
| | | * @param appUserId 用户ID |
| | | * @return true=上班中, false=未上班或已下班 |
| | | */ |
| | | @GetMapping("/inner/clock-in/checkWorkStatus") |
| | | R<Boolean> checkWorkStatus(@RequestParam("appUserId") Long appUserId); |
| | | } |
| | |
| | | R<Integer> getWaitTime(@RequestBody LonLatDto dto); |
| | | |
| | | // 根据开通城市 获取定价 |
| | | @GetMapping("/base/v1/mini-program/getPricingByCityId/{cityId}") |
| | | R<XpPricing> getPricingByCityId(@PathVariable("cityId")Long cityId); |
| | | @GetMapping("/base/v1/mini-program/getPricingByCid") |
| | | R<XpPricing> getPricingByCid(@RequestParam Long cityId); |
| | | |
| | | /** |
| | | * 保存XpGeoQueryEntity |
| | |
| | | */ |
| | | @GetMapping("/inner/fleet-operations/getVehicleByUserId/{id}") |
| | | R<List<Long>> getVehicleByUserId(@PathVariable("id") Long id); |
| | | |
| | | /** |
| | | * 根据运维人员ID查询关联的车队ID列表 |
| | | * |
| | | * @param userId 运维人员ID |
| | | * @return 车队ID列表 |
| | | */ |
| | | @GetMapping("/inner/fleet-operations/getFleetIdsByUserId/{userId}") |
| | | R<List<Long>> getFleetIdsByUserId(@PathVariable("userId") Long userId); |
| | | } |
| | |
| | | package com.xiaopeng.api.model.operation; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import java.time.LocalDateTime; |
| | | import java.io.Serializable; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.xiaopeng.common.core.web.domain.BaseModel; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import lombok.experimental.Accessors; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | @ApiModelProperty(value = "菜单类型 1=管理端 2=运维app") |
| | | private Integer groupType; |
| | | |
| | | |
| | | @ApiModelProperty(value = "子节点") |
| | | @TableField(exist = false) |
| | | List<SysMenu> children; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.vo.account; |
| | | |
| | | import lombok.Data; |
| | | import java.io.Serializable; |
| | | |
| | | /** |
| | | * 车队信息VO(用于个人中心) |
| | | * |
| | | * @author mitao |
| | | * @date 2026-02-14 |
| | | */ |
| | | @Data |
| | | public class FleetInfoVO implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 车队ID |
| | | */ |
| | | private Long fleetId; |
| | | |
| | | /** |
| | | * 车队名称 |
| | | */ |
| | | private String fleetName; |
| | | |
| | | /** |
| | | * 省份名称 |
| | | */ |
| | | private String provinceName; |
| | | |
| | | /** |
| | | * 城市名称 |
| | | */ |
| | | private String cityName; |
| | | |
| | | /** |
| | | * 区县名称 |
| | | */ |
| | | private String districtName; |
| | | |
| | | /** |
| | | * 车队队长ID |
| | | */ |
| | | private Long captainId; |
| | | |
| | | /** |
| | | * 车队队长名称 |
| | | */ |
| | | private String fleetLeaderName; |
| | | |
| | | /** |
| | | * 车队队长头像 |
| | | */ |
| | | private String fleetLeaderAvatar; |
| | | } |
| New file |
| | |
| | | package com.xiaopeng.api.vo.account; |
| | | |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 运维人员个人中心首页VO |
| | | * |
| | | * @author mitao |
| | | * @date 2026-02-14 |
| | | */ |
| | | @Data |
| | | public class XpOperationsUserProfileVO implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 用户ID |
| | | */ |
| | | @ApiModelProperty(value = "ID") |
| | | private Long id; |
| | | |
| | | /** |
| | | * 名称 |
| | | */ |
| | | @ApiModelProperty(value = "姓名") |
| | | private String nickName; |
| | | |
| | | /** |
| | | * 唯一编号 |
| | | */ |
| | | @ApiModelProperty(value = "唯一编号") |
| | | private String uniqueNumber; |
| | | |
| | | /** |
| | | * 头像 |
| | | */ |
| | | @ApiModelProperty(value = "头像") |
| | | private String avatar; |
| | | |
| | | /** |
| | | * 手机号 |
| | | */ |
| | | @ApiModelProperty(value = "手机号") |
| | | private String phoneNumber; |
| | | |
| | | /** |
| | | * 状态(1=启用 0=禁用) |
| | | */ |
| | | private Integer status; |
| | | |
| | | /** |
| | | * 关联车队信息 |
| | | */ |
| | | @ApiModelProperty(value = "关联车队信息") |
| | | private FleetInfoVO fleet; |
| | | |
| | | /** |
| | | * 角色列表 |
| | | */ |
| | | @ApiModelProperty(value = "角色列表") |
| | | private List<RoleInfoVO> roles; |
| | | |
| | | @ApiModelProperty("是否上班中") |
| | | private Boolean workFlag; |
| | | |
| | | /** |
| | | * 角色信息VO |
| | | */ |
| | | @Data |
| | | public static class RoleInfoVO implements Serializable { |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 角色ID |
| | | */ |
| | | @ApiModelProperty(value = "角色ID") |
| | | private Long roleId; |
| | | |
| | | /** |
| | | * 角色名称 |
| | | */ |
| | | @ApiModelProperty(value = "角色名称") |
| | | private String roleName; |
| | | } |
| | | } |
| | |
| | | com.xiaopeng.api.factory.automaticPilot.XpuVimClientFallbackFactory |
| | | com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory |
| | | com.xiaopeng.api.factory.maintenance.XpCheckLocationClientFallbackFactory |
| | | com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory |
| | | com.xiaopeng.api.factory.automaticPilot.OrderMissionClientFallbackFactory |
| | | com.xiaopeng.api.factory.maintenance.XpClockInClientFallbackFactory |
| | |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.xiaopeng.api.dto.map.OrderRouteAndFareDto; |
| | | import com.xiaopeng.api.dto.operation.LonLatDto; |
| | | import com.xiaopeng.api.dto.operation.ReassignOrderDTO; |
| | | import com.xiaopeng.api.dto.order.PopOrderDto; |
| | | import com.xiaopeng.api.dto.pushMessage.PushOrderRouteDTO; |
| | | import com.xiaopeng.api.dto.pushMessage.SendMessageDTO; |
| | | import com.xiaopeng.api.feignClient.map.XpMapClient; |
| | | import com.xiaopeng.api.feignClient.operation.ReassignOrderClient; |
| | | import com.xiaopeng.api.feignClient.order.XpOrderClient; |
| | | import com.xiaopeng.api.feignClient.pushMessage.XpPushMessageClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.UserVehicleClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; |
| | | import com.xiaopeng.api.model.order.XpOrder; |
| | | import com.xiaopeng.api.model.vehicleConvoy.XpVehicle; |
| | |
| | | import com.xiaopeng.api.vo.automaticPilot.proto.up.xpuNavigationRequest.NavigationRequest; |
| | | import com.xiaopeng.api.vo.automaticPilot.proto.up.xpuNavigationRequest.OrderPhase; |
| | | import com.xiaopeng.api.vo.operation.RouteVo; |
| | | import com.xiaopeng.api.vo.vehicleConvoy.CancelTaxiPassengerVo; |
| | | import com.xiaopeng.automaticPilot.util.XPUUtil; |
| | | import com.xiaopeng.common.core.constant.CacheConstants; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.enums.OrderStatusEnum; |
| | | import com.xiaopeng.common.core.enums.PushMessageTypeEnum; |
| | |
| | | |
| | | @Resource |
| | | private XpPushMessageClient xpPushMessageClient; |
| | | @Resource |
| | | private ReassignOrderClient reassignOrderClient; |
| | | @Resource |
| | | private UserVehicleClient userVehicleClient; |
| | | |
| | | |
| | | |
| | |
| | | case SYSTEM_STATUS_XPU: |
| | | log.info("XPU发布系统运行状态 (XPU→云端):{}", message); |
| | | systemStatusXpu(msgContent); |
| | | systemStatusXpuAndReassign(msgContent); |
| | | break; |
| | | case XPU_NAVIGATION_REQUEST: |
| | | log.info("XPU向云端请求导航 (XPU→云端):{}", message); |
| | |
| | | //开始处理消息对应的业务逻辑 |
| | | SystemRuntimeStatus systemRuntimeStatus = msgContent.getContent_detail().getObject("systemRuntimeStatus", SystemRuntimeStatus.class); |
| | | AbnormalReason abnormalReason = systemRuntimeStatus.getSystem_status().getAbnormal_reason(); |
| | | // 存储车辆挡位 |
| | | GearLevel gearLevel = systemRuntimeStatus.getVehicle_status().getDynamic_status().getGear_level(); |
| | | int value = gearLevel.getValue(); |
| | | redisTemplate.opsForValue().set(CacheConstants.GEAR_LEVEL + systemRuntimeStatus.getVehicle_status().getStatic_info().getVin(), value); |
| | | |
| | | // 判断是否为故障状态 |
| | | if (abnormalReason == AbnormalReason.FAULT) { |
| | | log.info("车辆故障,开始处理故障"); |
| | |
| | | * 订单车辆轨迹,禁止用于其他用处 |
| | | */ |
| | | public static final String ORDER_ROUTE_CAPTURE = "order_route_capture:"; |
| | | /** |
| | | * 车辆挡位 |
| | | */ |
| | | public static final String GEAR_LEVEL = "gear_level:"; |
| | | } |
| | |
| | | import com.xiaopeng.maintenance.vo.ShiftAttendanceVO; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | /** |
| | | * <p> |
| | | * 打卡明细 前端控制器 |
| | | * </p> |
| | | * 打卡管理 Controller |
| | | * |
| | | * @author xiaochen |
| | | * @since 2026-02-06 |
| | | * @author mitao |
| | | * @date 2026-02-14 |
| | | */ |
| | | @Api(tags = "打卡明细") |
| | | @Slf4j |
| | | @RestController |
| | | @RequestMapping("") |
| | | public class XpClockInController { |
| | |
| | | public R<Boolean> verifyRange(@RequestBody VerifyRangeDTO dto) { |
| | | return R.ok(xpClockInService.verifyRange(dto)); |
| | | } |
| | | private IXpClockInService clockInService; |
| | | |
| | | @ApiOperation(value = "上班打卡", tags = "运维APP-排班与考勤") |
| | | @PostMapping("/api/v1/clock/clockIn") |
| | |
| | | return xpClockInService.clockIn(dto); |
| | | } |
| | | |
| | | /** |
| | | * 检查用户是否上班中(内部接口) |
| | | */ |
| | | @GetMapping("/inner/clock-in/checkWorkStatus") |
| | | @ApiOperation(value = "检查用户是否上班中", tags = {"内部接口"}) |
| | | public R<Boolean> checkWorkStatus(@RequestParam("appUserId") Long appUserId) { |
| | | log.info("检查用户上班状态, appUserId={}", appUserId); |
| | | Boolean workFlag = clockInService.checkWorkStatus(appUserId); |
| | | return R.ok(workFlag); |
| | | } |
| | | |
| | | @ApiOperation(value = "下班打卡", tags = "运维APP-排班与考勤") |
| | | @PostMapping("/api/v1/clock/clockOut") |
| | | public R<String> clockOut(@RequestBody ClockDTO dto) { |
| | |
| | | |
| | | import com.xiaopeng.api.model.maintenance.XpClockIn; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | */ |
| | | public interface XpClockInMapper extends BaseMapper<XpClockIn> { |
| | | |
| | | /** |
| | | * 查询用户当天的打卡记录 |
| | | * @param tableSuffix 分表后缀 (0-9) |
| | | * @param appUserId 用户ID |
| | | * @param startTime 开始时间 (当天00:00:00) |
| | | * @param endTime 结束时间 (当天23:59:59) |
| | | * @return 打卡记录列表 |
| | | */ |
| | | List<XpClockIn> selectTodayClockInRecords( |
| | | @Param("tableSuffix") Integer tableSuffix, |
| | | @Param("appUserId") Long appUserId, |
| | | @Param("startTime") LocalDateTime startTime, |
| | | @Param("endTime") LocalDateTime endTime |
| | | ); |
| | | } |
| | |
| | | * @return |
| | | */ |
| | | R<String> clockOut(ClockDTO dto); |
| | | /** |
| | | * 检查用户是否上班中 |
| | | * @param appUserId 用户ID |
| | | * @return true=上班中(只打了上班卡), false=未上班或已下班 |
| | | */ |
| | | Boolean checkWorkStatus(Long appUserId); |
| | | } |
| | |
| | | package com.xiaopeng.maintenance.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.model.maintenance.XpCheckLocation; |
| | | import com.xiaopeng.api.model.maintenance.XpClockIn; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.xiaopeng.api.model.maintenance.XpShift; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.utils.DateUtils; |
| | |
| | | import com.xiaopeng.maintenance.mapper.XpClockInMapper; |
| | | import com.xiaopeng.maintenance.mapper.XpShiftMapper; |
| | | import com.xiaopeng.maintenance.service.IXpClockInService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import java.util.Objects; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.LocalTime; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | * @author mitao |
| | | * @since 2026-02-03 |
| | | */ |
| | | @Slf4j |
| | | @Service |
| | | public class XpClockInServiceImpl extends ServiceImpl<XpClockInMapper, XpClockIn> implements IXpClockInService { |
| | | |
| | |
| | | this.updateById(xpClockIn); |
| | | return R.ok(); |
| | | } |
| | | @Override |
| | | public Boolean checkWorkStatus(Long appUserId) { |
| | | if (appUserId == null) { |
| | | log.warn("检查上班状态失败: appUserId 为空"); |
| | | return false; |
| | | } |
| | | |
| | | try { |
| | | // 1. 计算当天时间范围 |
| | | LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN); // 00:00:00 |
| | | LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX); // 23:59:59 |
| | | |
| | | // 2. 计算分表后缀 (userId % 10) |
| | | Integer tableSuffix = (int) (appUserId % 10); |
| | | |
| | | // 3. 查询当天打卡记录 |
| | | List<XpClockIn> records = baseMapper.selectTodayClockInRecords( |
| | | tableSuffix, appUserId, startTime, endTime |
| | | ); |
| | | |
| | | // 4. 判断上班状态 |
| | | if (records == null || records.isEmpty()) { |
| | | // 无打卡记录 = 未上班 |
| | | log.debug("用户 {} 今日无打卡记录, workFlag=false", appUserId); |
| | | return false; |
| | | } else if (records.size() == 1) { |
| | | // 1条记录 = 已上班未下班 |
| | | log.debug("用户 {} 今日打卡1次, workFlag=true", appUserId); |
| | | return true; |
| | | } else { |
| | | // 2条及以上记录 = 已下班 |
| | | log.debug("用户 {} 今日打卡{}次, workFlag=false", appUserId, records.size()); |
| | | return false; |
| | | } |
| | | |
| | | } catch (Exception e) { |
| | | log.error("检查用户上班状态异常, appUserId={}", appUserId, e); |
| | | // 异常情况返回 false |
| | | return false; |
| | | } |
| | | } |
| | | } |
| | |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.xiaopeng.maintenance.mapper.XpClockInMapper"> |
| | | |
| | | <resultMap id="BaseResultMap" type="com.xiaopeng.api.model.maintenance.XpClockIn"> |
| | | <id column="id" property="id" /> |
| | | <result column="app_user_id" property="appUserId" /> |
| | | <result column="clock_time" property="clockTime" /> |
| | | <result column="shift_id" property="shiftId" /> |
| | | <result column="clock_result" property="clockResult" /> |
| | | <result column="clock_lon" property="clockLon" /> |
| | | <result column="clock_lat" property="clockLat" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | </resultMap> |
| | | |
| | | <!-- 查询用户当天的打卡记录 --> |
| | | <select id="selectTodayClockInRecords" resultMap="BaseResultMap"> |
| | | SELECT |
| | | id, app_user_id, clock_time, shift_id, clock_result, |
| | | clock_lon, clock_lat, create_time, update_time |
| | | FROM xp_clock_in_${tableSuffix} |
| | | WHERE app_user_id = #{appUserId} |
| | | AND clock_time BETWEEN #{startTime} AND #{endTime} |
| | | AND clock_result IN (1, 2) |
| | | ORDER BY clock_time ASC |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | <version>4.4.0</version> |
| | | <exclusions> |
| | | <exclusion> |
| | | <artifactId>com.google.guava</artifactId> |
| | | <groupId>guava</groupId> |
| | | <artifactId>guava</artifactId> |
| | | <groupId>com.google.guava</groupId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | |
| | | Locale locale = null; |
| | | if (StringUtils.isEmpty(language)) { |
| | | locale = Locale.getDefault(); |
| | | language = "1"; |
| | | } |
| | | //如果请求不为空 |
| | | //If the request is not empty. |
| | |
| | | package com.xiaopeng.operation.controller; |
| | | |
| | | import com.xiaopeng.api.model.operation.SysMenu; |
| | | import com.xiaopeng.api.model.operation.SysMenuBak; |
| | | import com.xiaopeng.api.model.operation.SysMenus; |
| | | import com.xiaopeng.common.core.constant.UserConstants; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.utils.StringUtils; |
| | | import com.xiaopeng.common.core.web.controller.BaseController; |
| | | import com.xiaopeng.common.core.web.domain.AjaxResult; |
| | | import com.xiaopeng.common.log.annotation.Log; |
| | | import com.xiaopeng.common.log.enums.BusinessType; |
| | | import com.xiaopeng.common.security.annotation.Logical; |
| | | import com.xiaopeng.common.security.annotation.RequiresPermissions; |
| | | import com.xiaopeng.common.security.service.TokenService; |
| | | import com.xiaopeng.common.security.utils.SecurityUtils; |
| | | import com.xiaopeng.operation.service.ISysMenuBakService; |
| | | import com.xiaopeng.operation.service.ISysMenuService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import javax.annotation.Resource; |
| | | import java.util.ArrayList; |
| | |
| | | * @author ruoyi |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/menu") |
| | | @Api(tags = "菜单模块") |
| | | public class SysMenuController extends BaseController |
| | | { |
| | | @Resource |
| | |
| | | * @param menuIds 菜单ID列表 |
| | | * @return 菜单列表 |
| | | */ |
| | | @PostMapping("/getMenusByMenuIds") |
| | | @ApiOperation(value = "根据菜单ID列表获取菜单列表", notes = "内部服务调用") |
| | | @PostMapping("/menu/getMenusByMenuIds") |
| | | @ApiOperation(value = "根据菜单ID列表获取菜单列表", tags = {"内部服务调用"}) |
| | | public R<List<SysMenu>> getMenusByMenuIds(@RequestBody List<Long> menuIds) { |
| | | if (menuIds == null || menuIds.isEmpty()) { |
| | | return R.ok(new ArrayList<>()); |
| | |
| | | List<SysMenu> menus = menuService.selectMenusByMenuIds(menuIds); |
| | | return R.ok(menus); |
| | | } |
| | | |
| | | /** |
| | | * 获取全部菜单树 |
| | | * @return |
| | | */ |
| | | @GetMapping("/api/v1/getMenuTree") |
| | | @ApiOperation(value = "获取全部菜单树", tags = {"管理后台-权限组设置"}) |
| | | public R<List<SysMenu>> getMenuTree() { |
| | | List<SysMenu> menus = menuService.getMenuTree(); |
| | | return R.ok(menus); |
| | | } |
| | | |
| | | @GetMapping("/api/v1/getPermission") |
| | | @ApiOperation(value = "获取获取当前登录用户菜单列表", tags = {"管理后台-菜单管理"}) |
| | | public R<List<SysMenu>> getPermission(){ |
| | | return R.ok(menuService.getPermission()); |
| | | } |
| | | |
| | | } |
| | |
| | | } |
| | | |
| | | // 根据开通城市 获取定价 |
| | | @GetMapping("/getPricingByCityId/{cityId}") |
| | | @GetMapping("/base/v1/mini-program/getPricingByCityId/{cityId}") |
| | | public R<XpPricing> getPricingByCityId(@PathVariable("cityId") Long cityId) { |
| | | XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one(); |
| | | return R.ok(pricing); |
| | | } |
| | | // 根据开通城市 获取定价 |
| | | @GetMapping("/base/v1/mini-program/getPricingByCid") |
| | | public R<XpPricing> getPricingByCid(@RequestParam Long cityId) { |
| | | XpPricing pricing = xpPricingService.lambdaQuery().eq(XpPricing::getCityId, cityId).last("limit 1").one(); |
| | | return R.ok(pricing); |
| | | } |
| | | |
| | | |
| | | @GetMapping("/base/v1/mini-program/pricing/rules/{openCityId}") |
| | |
| | | @NotNull(message = "定价ID不能为空") |
| | | private Long id; |
| | | |
| | | @ApiModelProperty(value = "基础价格json配置", required = true) |
| | | @ApiModelProperty(value = "基础价格json配置", required = true, notes = "{\n" + |
| | | " \"num3\": \"起步价数值1\",\n" + |
| | | " \"num4\": \"起步价数值2\",\n" + |
| | | " \"num5\": \"起步价数值3\",\n" + |
| | | " \"num6\": \"起步价数值4\",\n" + |
| | | " \"num7\": \"起步价数值5\",\n" + |
| | | " \"num8\": \"起步价数值6\",\n" + |
| | | " \"num9\": \"起步价数值7\",\n" + |
| | | " \"num10\": \"起步价数值8\",\n" + |
| | | " \"num11\": \"起步价数值9\",\n" + |
| | | " \"num12\": \"起步价数值10\",\n" + |
| | | " \"num13\": \"起步价数值11\",\n" + |
| | | " \"num14\": \"起步价数值12\",\n" + |
| | | " \"num15\": \"起步价数值13\",\n" + |
| | | " \"num16\": \"起步价数值14\",\n" + |
| | | " \"num17\": \"起步价数值15\",\n" + |
| | | " \"num18\": \"起步价数值16\",\n" + |
| | | " \"num19\": \"起步价数值17\",\n" + |
| | | " \"num20\": \"起步价数值18\",\n" + |
| | | " \"num21\": \"起步价数值19\",\n" + |
| | | " \"num22\": \"起步价数值20\",\n" + |
| | | " \"num23\": \"起步价数值21\",\n" + |
| | | " \"num24\": \"起步价数值22\",\n" + |
| | | " \"num25\": \"起步价数值23\",\n" + |
| | | " \"num26\": \"起步价数值24\",\n" + |
| | | " \"num27\": \"起步价数值25\",\n" + |
| | | " \"num28\": \"起步价数值26\",\n" + |
| | | " \"num29\": \"起步价数值27\",\n" + |
| | | " \"num30\": \"起步价数值28\",\n" + |
| | | " \"num31\": \"起步价数值29\",\n" + |
| | | " \"num32\": \"起步价数值30\",\n" + |
| | | " \"num33\": \"起步价数值31\",\n" + |
| | | " \"num34\": \"起步价数值32\"\n" + |
| | | "}") |
| | | @NotBlank(message = "基础价格json配置不能为空") |
| | | private String basicPricingJson; |
| | | |
| | | @ApiModelProperty(value = "起步价json配置", required = true) |
| | | @ApiModelProperty(value = "起步价json配置", required = true, notes = "[\n" + |
| | | " {\n" + |
| | | " \"num1\": \"08:00\",\n" + |
| | | " \"num2\": \"10:00\",\n" + |
| | | " \"num3\": \"起步价数值1\",\n" + |
| | | " \"num4\": \"起步价数值2\",\n" + |
| | | " \"num5\": \"起步价数值3\",\n" + |
| | | " \"num6\": \"起步价数值4\",\n" + |
| | | " \"num7\": \"起步价数值5\"\n" + |
| | | " },\n" + |
| | | " {\n" + |
| | | " \"num1\": \"10:00\",\n" + |
| | | " \"num2\": \"12:00\",\n" + |
| | | " \"num3\": \"起步价数值1\",\n" + |
| | | " \"num4\": \"起步价数值2\",\n" + |
| | | " \"num5\": \"起步价数值3\",\n" + |
| | | " \"num6\": \"起步价数值4\",\n" + |
| | | " \"num7\": \"起步价数值5\"\n" + |
| | | " },\n" + |
| | | " {\n" + |
| | | " \"num1\": \"14:00\",\n" + |
| | | " \"num2\": \"16:00\",\n" + |
| | | " \"num3\": \"起步价数值1\",\n" + |
| | | " \"num4\": \"起步价数值2\",\n" + |
| | | " \"num5\": \"起步价数值3\",\n" + |
| | | " \"num6\": \"起步价数值4\",\n" + |
| | | " \"num7\": \"起步价数值5\"\n" + |
| | | " },\n" + |
| | | " {\n" + |
| | | " \"num1\": \"18:00\",\n" + |
| | | " \"num2\": \"20:00\",\n" + |
| | | " \"num3\": \"起步价数值1\",\n" + |
| | | " \"num4\": \"起步价数值2\",\n" + |
| | | " \"num5\": \"起步价数值3\",\n" + |
| | | " \"num6\": \"起步价数值4\",\n" + |
| | | " \"num7\": \"起步价数值5\"\n" + |
| | | " }\n" + |
| | | "]") |
| | | @NotBlank(message = "起步价json配置不能为空") |
| | | private String startingFareJson; |
| | | |
| | | @ApiModelProperty(value = "动态价格json配置", required = true) |
| | | @ApiModelProperty(value = "动态价格json配置", required = true, notes = " {\n" + |
| | | " \"num1\": \"起步价数值1\",\n" + |
| | | " \"num2\": \"起步价数值2\",\n" + |
| | | " \"num3\": \"起步价数值3\",\n" + |
| | | " \"num4\": \"起步价数值4\",\n" + |
| | | " \"num5\": \"起步价数值5\",\n" + |
| | | " \"num6\": \"起步价数值6\",\n" + |
| | | " \"num7\": \"起步价数值7\",\n" + |
| | | " \"num8\": \"起步价数值8\",\n" + |
| | | " \"num9\": \"起步价数值9\"\n" + |
| | | " }") |
| | | @NotBlank(message = "动态价格json配置不能为空") |
| | | private String dynamicPricingJson; |
| | | } |
| | |
| | | @ApiModelProperty(value = "三轮推单时间") |
| | | private Integer threePushTime; |
| | | |
| | | @ApiModelProperty(value = "四轮推单距离") |
| | | private Double fourPushDistance; |
| | | |
| | | @ApiModelProperty(value = "四轮推单时间") |
| | | private Integer fourPushTime; |
| | | |
| | | @ApiModelProperty(value = "五轮推单距离") |
| | | private Double fivePushDistance; |
| | | |
| | | @ApiModelProperty(value = "五轮推单时间") |
| | | private Integer fivePushTime; |
| | | |
| | | @ApiModelProperty(value = "规则类型 1=单手举手 2=多手举手") |
| | | private Integer ruleType; |
| | | |
| | |
| | | * @return 菜单列表 |
| | | */ |
| | | List<SysMenu> selectMenusByMenuIds(List<Long> menuIds); |
| | | |
| | | /** |
| | | *获取全部菜单树 |
| | | * @return |
| | | */ |
| | | List<SysMenu> getMenuTree(); |
| | | |
| | | /** |
| | | *获取获取当前登录用户菜单列表 |
| | | * @return |
| | | */ |
| | | List<SysMenu> getPermission(); |
| | | } |
| | |
| | | import com.xiaopeng.operation.service.ISysMenuService; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | |
| | | // 使用 MyBatis-Plus 的 listByIds 方法批量查询 |
| | | return this.listByIds(menuIds); |
| | | } |
| | | |
| | | @Override |
| | | public List<SysMenu> getMenuTree() { |
| | | List<SysMenu> all = list(); |
| | | if (all == null || all.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 1) 按 parentId 分组(M/C/F 都先放进去,后面再按类型筛) |
| | | Map<Long, List<SysMenu>> childrenMap = all.stream() |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.groupingBy(m -> m.getParentId() == null ? 0L : m.getParentId())); |
| | | |
| | | // 2) 找到根目录:通常 parentId = 0 或 null 的目录(M) |
| | | List<SysMenu> roots = childrenMap.getOrDefault(0L, Collections.emptyList()).stream() |
| | | .filter(m -> "M".equals(m.getMenuType())) |
| | | .sorted(Comparator.comparing(SysMenu::getOrderNum, Comparator.nullsLast(Integer::compareTo))) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 3) 递归挂载:M 下挂 C;C 下挂 F(也允许 C 下还有 C,或 M 下还有 M,按你的数据结构来) |
| | | for (SysMenu root : roots) { |
| | | fillChildren(root, childrenMap); |
| | | } |
| | | |
| | | return roots; |
| | | } |
| | | |
| | | private void fillChildren(SysMenu node, Map<Long, List<SysMenu>> childrenMap) { |
| | | Long id = node.getId(); |
| | | if (id == null) { |
| | | node.setChildren(Collections.emptyList()); |
| | | return; |
| | | } |
| | | |
| | | List<SysMenu> rawChildren = childrenMap.getOrDefault(id, Collections.emptyList()); |
| | | if (rawChildren.isEmpty()) { |
| | | node.setChildren(Collections.emptyList()); |
| | | return; |
| | | } |
| | | |
| | | // 根据你的层级规则过滤子节点: |
| | | // - M 只能挂 M/C(看你业务,很多系统 M 下是 C) |
| | | // - C 只能挂 F(按钮)或(有些系统 C 下还能挂 C) |
| | | // - F 一般不挂任何子节点 |
| | | String type = node.getMenuType(); |
| | | |
| | | List<SysMenu> children = rawChildren.stream() |
| | | .filter(Objects::nonNull) |
| | | .filter(child -> { |
| | | if ("M".equals(type)) { |
| | | return "M".equals(child.getMenuType()) || "C".equals(child.getMenuType()); |
| | | } |
| | | if ("C".equals(type)) { |
| | | return "F".equals(child.getMenuType()); // 若允许 C 下还有 C,可改为:return "C".equals(...) || "F".equals(...) |
| | | } |
| | | return false; // F 不再往下挂 |
| | | }) |
| | | .sorted(Comparator.comparing(SysMenu::getOrderNum, Comparator.nullsLast(Integer::compareTo))) |
| | | .collect(Collectors.toList()); |
| | | |
| | | node.setChildren(children); |
| | | |
| | | // 继续递归 |
| | | for (SysMenu child : children) { |
| | | fillChildren(child, childrenMap); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public List<SysMenu> getPermission() { |
| | | |
| | | return Collections.emptyList(); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public NearestVo nearest(NearestDto dto) { |
| | | // 获取所有的站点信息 |
| | | List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId)); |
| | | NearestVo nearestVo = new NearestVo(); |
| | | List<NearestNextVo> nearestNextVos = new ArrayList<>(); |
| | | LocalTime now = LocalTime.now(); |
| | | // 走缓存 |
| | | Object orderDistanceObj = redisService.getCacheObject(CacheConstants.NEAR_POINT_DISTANCE); |
| | | Integer orderDistance; |
| | |
| | | XpSystemSetting xpSystemSetting = xpSystemSettingMapper.selectOne(null); |
| | | orderDistance = xpSystemSetting.getNearPointDistance(); |
| | | } |
| | | // 获取所有的站点信息 |
| | | List<XpPoint> list = list(new LambdaQueryWrapper<XpPoint>().eq(XpPoint::getOperationStatus, 2).eq(XpPoint::getPointType,1).isNotNull(XpPoint::getElectronicAreaId) |
| | | // 添加距离计算字段 |
| | | .apply("ST_Distance_Sphere(point({0}, {1}), point(point_lon, point_lat)) <= {2}", |
| | | dto.getLongitude(), dto.getLatitude(), orderDistance)); |
| | | NearestVo nearestVo = new NearestVo(); |
| | | List<NearestNextVo> nearestNextVos = new ArrayList<>(); |
| | | LocalTime now = LocalTime.now(); |
| | | |
| | | |
| | | for (XpPoint xpPoint : list) { |
| | | // 判断是否在时间段内 |
| | |
| | | } |
| | | return 0; |
| | | } |
| | | /** |
| | | * 2非工作日 0工作日 1节假日 3调休日 |
| | | * @return |
| | | */ |
| | | public Integer isHolidayToday(LocalDate now) { |
| | | Object cacheObject = redisService.getCacheObject(CacheConstants.IS_DAY + now); |
| | | // 判断缓存里面是否有节假日算法 |
| | | if (cacheObject!=null) { |
| | | return Integer.valueOf(cacheObject.toString()); |
| | | } |
| | | String result = HttpUtils.sendGet("https://publicapi.xiaoai.me/holiday/day?date=" +now); |
| | | |
| | | // String result = HttpUtils.sendGet("https://publicapi.xiaoai.me/holiday/day?date=2026-01-04"); |
| | | JSONObject jsonObject = JSONObject.parseObject(result); |
| | | Object data = jsonObject.get("data"); |
| | | JSONArray parse = JSONArray.parse(data.toString()); |
| | | for (int i = 0; i < parse.size(); i++) { |
| | | JSONObject jsonObject1 = parse.getJSONObject(i); |
| | | String daytype = jsonObject1.get("daytype").toString(); |
| | | if (daytype.equals("0")) { |
| | | redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS); |
| | | return 0; |
| | | } else if (daytype.equals("1")) { |
| | | redisService.setCacheObject(CacheConstants.IS_DAY + now, "1", 1l, TimeUnit.DAYS); |
| | | return 1; |
| | | } else if (daytype.equals("2")) { |
| | | redisService.setCacheObject(CacheConstants.IS_DAY + now, "0", 1l, TimeUnit.DAYS); |
| | | return 0; |
| | | } |
| | | } |
| | | return 0; |
| | | } |
| | | } |
| | |
| | | @ApiModelProperty(value = "三轮推单时间") |
| | | private Integer threePushTime; |
| | | |
| | | @ApiModelProperty(value = "四轮推单距离") |
| | | private Double fourPushDistance; |
| | | |
| | | @ApiModelProperty(value = "四轮推单时间") |
| | | private Integer fourPushTime; |
| | | |
| | | @ApiModelProperty(value = "五轮推单距离") |
| | | private Double fivePushDistance; |
| | | |
| | | @ApiModelProperty(value = "五轮推单时间") |
| | | private Integer fivePushTime; |
| | | |
| | | @ApiModelProperty(value = "规则类型 1=单手举手 2=多手举手") |
| | | private Integer ruleType; |
| | | |
| | |
| | | <artifactId>unified-verification</artifactId> |
| | | <version>1.5.2-v2</version> |
| | | </dependency> |
| | | <!-- <dependency>--> |
| | | <dependency> |
| | | <groupId>org.quartz-scheduler</groupId> |
| | | <artifactId>quartz</artifactId> |
| | | </dependency> |
| | | <!-- <dependency>--> |
| | | <!-- <groupId>org.apache.shardingsphere</groupId>--> |
| | | <!-- <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>--> |
| | | <!-- <version>5.0.0</version>--> |
| New file |
| | |
| | | package com.xiaopeng.order.config; |
| | | |
| | | import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; |
| | | import com.xiaopeng.common.core.utils.StringUtils; |
| | | import com.xiaopeng.common.security.utils.SecurityUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.ibatis.reflection.MetaObject; |
| | | import org.springframework.context.annotation.Configuration; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * 数据更新操作处理 |
| | | * 自动填充 createBy、updateBy、createTime、updateTime 字段 |
| | | * |
| | | * @author mitao |
| | | * @date 2026-02-14 |
| | | */ |
| | | @Slf4j |
| | | @Configuration |
| | | public class DataUpdateHandlerConfig implements MetaObjectHandler { |
| | | |
| | | /** |
| | | * 新增数据执行 |
| | | * |
| | | * @param metaObject 元对象 |
| | | */ |
| | | @Override |
| | | public void insertFill(MetaObject metaObject) { |
| | | String userName = ""; |
| | | try { |
| | | userName = SecurityUtils.getLoginUser().getSysUser().getNickName(); |
| | | log.info("DataUpdateInterceptor insertFill"); |
| | | } catch (Exception e) { |
| | | log.error("DataUpdateInterceptor insertFill error:{}", e.getMessage()); |
| | | } |
| | | |
| | | this.setFieldValByName("createBy", userName, metaObject); |
| | | this.setFieldValByName("updateBy", userName, metaObject); |
| | | this.setFieldValByName("createTime", LocalDateTime.now(), metaObject); |
| | | this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); |
| | | } |
| | | |
| | | /** |
| | | * 修改数据执行 |
| | | * |
| | | * @param metaObject 元对象 |
| | | */ |
| | | @Override |
| | | public void updateFill(MetaObject metaObject) { |
| | | String userName = ""; |
| | | try { |
| | | userName = SecurityUtils.getLoginUser().getSysUser().getNickName(); |
| | | log.info("DataUpdateInterceptor updateFill"); |
| | | } catch (Exception e) { |
| | | log.error("DataUpdateInterceptor updateFill error:{}", e.getMessage()); |
| | | } |
| | | |
| | | this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject); |
| | | this.setFieldValByName("updateBy", userName, metaObject); |
| | | } |
| | | } |
| | |
| | | String ORDER_REASSIGN_NEW_VEHICLE_OPERATING_STATUS_INVALID = "order.reassign.new.vehicle.operating.status.invalid"; |
| | | String ORDER_REASSIGN_NEW_VEHICLE_NOT_IN_ACCEPT_MODE = "order.reassign.new.vehicle.not.in.accept.mode"; |
| | | String ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT = "order.reassign.new.vehicle.not.start.accept"; |
| | | String ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE = "order.reassign.new.vehicle.not.free"; |
| | | } |
| | | |
| | |
| | | import com.xiaopeng.order.model.query.OrderEvaluatePageQuery; |
| | | import com.xiaopeng.order.model.vo.OrderEvaluatePageVO; |
| | | import com.xiaopeng.order.service.IXpOrderEvaluateService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/api/v1/orderEvaluate") |
| | | @Api(tags = "订单评价管理") |
| | | public class XpOrderEvaluateController { |
| | | |
| | | @Resource |
| | |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | |
| | | public abstract class AbstractJob implements Job{ |
| | | public abstract class AbstractJob implements Job { |
| | | protected Logger logger = LoggerFactory.getLogger(getClass()); |
| | | |
| | | @Override |
| | |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpFleetVehicleClient; |
| | | import com.xiaopeng.api.feignClient.vehicleConvoy.XpVehicleClient; |
| | | import com.xiaopeng.api.model.account.XpAppUser; |
| | | import com.xiaopeng.api.model.operation.XpGeoQueryEntity; |
| | | import com.xiaopeng.api.model.operation.XpFeedbackType; |
| | | import com.xiaopeng.api.model.operation.XpGeoQueryEntity; |
| | | import com.xiaopeng.api.model.operation.XpPoint; |
| | | import com.xiaopeng.api.model.operation.XpPricing; |
| | | import com.xiaopeng.api.model.order.*; |
| | |
| | | import com.xiaopeng.order.utils.TrajectoryGenerator; |
| | | import com.xiaopeng.order.utils.holiday.HolidayUtil; |
| | | import com.xiaopeng.order.vo.OrderDetailVo; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.shardingsphere.transaction.core.TransactionType; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.data.domain.Sort; |
| | | import org.springframework.data.mongodb.core.MongoTemplate; |
| | | import org.springframework.data.mongodb.core.geo.GeoJsonPoint; |
| | | import org.springframework.data.mongodb.core.query.Criteria; |
| | | import org.springframework.data.mongodb.core.query.Query; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | wrapper.orderByDesc(XpOrder::getCreateTime); |
| | | |
| | | // 2. 分页查询 |
| | | PageInfo<XpOrder> pageInfo = new PageInfo<>(query.getPageNum(), query.getPageSize()); |
| | | PageInfo<XpOrder> orderPage = this.page(pageInfo, wrapper); |
| | | Page<XpOrder> page = new Page<>(query.getPageNum(), query.getPageSize()); |
| | | Page<XpOrder> pageResult = this.page(page, wrapper); |
| | | |
| | | if (CollectionUtils.isEmpty(orderPage.getRecords())) { |
| | | if (CollectionUtils.isEmpty(pageResult.getRecords())) { |
| | | PageInfo<XpOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize()); |
| | | resultPage.setTotal(0L); |
| | | resultPage.setRecords(new ArrayList<>()); |
| | |
| | | Set<Long> userIds = new HashSet<>(); |
| | | Set<Long> vehicleIds = new HashSet<>(); |
| | | Set<Long> fleetIds = new HashSet<>(); |
| | | for (XpOrder order : orderPage.getRecords()) { |
| | | for (XpOrder order : pageResult.getRecords()) { |
| | | if (Objects.nonNull(order.getAppUserId())) { |
| | | userIds.add(order.getAppUserId()); |
| | | } |
| | |
| | | Map<Long, XpFleet> fleetMap = batchGetFleets(fleetIds); |
| | | |
| | | // 5. 组装 VO |
| | | List<XpOrderPageVO> voList = orderPage.getRecords().stream() |
| | | List<XpOrderPageVO> voList = pageResult.getRecords().stream() |
| | | .map(order -> convertToPageVO(order, userMap, vehicleMap, fleetMap)) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 6. 返回分页结果 |
| | | PageInfo<XpOrderPageVO> resultPage = new PageInfo<>(query.getPageNum(), query.getPageSize()); |
| | | resultPage.setTotal(orderPage.getTotal()); |
| | | resultPage.setTotal(pageResult.getTotal()); |
| | | resultPage.setRecords(voList); |
| | | return resultPage; |
| | | } |
| | |
| | | if (Objects.isNull(newVehicle.getAcceptOrTestStatus()) || newVehicle.getAcceptOrTestStatus() != 1) { |
| | | throw new ServiceException("新车辆未开始接单", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_START_ACCEPT); |
| | | } |
| | | if (Objects.isNull(newVehicle.getState()) || newVehicle.getState() != 0) { |
| | | throw new ServiceException("新车辆已接单,请重新选择", I18nKeyConstant.ORDER_REASSIGN_NEW_VEHICLE_NOT_FREE); |
| | | } |
| | | |
| | | // 6. 保存原车辆和车队信息 |
| | | Long oldVehicleId = order.getVehicleId(); |
| | |
| | | |
| | | log.info("订单改派成功,订单ID: {}, 原车辆ID: {}, 新车辆ID: {}, 原车队ID: {}, 新车队ID: {}, 操作人: {}", |
| | | orderId, oldVehicleId, newVehicleId, oldFleetId, newFleetId, SecurityUtils.getUsername()); |
| | | //推送消息 |
| | | } |
| | | |
| | | @Override |
| | |
| | | userVehicleClient.cancelTaxiPassenger(cancelTaxiPassengerVo); |
| | | } |
| | | // 根据订单所属开通城市id 查询定价 |
| | | R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCityId(order.getCityId()); |
| | | R<XpPricing> pricingByCityId = xpPricingClient.getPricingByCid(order.getCityId()); |
| | | XpPricing pricing = pricingByCityId.getData(); |
| | | if (pricing == null) { |
| | | throw new ServiceException("当前城市未配置定价规则", I18nKeyConstant.ORDER_PRICING_NOT_EXISTS); |
| | |
| | | order.reassign.new.vehicle.operating.status.invalid=新车辆运营状态异常,无法接单 |
| | | order.reassign.new.vehicle.not.in.accept.mode=新车辆当前不在接单模式 |
| | | order.reassign.new.vehicle.not.start.accept=新车辆未开始接单 |
| | | order.reassign.new.vehicle.not.free=新车辆已接单,请重新选择 |
| | | order.pricing.not.exists=当前城市未配置定价规则 |
| | |
| | | order.reassign.new.vehicle.operating.status.invalid=The operation status of the new vehicle is abnormal and orders cannot be accepted |
| | | order.reassign.new.vehicle.not.in.accept.mode=The new vehicle is currently not in order-taking mode |
| | | order.reassign.new.vehicle.not.start.accept=The new vehicle has not started taking orders yet |
| | | order.reassign.new.vehicle.not.free=The new vehicle has been ordered. Please choose again |
| | | welcome.message=Welcome to our application! |
| | | order.pricing.not.exists=The current city has not been configured with pricing rules |
| | |
| | | order.reassign.new.vehicle.operating.status.invalid=新车辆运营状态异常,无法接单 |
| | | order.reassign.new.vehicle.not.in.accept.mode=新车辆当前不在接单模式 |
| | | order.reassign.new.vehicle.not.start.accept=新车辆未开始接单 |
| | | order.reassign.new.vehicle.not.free=新车辆已接单,请重新选择 |
| | | welcome.message=欢迎来到我们的应用程序! |
| | | order.pricing.not.exists=当前城市未配置定价规则 |
| | |
| | | <version>4.4.0</version> |
| | | <exclusions> |
| | | <exclusion> |
| | | <artifactId>com.google.guava</artifactId> |
| | | <groupId>guava</groupId> |
| | | <artifactId>guava</artifactId> |
| | | <groupId>com.google.guava</groupId> |
| | | </exclusion> |
| | | </exclusions> |
| | | </dependency> |
| | |
| | | String FLEET_OPERATION_USER_BOUND_2 = "fleet.operation.user.bound.v2"; |
| | | String FLEET_VEHICLE_BOUND_V1 = "fleet.vehicle.bound.v1"; |
| | | String FLEET_VEHICLE_BOUND_V2 = "fleet.vehicle.bound.v2"; |
| | | |
| | | // ========== 车辆开关车门 ========== |
| | | String VEHICLE_NOT_P_GEAR = "vehicle.not.p.gear"; |
| | | } |
| | |
| | | .list().stream().map(XpFleetVehicle::getVehicleId).collect(Collectors.toList()); |
| | | return R.ok(vehicleIds); |
| | | } |
| | | |
| | | /** |
| | | * 根据运维人员ID查询关联的车队ID列表(内部接口) |
| | | * |
| | | * @param userId 运维人员ID |
| | | * @return 车队ID列表 |
| | | */ |
| | | @ApiOperation(value = "根据运维人员ID查询关联的车队ID列表", tags = {"内部接口"}) |
| | | @GetMapping("/inner/fleet-operations/getFleetIdsByUserId/{userId}") |
| | | public R<List<Long>> getFleetIdsByUserId(@PathVariable("userId") Long userId) { |
| | | List<Long> fleetIds = xpFleetOperationsService.lambdaQuery() |
| | | .eq(XpFleetOperations::getOperationsUserId, userId) |
| | | .list() |
| | | .stream() |
| | | .map(XpFleetOperations::getFleetId) |
| | | .distinct() |
| | | .collect(Collectors.toList()); |
| | | return R.ok(fleetIds); |
| | | } |
| | | } |
| | |
| | | import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandEnum; |
| | | import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandParamVo; |
| | | import com.xiaopeng.api.vo.vehicleConvoy.VehiclesCommandVo; |
| | | import com.xiaopeng.common.core.constant.CacheConstants; |
| | | import com.xiaopeng.common.core.domain.R; |
| | | import com.xiaopeng.common.core.exception.ServiceException; |
| | | import com.xiaopeng.common.core.web.page.PageInfo; |
| | | import com.xiaopeng.common.redis.service.RedisService; |
| | | import com.xiaopeng.vehicleConvoy.constant.I18nKeyConstant; |
| | | import com.xiaopeng.vehicleConvoy.mapper.XpVehicleMapper; |
| | | import com.xiaopeng.vehicleConvoy.model.dto.*; |
| | |
| | | |
| | | @Autowired |
| | | private IXpVehicleAnnualRecordService xpVehicleAnnualRecordService; |
| | | |
| | | @Resource |
| | | private XpuVinClient xpuVinClient; |
| | | @Autowired |
| | | private RedisService redisService; |
| | | |
| | | @Override |
| | | public List<XpVehicle> listByIds(List<Long> ids) { |
| | |
| | | if("door".equals(dto.getDoorType())){ |
| | | vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_UNLOCK); |
| | | }else { |
| | | // 开启后备箱 判断是否为P档 |
| | | Integer gearLevel = redisService.getCacheObject(CacheConstants.GEAR_LEVEL + xpVehicle.getVehicleVin()); |
| | | if(gearLevel == null || gearLevel != 4){ |
| | | throw new ServiceException("车辆非P档,无法开启后备箱", I18nKeyConstant.VEHICLE_NOT_P_GEAR); |
| | | } |
| | | vehiclesCommandVo.setCommandCode(VehiclesCommandEnum.DOOR_TRUNK_OPEN); |
| | | } |
| | | try{ |
| | |
| | | vehicle.import.data.empty=Import data is empty! |
| | | vehicle.operation.info.incomplete=The following information is incomplete and cannot start operation: {0} |
| | | vehicle.batch.operation.condition.not.met=The following vehicles do not meet operating conditions: {0} |
| | | vehicle.time.expired=The following dates have expired, please enter valid dates: {0} |
| | | vehicle.time.expired=The following dates have expired, please enter valid dates: {0} |
| | | vehicle.not.p.gear=The trunk cannot be opened when the vehicle is not in P gear |
| | |
| | | vehicle.import.data.empty=导入数据为空! |
| | | vehicle.operation.info.incomplete=以下信息未完善,无法开始运营:{0} |
| | | vehicle.batch.operation.condition.not.met=以下车辆不满足运营条件:{0} |
| | | vehicle.time.expired=以下时间已过期,请填写有效日期:{0} |
| | | vehicle.time.expired=以下时间已过期,请填写有效日期:{0} |
| | | vehicle.not.p.gear=车辆非P档,无法开启后备箱 |