Merge remote-tracking branch 'origin/main'

This commit is contained in:
attiya 2025-12-23 21:20:21 +08:00
commit e7fa7c2317
27 changed files with 663 additions and 107 deletions

1
.gitignore vendored
View File

@ -1,4 +1,5 @@
target/
logs/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

View File

@ -16,10 +16,11 @@ import java.util.TimeZone;
public class EbikeTenantFactory implements TenantFactory {
public Object[] getTenantIds() {
try {
if (StpUtil.isLogin()) {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
Object attribute = attributes.getAttribute("tenantId", RequestAttributes.SCOPE_REQUEST);
if (attribute != null) {
String id = (String) StpUtil.getLoginId();
Object object = StpUtil.getSessionByLoginId(id).get(id);
ObjectMapper objectMapper = new ObjectMapper()
@ -35,7 +36,10 @@ public class EbikeTenantFactory implements TenantFactory {
break;
}
}
long tenantId = Long.parseLong(attribute.toString());
long tenantId = 0;
if (attribute != null) {
tenantId = Long.parseLong(attribute.toString());
}
//系统管理员
if (isSysAdmin) {
return null;
@ -46,5 +50,9 @@ public class EbikeTenantFactory implements TenantFactory {
}
return null;
} catch (Exception e) {
return null;
}
}
}

View File

@ -164,6 +164,10 @@ public class SafeOrderExpirationListener {
.where(EBIKE_BIKE_ORDER.HANDLE_STATE.eq(OrderHandleState.PROCESSED))
.where(EBIKE_BIKE_ORDER.DISPATCH_STATE.eq(OrderDispatchState.PROCESSED));
EbikeBikeOrder bikeOrder = orderService.getOne(query);
if (bikeOrder == null) {
log.error("处理工单业务异常,不存在满足处理条件的工单: orderId={}", orderId);
return;
}
bikeOrder.setDispatchState(OrderDispatchState.INEFFECTIVE);
orderService.updateById(bikeOrder);
} catch (Exception e) {

View File

@ -276,6 +276,17 @@ public class EbikeBikeOrderController {
}
/**
* 有效调度工单统计
*
* @return 操作结果
*/
@GetMapping("effectiveDispatchOrder")
public JsonResult<List<EffectiveDispatchOrderDto>> effectiveDispatchOrder(@RequestBody EffectiveDispatchOrderVo effectiveDispatchOrderVo) {
List<EffectiveDispatchOrderDto> list = ebikeBikeOrderService.effectiveDispatchOrder(effectiveDispatchOrderVo);
return JsonResult.success(list);
}
/**
* 工单看板
*

View File

@ -13,6 +13,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Objects;
import static com.cdzy.operations.model.entity.table.EbikeSiteTableDef.EBIKE_SITE;
@ -72,8 +73,10 @@ public class EbikeSiteController {
* @return 所有数据
*/
@GetMapping("list")
public JsonResult<?> list() {
List<EbikeSite> list = ebikeSiteService.list();
public JsonResult<?> list(Long regionId) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(EBIKE_SITE.REGION_ID.eq(regionId, Objects.nonNull(regionId)));
List<EbikeSite> list = ebikeSiteService.list(queryWrapper);
return JsonResult.success(list);
}

View File

@ -0,0 +1,33 @@
package com.cdzy.operations.model.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author attiya
* @since 2025-12-23
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EffectiveDispatchOrderDto {
/**
* 用户名
*/
private String username;
/**
* 用户名
*/
private String staffId;
/**
* 有效调度工单总数
*/
private Integer count;
}

View File

@ -0,0 +1,29 @@
package com.cdzy.operations.model.vo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.LocalDateTime;
/**
* @author attiya
* @since 2025-12-23
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class EffectiveDispatchOrderVo {
/**
* 开始时间
*/
private LocalDateTime startTime;
/**
* 结束时间
*/
private LocalDateTime endTime;
}

View File

@ -1,9 +1,6 @@
package com.cdzy.operations.service;
import com.cdzy.operations.model.dto.EbikeBikeOrderInfoDto;
import com.cdzy.operations.model.dto.EbikeOrderBikeListDto;
import com.cdzy.operations.model.dto.EbikeOrderBikeInfoDto;
import com.cdzy.operations.model.dto.EbikeOrderBulletinBoardInfo;
import com.cdzy.operations.model.dto.*;
import com.cdzy.operations.model.entity.EbikeBikeOrder;
import com.cdzy.operations.model.entity.EbikeRegion;
import com.cdzy.operations.model.vo.*;
@ -133,4 +130,11 @@ public interface EbikeBikeOrderService extends IService<EbikeBikeOrder> {
* 工单看板
*/
EbikeOrderBulletinBoardInfo bulletinBoard();
/**
* 有效调度工单统计
* @param effectiveDispatchOrderVo 筛选信息
* @return 统计结果
*/
List<EffectiveDispatchOrderDto> effectiveDispatchOrder(EffectiveDispatchOrderVo effectiveDispatchOrderVo);
}

View File

@ -6,10 +6,7 @@ import com.cdzy.common.ex.EbikeException;
import com.cdzy.common.model.dto.ResGPSDto;
import com.cdzy.operations.enums.*;
import com.cdzy.operations.mapper.*;
import com.cdzy.operations.model.dto.EbikeBikeOrderInfoDto;
import com.cdzy.operations.model.dto.EbikeOrderBikeInfoDto;
import com.cdzy.operations.model.dto.EbikeOrderBikeListDto;
import com.cdzy.operations.model.dto.EbikeOrderBulletinBoardInfo;
import com.cdzy.operations.model.dto.*;
import com.cdzy.operations.model.entity.*;
import com.cdzy.operations.model.vo.*;
import com.cdzy.operations.service.EbikeBikeOrderService;
@ -34,7 +31,11 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import static com.cdzy.operations.model.entity.table.EbikeBatteryInfoTableDef.EBIKE_BATTERY_INFO;
import static com.cdzy.operations.model.entity.table.EbikeBikeInfoTableDef.EBIKE_BIKE_INFO;
@ -104,6 +105,9 @@ public class EbikeBikeOrderServiceImpl extends ServiceImpl<EbikeBikeOrderMapper,
@Resource
private EbikeOrderPartMapper ebikeOrderPartMapper;
@Resource
private EbikeDispatchConfigurationMapper ebikeDispatchConfigurationMapper;
@Override
public void createBatterySwapOrder(String ecuSn) {
QueryWrapper queryWrapper = QueryWrapper.create()
@ -200,6 +204,7 @@ public class EbikeBikeOrderServiceImpl extends ServiceImpl<EbikeBikeOrderMapper,
.orderCode(snowFlakeIDKeyGenerator.nextId())
.orderType(BikeOrderType.DISPATCH)
.operatorId(bikeInfo.getOperatorId())
.dispatchState(OrderDispatchState.PROCESSED)
.build();
this.mapper.insert(ebikeBikeOrder);
//TODO:车辆状态
@ -566,7 +571,7 @@ public class EbikeBikeOrderServiceImpl extends ServiceImpl<EbikeBikeOrderMapper,
.where(EBIKE_BIKE_ORDER.ORDER_TYPE.eq(BikeOrderType.DISPATCH));
EbikeBikeOrder bikeOrder = getOne(queryWrapper);
if (bikeOrder == null) {
throw new EbikeException("该工单不存在或已作废");
throw new EbikeException("该工单不存在或已完成、已作废");
}
Long receiverId = bikeOrder.getReceiverId();
long loginId = StpUtil.getLoginIdAsLong();
@ -579,6 +584,7 @@ public class EbikeBikeOrderServiceImpl extends ServiceImpl<EbikeBikeOrderMapper,
}
bikeOrder.setSiteId(doneDispatchOrderVo.getSiteId());
bikeOrder.setHandleState(OrderHandleState.PROCESSED);
bikeOrder.setRemarks(doneDispatchOrderVo.getRemarks());
List<String> fileUrls = doneDispatchOrderVo.getFileUrls();
List<EbikeOrderFile> list = new ArrayList<>();
@ -596,7 +602,11 @@ public class EbikeBikeOrderServiceImpl extends ServiceImpl<EbikeBikeOrderMapper,
ebikeOrderFileMapper.insertBatch(list);
}
//TODO:车辆状态
bikeOrder.setHandleAt(LocalDateTime.now());
updateById(bikeOrder);
queryWrapper.clear();
EbikeDispatchConfiguration dispatchConfiguration = ebikeDispatchConfigurationMapper.selectOneByQuery(queryWrapper);
redisUtil.saveDispatchOrder(bikeOrder.getOrderId(),bikeOrder,dispatchConfiguration!=null?dispatchConfiguration.getDispatchDuration():24L, TimeUnit.HOURS);
}
@Override
@ -753,6 +763,27 @@ public class EbikeBikeOrderServiceImpl extends ServiceImpl<EbikeBikeOrderMapper,
return null;
}
@Override
public List<EffectiveDispatchOrderDto> effectiveDispatchOrder(EffectiveDispatchOrderVo effectiveDispatchOrderVo) {
if (effectiveDispatchOrderVo.getStartTime() == null) {
effectiveDispatchOrderVo.setStartTime(LocalDateTime.now().toLocalDate().withDayOfMonth(1).atStartOfDay());
}
if (effectiveDispatchOrderVo.getEndTime() == null) {
effectiveDispatchOrderVo.setEndTime(LocalDateTime.now().toLocalDate().withDayOfMonth(LocalDate.now().lengthOfMonth()).atTime(LocalTime.MAX));
}
QueryWrapper queryWrapper = QueryWrapper.create()
.select(QueryMethods.count().as(EffectiveDispatchOrderDto::getCount),EBIKE_BIKE_ORDER.RECEIVER_ID.as(EffectiveDispatchOrderDto::getStaffId))
.where(EBIKE_BIKE_ORDER.HANDLE_AT.between(effectiveDispatchOrderVo.getStartTime(), effectiveDispatchOrderVo.getEndTime()))
.where(EBIKE_BIKE_ORDER.ORDER_TYPE.eq(BikeOrderType.DISPATCH))
.where(EBIKE_BIKE_ORDER.DISPATCH_STATE.eq(OrderDispatchState.EFFECTIVE))
.groupBy(EBIKE_BIKE_ORDER.RECEIVER_ID)
.orderBy("count",true);
return ebikeBikeOrderMapper.selectListByQueryAs(queryWrapper, EffectiveDispatchOrderDto.class);
}
EbikeBikeInfo checkBikeCode(String bikeCode) {
QueryWrapper queryWrapper = QueryWrapper.create()
.where(EBIKE_BIKE_INFO.BIKE_CODE.eq(bikeCode))

View File

@ -117,8 +117,15 @@ minio:
show-url: https://www.cdzhuojing.cn/file
logging:
level:
root: INFO
org.springframework.web: WARN
org.mybatis: DEBUG # 查看MyBatis SQL语句
file:
name: logs/${spring.application.name}.log # 输出到文件(默认追加)
pattern:
dateformat: yyyy-MM-dd HH:mm:ss.SSS # 包含毫秒
console: "%d{yyyy-MM-dd HH:mm:ss} - %highlight(%-5level) [%thread] %cyan(%logger{36}) : %msg%n"
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
geo-coding:
api-url: https://restapi.amap.com/v3/geocode

View File

@ -1,14 +1,20 @@
package com.cdzy.operations;
import com.cdzy.operations.utils.RedisUtil;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class EbikeOperationsApplicationTests {
@Resource
RedisUtil redisUtil;
@Test
void contextLoads() {
redisUtil.releaseDispatchLock("bike:dispatch:lock:360967408603103232");
}

View File

@ -16,10 +16,11 @@ import java.util.TimeZone;
public class EbikeTenantFactory implements TenantFactory {
public Object[] getTenantIds() {
try {
if (StpUtil.isLogin()) {
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
if (attributes != null) {
Object attribute = attributes.getAttribute("tenantId", RequestAttributes.SCOPE_REQUEST);
if (attribute != null) {
String id = (String) StpUtil.getLoginId();
Object object = StpUtil.getSessionByLoginId(id).get(id);
ObjectMapper objectMapper = new ObjectMapper()
@ -35,7 +36,10 @@ public class EbikeTenantFactory implements TenantFactory {
break;
}
}
long tenantId = Long.parseLong(attribute.toString());
long tenantId = 0;
if (attribute != null) {
tenantId = Long.parseLong(attribute.toString());
}
//系统管理员
if (isSysAdmin) {
return null;
@ -46,5 +50,9 @@ public class EbikeTenantFactory implements TenantFactory {
}
return null;
} catch (Exception e) {
return null;
}
}
}

View File

@ -0,0 +1,36 @@
package com.cdzy.user.controller;
import com.cdzy.common.model.response.JsonResult;
import com.cdzy.user.model.dto.EbikeReportRecordDto;
import com.cdzy.user.service.EbikeReportRecordService;
import jakarta.annotation.Resource;
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;
/**
* 用户上报记录 控制层
*
* @author yanglei
* @since 2025-12-23 16:51
*/
@RestController
@RequestMapping("/ebikeReportRecord")
public class EbikeReportRecordController {
@Resource
private EbikeReportRecordService reportRecordService;
/**
* 保存用户上报记录
*
* @param reportRecordDto 用户上报记录参数
*/
@PostMapping("/saveReportRecord")
public JsonResult<?> saveReportRecord(@RequestBody EbikeReportRecordDto reportRecordDto) {
reportRecordService.saveReportRecord(reportRecordDto);
return JsonResult.success();
}
}

View File

@ -8,6 +8,7 @@ import com.cdzy.user.model.entity.EbikeOrder;
import com.cdzy.user.model.entity.EbikeUser;
import com.cdzy.user.model.vo.EbikeUserVo;
import com.cdzy.user.service.EbikeOrderService;
import com.cdzy.user.service.EbikeUserRealInfoService;
import com.cdzy.user.service.EbikeUserService;
import com.cdzy.user.utils.RedisUtil;
import com.cdzy.user.utils.VerifyUtil;
@ -42,6 +43,9 @@ public class EbikeUserController {
@Resource
private EbikeOrderService ebikeOrderTransactionService;
@Resource
private EbikeUserRealInfoService ebikeUserRealInfoService;
/**
* 用户微信无感登录
*
@ -97,9 +101,24 @@ public class EbikeUserController {
*/
@PostMapping("/verifyRealName")
public JsonResult<?> verifyRealName(@RequestBody UserValidateDto userValidateDto) {
return verifyUtil.verifyRealName(userValidateDto);
verifyUtil.verifyRealName(userValidateDto);
return JsonResult.success();
}
/**
* 根据用户id校验用户是否已进行实名认证
*
* @param userId 用户id
* @return true 已进行实名验证 false 实名认证失败或未进行实名认证
*/
@GetMapping("/checkUserIsVerify")
public JsonResult<?> checkUserIsVerify(@RequestParam(name = "userId") Long userId) {
boolean result = ebikeUserRealInfoService.checkUserIsVerify(userId);
return JsonResult.success(result);
}
/**
* 注销用户信息
*

View File

@ -0,0 +1,12 @@
package com.cdzy.user.mapper;
import com.cdzy.user.model.entity.EbikeReportRecordFile;
import com.mybatisflex.core.BaseMapper;
/**
* @author yanglei
* @since 2025-12-23 16:50
*/
public interface EbikeReportRecordFileMapper extends BaseMapper<EbikeReportRecordFile> {
}

View File

@ -0,0 +1,12 @@
package com.cdzy.user.mapper;
import com.cdzy.user.model.entity.EbikeReportRecord;
import com.mybatisflex.core.BaseMapper;
/**
* @author yanglei
* @since 2025-12-23 16:50
*/
public interface EbikeReportRecordMapper extends BaseMapper<EbikeReportRecord> {
}

View File

@ -0,0 +1,43 @@
package com.cdzy.user.model.dto;
import com.cdzy.user.handler.PGpointDeserializer;
import com.cdzy.user.handler.PGpointSerializer;
import com.cdzy.user.handler.PGpointTypeHandler;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.mybatisflex.annotation.Column;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.postgresql.geometric.PGpoint;
import java.util.List;
/**
* 用户上报请求参数
*
* @author yanglei
* @since 2025-12-23 16:55
*/
@Data
public class EbikeReportRecordDto {
/**
* 用户id
*/
@NotNull(message = "用户id不能为空")
private Long userId;
/**
* 骑行起始点
*/
@Column(typeHandler = PGpointTypeHandler.class)
@JsonSerialize(using = PGpointSerializer.class)
@JsonDeserialize(using = PGpointDeserializer.class)
@NotNull(message = "上报位置")
private PGpoint location;
/**
* 用户上报文件
*/
private List<EbikeReportRecordFileDto> recordFiles;
}

View File

@ -0,0 +1,29 @@
package com.cdzy.user.model.dto;
import lombok.Data;
/**
* 用户上报文件请求参数
*
* @author yanglei
* @since 2025-11-18 11:26
*/
@Data
public class EbikeReportRecordFileDto {
/**
* 用户上报文件主键id
*/
private Long recordFileId;
/**
* 文件名称
*/
private String fileName;
/**
* 文件地址
*/
private String fileUrl;
}

View File

@ -0,0 +1,82 @@
package com.cdzy.user.model.entity;
import com.cdzy.user.handler.PGpointDeserializer;
import com.cdzy.user.handler.PGpointSerializer;
import com.cdzy.user.handler.PGpointTypeHandler;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.postgresql.geometric.PGpoint;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author yanglei
* @since 2025-12-23 16:44
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("ebike_report_record")
public class EbikeReportRecord implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id
private Long recordId;
/**
* 上报位置
*/
@Column(typeHandler = PGpointTypeHandler.class)
@JsonSerialize(using = PGpointSerializer.class)
@JsonDeserialize(using = PGpointDeserializer.class)
private PGpoint location;
/**
* 处理状态 0-未处理 1-已处理
*/
private Integer recordStatus;
/**
* 创建人
*/
private Long createBy;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新人
*/
private Long updateBy;
/**
* 更新时间
*/
@Column(onUpdateValue = "now()")
private LocalDateTime updateTime;
/**
* 删除状态true表示已删除
*/
private Boolean isDeleted;
}

View File

@ -0,0 +1,76 @@
package com.cdzy.user.model.entity;
import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* @author yanglei
* @since 2025-12-23 16:44
*/
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Table("ebike_report_record_file")
public class EbikeReportRecordFile implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键ID
*/
@Id
private Long recordFileId;
/**
* 用户退款主键id
*/
private Long recordId;
/**
* 文件名称
*/
private String fileName;
/**
* 文件地址
*/
private String fileUrl;
/**
* 创建人
*/
private Long createBy;
/**
* 创建时间
*/
@Column(onInsertValue = "now()")
private LocalDateTime createTime;
/**
* 更新人
*/
private Long updateBy;
/**
* 更新时间
*/
@Column(onUpdateValue = "now()")
private LocalDateTime updateTime;
/**
* 删除状态true表示已删除
*/
private Boolean isDeleted;
}

View File

@ -0,0 +1,15 @@
package com.cdzy.user.service;
import com.cdzy.user.model.dto.EbikeReportRecordDto;
import com.cdzy.user.model.entity.EbikeReportRecord;
import com.mybatisflex.core.service.IService;
/**
* @author yanglei
* @since 2025-12-23 16:53
*/
public interface EbikeReportRecordService extends IService<EbikeReportRecord> {
void saveReportRecord(EbikeReportRecordDto reportRecordDto);
}

View File

@ -19,4 +19,12 @@ public interface EbikeUserRealInfoService extends IService<EbikeUserRealInfo> {
*/
EbikeUserRealInfo getRealInfoByUserId(Long userId);
/**
* 校验用户是否已实名认证成功
*
* @param userId 用户id
* @return true 已实名认证成功 false 实名认证失败
*/
boolean checkUserIsVerify(Long userId);
}

View File

@ -18,6 +18,7 @@ import com.cdzy.user.model.vo.EbikeUserAllOrdersVo;
import com.cdzy.user.service.EbikeOrderDetailService;
import com.cdzy.user.service.EbikeOrderService;
import com.cdzy.user.service.EbikePaymentService;
import com.cdzy.user.service.EbikeUserRealInfoService;
import com.cdzy.user.utils.StringUtils;
import com.ebike.feign.clients.OperationsFeignClient;
import com.ebike.feign.model.dto.FeignEbikeDto;
@ -68,6 +69,9 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
@Resource
private EbikeOrderDetailService ebikeOrderDetailService;
@Resource
private EbikeUserRealInfoService ebikeUserRealInfoService;
@Transactional
@Override
public EbikeOrder saveRide(EbikeUserCyclingDto orderDto) {
@ -83,6 +87,11 @@ public class EbikeOrderServiceImpl extends ServiceImpl<EbikeOrderMapper, EbikeOr
throw new EbikeException("请完成未支付订单后再试");
}
}
// 校验用户是否已进行实名认证
boolean userIsVerify = ebikeUserRealInfoService.checkUserIsVerify(userId);
if (!userIsVerify) {
throw new EbikeException("请先完成实名认证");
}
EbikeBikeInfoVo bikeInfo = queryBikeInfo(orderDto.getBikeCode());
// 创建订单
EbikeOrder order = EbikeOrder.builder()

View File

@ -0,0 +1,55 @@
package com.cdzy.user.service.impl;
import com.cdzy.common.enums.GlobalConstants;
import com.cdzy.user.mapper.EbikeReportRecordFileMapper;
import com.cdzy.user.mapper.EbikeReportRecordMapper;
import com.cdzy.user.model.dto.EbikeFaultFileDto;
import com.cdzy.user.model.dto.EbikeReportRecordDto;
import com.cdzy.user.model.dto.EbikeReportRecordFileDto;
import com.cdzy.user.model.entity.EbikeFaultFile;
import com.cdzy.user.model.entity.EbikeReportRecord;
import com.cdzy.user.model.entity.EbikeReportRecordFile;
import com.cdzy.user.service.EbikeReportRecordService;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author yanglei
* @since 2025-12-23 16:53
*/
@Service
public class EbikeReportRecordServiceImpl extends ServiceImpl<EbikeReportRecordMapper, EbikeReportRecord> implements EbikeReportRecordService {
@Resource
private EbikeReportRecordFileMapper reportRecordFileMapper;
@Transactional
@Override
public void saveReportRecord(EbikeReportRecordDto reportRecordDto) {
EbikeReportRecord ebikeReportRecord = EbikeReportRecord.builder()
.location(reportRecordDto.getLocation())
.recordStatus(GlobalConstants.NUMBER_ZERO)
.createBy(reportRecordDto.getUserId())
.build();
this.save(ebikeReportRecord);
Long recordId = ebikeReportRecord.getRecordId();
List<EbikeReportRecordFileDto> recordFiles = reportRecordDto.getRecordFiles();
if (Objects.nonNull(recordFiles)) {
List<EbikeReportRecordFile> fileEntities = recordFiles.stream()
.map(dto -> EbikeReportRecordFile.builder()
.recordId(recordId)
.fileName(dto.getFileName())
.fileUrl(dto.getFileUrl())
.createBy(reportRecordDto.getUserId())
.build())
.collect(Collectors.toList());
reportRecordFileMapper.insertBatch(fileEntities);
}
}
}

View File

@ -1,5 +1,6 @@
package com.cdzy.user.service.impl;
import com.cdzy.user.enums.RealNameAttestationStatus;
import com.cdzy.user.mapper.EbikeUserRealInfoMapper;
import com.cdzy.user.model.entity.EbikeUserRealInfo;
import com.cdzy.user.service.EbikeUserRealInfoService;
@ -26,4 +27,15 @@ public class EbikeUserRealInfoServiceImpl extends ServiceImpl<EbikeUserRealInfoM
return mapper.selectOneByQuery(queryWrapper);
}
@Override
public boolean checkUserIsVerify(Long userId) {
EbikeUserRealInfo info = this.mapper.selectOneByQuery(
QueryWrapper.create()
.where(EBIKE_USER_REAL_INFO.USER_ID.eq(userId))
);
return info != null &&
info.getAttestationStatus() != null &&
info.getAttestationStatus() == RealNameAttestationStatus.CERTIFIED;
}
}

View File

@ -5,7 +5,7 @@ import com.cdp.product.security.exception.DecryptFailureException;
import com.cdp.product.security.exception.EncryptFailureException;
import com.cdp.product.security.exception.SignFailureException;
import com.cdp.product.security.strategyImpl.SM2StrategyImpl;
import com.cdzy.common.model.response.JsonResult;
import com.cdzy.common.ex.EbikeException;
import com.cdzy.common.secure.SecureComponent;
import com.cdzy.common.secure.SecureSM2Component;
import com.cdzy.user.config.RealNameVerifyConfig;
@ -148,19 +148,16 @@ public class VerifyUtil {
* 验证用户实名
*
* @param userValidateDto 验证用户实名请求
* @return 验证结果
*/
@Transactional
public JsonResult<?> verifyRealName(UserValidateDto userValidateDto) {
public void verifyRealName(UserValidateDto userValidateDto) {
EbikeUserVo userInfo = ebikeUserService.getUserInfoByUserId(userValidateDto.getUserId());
if (userInfo == null) {
log.error("用户{}不存在", userValidateDto.getUserId());
return JsonResult.failed("用户不存在");
throw new EbikeException("用户不存在");
}
// 校验是否已进行验证
Integer realNameStatus = userInfo.getUserRealNameStatus();
if (realNameStatus != null && realNameStatus.equals(RealNameAttestationStatus.CERTIFIED)) {
return JsonResult.failed("用户已实名验证");
throw new EbikeException("用户已实名验证");
}
try {
// 1. 加密数据
@ -177,42 +174,45 @@ public class VerifyUtil {
ebikeRealNameVerifyDto.setTimestamp(encryptedTimestamp);
ebikeRealNameVerifyDto.setSign(sign);
ebikeRealNameVerifyDto.setKey(realNameVerifyConfig.getApiKey());
// 2. 验证用户实名
// 2. 调用第三方实名验证
JsonNode result = httpPost("验证用户实名", REALNAME_VERIFY_URL, ebikeRealNameVerifyDto, null);
log.info("验证用户实名结果: {}", result);
if (result == null) {
log.error("验证用户实名失败");
return JsonResult.failed("验证用户实名失败");
throw new EbikeException("验证用户实名失败");
}
if (result.has("code") && "10000".equals(result.get("code").asText())) {
// 3. 解密结果
String code = result.has("code") ? result.get("code").asText() : "";
if (!"10000".equals(code)) {
String message = result.has("message") ? result.get("message").asText() : "未知错误";
log.error("验证用户实名失败, code: {}, message: {}", code, message);
throw new EbikeException("验证用户实名失败: " + message);
}
// 3. 解密并解析结果
String data = securityContext.decrypt(result.get("data").asText(), realNameVerifyConfig.getClientPrivateKey());
JsonNode jsonData = objectMapper.readTree(data);
if (jsonData.has("result") && "1".equals(jsonData.get("result").asText())) {
// 4. 更新用户实名信息
// 验证成功直接返回
updateRealNameInfo(userValidateDto, true);
return JsonResult.success("验证用户实名成功");
}
log.info("验证用户实名成功");
return;
} else {
// 验证失败
String failReason = jsonData.has("msg") ? jsonData.get("msg").asText() : "实名验证未通过";
log.warn("实名验证未通过,原因: {}", failReason);
}
// 4. 验证失败更新为未认证
updateRealNameInfo(userValidateDto, false);
String errorCode = result.has("code") ? result.get("code").asText() : "未知错误";
String errorMessage = result.has("message") ? result.get("message").asText() : "验证失败";
log.error("验证用户实名失败,{} {}", errorCode, errorMessage);
return JsonResult.failed(errorCode + ", " + errorMessage);
throw new EbikeException("验证用户实名失败");
} catch (EncryptFailureException e) {
log.error("加密失败", e);
return JsonResult.failed("加密失败");
throw new EbikeException("加密失败");
} catch (SignFailureException e) {
log.error("签名失败", e);
return JsonResult.failed("签名失败");
throw new EbikeException("签名失败");
} catch (DecryptFailureException e) {
log.error("结果解密失败", e);
return JsonResult.failed("结果解密失败");
throw new EbikeException("结果解密失败");
} catch (JsonProcessingException e) {
log.error("JSON解析失败", e);
return JsonResult.failed("数据解析失败");
throw new EbikeException("数据解析失败");
}
}
@ -277,7 +277,7 @@ public class VerifyUtil {
private JsonNode httpPost(String func_, String url, EbikeRealNameVerifyDto dto, Map<String, Object> body) {
StringBuilder queryString = new StringBuilder();
appendParam(queryString, "name", dto.getName());
appendParam(queryString, "idCard", dto.getIdCard());
appendParam(queryString, "idcard", dto.getIdCard());
appendParam(queryString, "timestamp", dto.getTimestamp());
appendParam(queryString, "sign", dto.getSign());
appendParam(queryString, "key", dto.getKey());

View File

@ -74,9 +74,12 @@ wechat:
#数据宝
real-name:
api-key: 00fb13c55a660c64299d32aa041d77ad
client-private-key: c437d793ecc0b35845c30aeb7c330baca69dcfcf2a69f6cddccf978a2023ca1f
server-public-key: 03b7affa6dd528711e3d9a853900a712f98bc27678b68a4af50e4381b730de96e5
api-key: 086ba8e74bd22b814af36f12371f8f3e
#客户端私钥
client-private-key: cc1ed364d4c05b9ce0de5c690484c301e00452c1f183f93fa43e5b0ba6a76c99
#服务端公钥
server-public-key: 03a21563bf3d2b43cfd519918a0138ac6cfde1ff3b0e4a7a31edb602e9537390a7
management:
endpoints: