Compare commits

...

14 Commits

Author SHA1 Message Date
35260a773e refactor(certificate): 优化身份证OCR识别流程,增强容错性
- 身份证OCR识别改为尽力而为模式,识别失败不阻断业务流程
- 增加异常捕获,避免OCR服务异常导致业务中断
- 仅在OCR识别成功且信息一致时更新认证状态和用户认证信息
- 在订单身份核验中OCR识别异常或识别失败均记录日志,业务继续执行
- 回填实名信息时增加字段有效性校验,避免空字段导致信息错误
- 优化身份证图片缺失及格式异常处理逻辑,不再返回失败,仅记录警告日志
2026-07-30 23:28:21 +08:00
377a592195 refactor(certificate): 合并身份证正反面OCR识别接口
- 移除旧的正面、反面单独识别接口,改用统一的身份证OCR合并识别接口
- 优化身份证OCR调用逻辑,添加图片base64转换失败处理
- 新增身份证识别服务实现,调用阿里云蜜堂有信接口
- 统一身份证照片识别失败错误码和提示,增强日志记录
- 验证身份证信息一致性及有效期,支持多格式日期解析
- 调整controller逻辑,使用新身份证OCR响应对象进行验证和回填
2026-07-30 22:27:02 +08:00
8e081af4d8 fix(order): 续费时间无缝衔接,避免空窗期多送有效期
- 续费时根据父订单到期时间判断,未逾期顺延,否则从当天延续一个月
- 后台统一计算续费开始与结束时间,不信任前端传入的时间参数
- 更新订单与续费记录的起始和结束时间逻辑,防止有效期重叠或空白
- 修改查询参数,支持仅查询续费单(rentOrderId 不为空)
- 保持支付状态和订单状态更新的正常流程,确保续费处理正确
2026-07-27 11:36:51 +08:00
1555fe1898 refactor(equipment): 优化身份证校验逻辑,抽取为独立方法
- 将身份证真实性校验代码抽取到 verifyIdCard 私有方法中
- 在校验方法中添加异常捕获,避免接口异常导致程序崩溃
- 优化图片数据解析和参数校验逻辑,提升健壮性
- 校验失败时返回详细错误信息,便于定位问题
- 身份证识别通过后自动回填订单和用户的实名信息
- 保持接口调用流程简洁,提高代码可读性和维护性
2026-07-26 14:12:21 +08:00
1a5ff59324 20260716 2026-07-16 14:45:28 +08:00
de7ae7fb7c 修复缴费记录逾期天数问题 2026-07-01 19:13:42 +08:00
dbeacbc3a3 修复缴费记录逾期天数问题 2025-08-07 23:20:27 +08:00
f62b8997ca Merge remote-tracking branch 'origin/dev' into dev 2025-07-29 00:32:13 +08:00
71483f272d 修复:电池系统的https证书bug 2025-07-29 00:26:43 +08:00
aecfea75f2 修复无法支付问题 2025-04-01 13:00:13 +08:00
df3ae32eb7 修复:已知问题 2025-03-13 15:53:20 +08:00
62dad7098f 修复:租金现在是 3001月,我要改 2601月
当确定商品套餐后要修改套餐数据时,希望修改后不管是新下单还是续费都跟着一起改变,当有逾期时要把逾期缴清后才可以使用修改后的套餐。急!!!
2025-03-13 12:53:34 +08:00
c34960d011 fix bug 2025-01-20 12:09:46 +08:00
a8d369a8b2 fix bug 2024-12-31 22:11:52 +08:00
21 changed files with 350 additions and 233 deletions

View File

@@ -56,8 +56,6 @@ public class EquipmentController extends BaseController {
@Resource @Resource
private EquipmentRecordService equipmentRecordService; private EquipmentRecordService equipmentRecordService;
@PreAuthorize("hasAuthority('apps:equipment:list')")
@OperationLog
@ApiOperation("分页查询设备管理") @ApiOperation("分页查询设备管理")
@GetMapping("/page") @GetMapping("/page")
public ApiResult<PageResult<Equipment>> page(EquipmentParam param) { public ApiResult<PageResult<Equipment>> page(EquipmentParam param) {
@@ -68,8 +66,6 @@ public class EquipmentController extends BaseController {
return success(equipmentService.pageRel(param)); return success(equipmentService.pageRel(param));
} }
@PreAuthorize("hasAuthority('apps:equipment:list')")
@OperationLog
@ApiOperation("查询全部设备管理") @ApiOperation("查询全部设备管理")
@GetMapping() @GetMapping()
public ApiResult<List<Equipment>> list(EquipmentParam param) { public ApiResult<List<Equipment>> list(EquipmentParam param) {
@@ -84,7 +80,8 @@ public class EquipmentController extends BaseController {
@OperationLog @OperationLog
@ApiOperation("根据id查询设备管理") @ApiOperation("根据id查询设备管理")
@GetMapping("/{id}") @GetMapping("/{id}")
public ApiResult<Equipment> get(@PathVariable("id") Integer id) { public ApiResult<?> get(@PathVariable("id") Integer id) {
if (getLoginUser() == null) return fail("请先登录");
// return success(equipmentService.getById(id)); // return success(equipmentService.getById(id));
// 使用关联查询 // 使用关联查询
return success(equipmentService.getByIdRel(id)); return success(equipmentService.getByIdRel(id));

View File

@@ -44,7 +44,6 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
PageParam<Equipment, EquipmentParam> page = new PageParam<>(param); PageParam<Equipment, EquipmentParam> page = new PageParam<>(param);
page.setDefaultOrder("create_time desc"); page.setDefaultOrder("create_time desc");
List<Equipment> list = baseMapper.selectPageRel(page, param); List<Equipment> list = baseMapper.selectPageRel(page, param);
Set<Integer> touziUserIds = list.stream().map(Equipment::getTouziUserId).collect(Collectors.toSet()); Set<Integer> touziUserIds = list.stream().map(Equipment::getTouziUserId).collect(Collectors.toSet());
// List<User> touziUserList = userService.lambdaQuery().in(User::getUserId, touziUserIds).list(); // List<User> touziUserList = userService.lambdaQuery().in(User::getUserId, touziUserIds).list();
Map<Integer, User> touziUserCollect = null; Map<Integer, User> touziUserCollect = null;
@@ -54,13 +53,14 @@ public class EquipmentServiceImpl extends ServiceImpl<EquipmentMapper, Equipment
touziUserCollect = touziUserList.stream().collect(Collectors.toMap(User::getUserId, e->e)); touziUserCollect = touziUserList.stream().collect(Collectors.toMap(User::getUserId, e->e));
} }
} }
System.out.println("touziUserCollect = " + touziUserCollect);
// 查询绑定电池的用户 // 查询绑定电池的用户
for (Equipment equipment : list) { for (Equipment equipment : list) {
// 查询状态 // 查询状态
System.out.println("equipment.getEquipmentCode() = " + equipment.getEquipmentCode()); System.out.println("equipment.getEquipmentCode() = " + equipment.getEquipmentCode());
try { try {
ResponseEntity<JSONObject> entity = restTemplate.getForEntity("http://battery.zfdliot.com/api/battery/status?battery_sn=" + equipment.getEquipmentCode(), JSONObject.class); ResponseEntity<JSONObject> entity = restTemplate.getForEntity("http://battery.zfdliot.com/api/battery/status?battery_sn=" + equipment.getEquipmentCode(), JSONObject.class);
System.out.println("entity = " + entity);
JSONObject body = entity.getBody(); JSONObject body = entity.getBody();
Integer code = body.getInteger("code"); Integer code = body.getInteger("code");
JSONObject data = body.getJSONObject("data"); JSONObject data = body.getJSONObject("data");

View File

@@ -13,9 +13,8 @@ import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.love.entity.Certificate; import com.gxwebsoft.love.entity.Certificate;
import com.gxwebsoft.love.param.CertificateParam; import com.gxwebsoft.love.param.CertificateParam;
import com.gxwebsoft.love.service.CertificateService; import com.gxwebsoft.love.service.CertificateService;
import com.gxwebsoft.love.vo.idcheck.BackRecognitionResult; import lombok.extern.slf4j.Slf4j;
import com.gxwebsoft.love.vo.idcheck.FrontRecognitionResult; import com.gxwebsoft.oa.vo.IdcardRespVO;
import com.gxwebsoft.love.vo.idcheck.Response;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
@@ -33,6 +32,7 @@ import java.util.stream.Collectors;
* @since 2023-06-24 11:18:52 * @since 2023-06-24 11:18:52
*/ */
@Api(tags = "证件管理记录表管理") @Api(tags = "证件管理记录表管理")
@Slf4j
@RestController @RestController
@RequestMapping("/api/love/certificate") @RequestMapping("/api/love/certificate")
public class CertificateController extends BaseController { public class CertificateController extends BaseController {
@@ -97,32 +97,23 @@ public class CertificateController extends BaseController {
return fail("请上传身份证照片", certificate); return fail("请上传身份证照片", certificate);
} }
// 验证正面 // 身份证OCR识别正反面合并尽力而为失败不阻断业务识别成功且信息一致才置为已认证
Response<FrontRecognitionResult> front = certificateService.idcardfrontrecongnition(collect.get(0)); try {
if (front.getError_code() != 0) { IdcardRespVO resp = certificateService.idcardOcr(collect.get(0), collect.get(1));
return fail(front.getReason()); if (resp != null && "FP00000".equals(resp.getCode())
} && certificate.getCertificateCode().equals(resp.getIdCardNo())
&& certificate.getRealName().equals(resp.getName())) {
// 验证反面
Response<BackRecognitionResult> back = certificateService.idcardbackrecongnition(collect.get(1));
if (back.getError_code() != 0) {
return fail(back.getReason());
}
certificate.setStatus(30); certificate.setStatus(30);
// 信息是否一致
if (!certificate.getCertificateCode().equals(front.getResult().getIdcardno())) {
return fail("认证失败", certificate);
}
if (!certificate.getRealName().equals(front.getResult().getName())) {
return fail("认证失败", certificate);
}
User user = new User(); User user = new User();
user.setUserId(loginUser.getUserId()); user.setUserId(loginUser.getUserId());
user.setCertification(1); user.setCertification(1);
userService.updateById(user); userService.updateById(user);
} else {
log.warn("身份证OCR识别未通过或跳过证件先按待审核保存, code={}", resp == null ? "null" : resp.getCode());
}
} catch (Exception e) {
log.error("身份证OCR识别调用异常跳过校验证件先按待审核保存", e);
}
} }
// 房屋认证 // 房屋认证

View File

@@ -4,9 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
import com.gxwebsoft.common.core.web.PageResult; import com.gxwebsoft.common.core.web.PageResult;
import com.gxwebsoft.love.entity.Certificate; import com.gxwebsoft.love.entity.Certificate;
import com.gxwebsoft.love.param.CertificateParam; import com.gxwebsoft.love.param.CertificateParam;
import com.gxwebsoft.love.vo.idcheck.BackRecognitionResult; import com.gxwebsoft.oa.vo.IdcardRespVO;
import com.gxwebsoft.love.vo.idcheck.FrontRecognitionResult;
import com.gxwebsoft.love.vo.idcheck.Response;
import java.util.List; import java.util.List;
@@ -42,10 +40,15 @@ public interface CertificateService extends IService<Certificate> {
*/ */
Certificate getByIdRel(Integer certificateId); Certificate getByIdRel(Integer certificateId);
/**
* 身份证OCR识别正反面合并识别
*
* @param frontImageUrl 正面图片 URL
* @param backImageUrl 反面图片 URL
* @return 识别结果
*/
IdcardRespVO idcardOcr(String frontImageUrl, String backImageUrl);
boolean verifyIdcard(Certificate param, List<String> files); boolean verifyIdcard(Certificate param, List<String> files);
Response<FrontRecognitionResult> idcardfrontrecongnition(String imageUrl);
Response<BackRecognitionResult> idcardbackrecongnition(String imageUrl);
} }

View File

@@ -2,7 +2,6 @@ package com.gxwebsoft.love.service.impl;
import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.exceptions.ExceptionUtil;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gxwebsoft.common.core.utils.HttpUtils; import com.gxwebsoft.common.core.utils.HttpUtils;
import com.gxwebsoft.common.core.utils.ImageUtil; import com.gxwebsoft.common.core.utils.ImageUtil;
@@ -12,9 +11,7 @@ import com.gxwebsoft.love.entity.Certificate;
import com.gxwebsoft.love.mapper.CertificateMapper; import com.gxwebsoft.love.mapper.CertificateMapper;
import com.gxwebsoft.love.param.CertificateParam; import com.gxwebsoft.love.param.CertificateParam;
import com.gxwebsoft.love.service.CertificateService; import com.gxwebsoft.love.service.CertificateService;
import com.gxwebsoft.love.vo.idcheck.BackRecognitionResult; import com.gxwebsoft.oa.vo.IdcardRespVO;
import com.gxwebsoft.love.vo.idcheck.FrontRecognitionResult;
import com.gxwebsoft.love.vo.idcheck.Response;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
@@ -22,10 +19,12 @@ import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate; import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime; import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
/** /**
* 证件管理记录表Service实现 * 证件管理记录表Service实现
@@ -39,12 +38,13 @@ public class CertificateServiceImpl extends ServiceImpl<CertificateMapper, Certi
@Resource @Resource
private RestTemplate restTemplate; private RestTemplate restTemplate;
private String appcode = "566b5786c5874464909d8c0b7f64cdc7"; private static final String HOST = "https://miitangs14.market.alicloudapi.com";
private static final String APP_CODE = "1390018309a443c695c98c8da2bed691";
private static final String SUCCESS_CODE = "FP00000";
@Override @Override
public PageResult<Certificate> pageRel(CertificateParam param) { public PageResult<Certificate> pageRel(CertificateParam param) {
PageParam<Certificate, CertificateParam> page = new PageParam<>(param); PageParam<Certificate, CertificateParam> page = new PageParam<>(param);
//page.setDefaultOrder("create_time desc");
List<Certificate> list = baseMapper.selectPageRel(page, param); List<Certificate> list = baseMapper.selectPageRel(page, param);
return new PageResult<>(list, page.getTotal()); return new PageResult<>(list, page.getTotal());
} }
@@ -52,9 +52,7 @@ public class CertificateServiceImpl extends ServiceImpl<CertificateMapper, Certi
@Override @Override
public List<Certificate> listRel(CertificateParam param) { public List<Certificate> listRel(CertificateParam param) {
List<Certificate> list = baseMapper.selectListRel(param); List<Certificate> list = baseMapper.selectListRel(param);
// 排序
PageParam<Certificate, CertificateParam> page = new PageParam<>(); PageParam<Certificate, CertificateParam> page = new PageParam<>();
//page.setDefaultOrder("create_time desc");
return page.sortRecords(list); return page.sortRecords(list);
} }
@@ -65,94 +63,81 @@ public class CertificateServiceImpl extends ServiceImpl<CertificateMapper, Certi
return param.getOne(baseMapper.selectListRel(param)); return param.getOne(baseMapper.selectListRel(param));
} }
@Override
public IdcardRespVO idcardOcr(String frontImageUrl, String backImageUrl) {
String frontImg = ImageUtil.ImageBase64(frontImageUrl);
String backImg = ImageUtil.ImageBase64(backImageUrl);
if (frontImg == null || frontImg.isEmpty() || backImg == null || backImg.isEmpty()) {
IdcardRespVO fail = new IdcardRespVO();
fail.setCode("PARAM_ERROR");
fail.setMessage("身份证图片转base64失败");
return fail;
}
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "APPCODE " + APP_CODE);
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
headers.put("X-Ca-Nonce", UUID.randomUUID().toString());
Map<String, String> querys = new HashMap<>();
Map<String, String> bodys = new HashMap<>();
bodys.put("reqNo", UUID.randomUUID().toString().replace("-", ""));
bodys.put("frontImg", frontImg);
bodys.put("backImg", backImg);
try {
HttpResponse response = HttpUtils.doPost(HOST, "/v1/tools/ocr/idCard", "POST", headers, querys, bodys);
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity);
return JSONObject.parseObject(string, IdcardRespVO.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override @Override
public boolean verifyIdcard(Certificate param, List<String> files) { public boolean verifyIdcard(Certificate param, List<String> files) {
// 验证正面 if (files == null || files.size() < 2) {
Response<FrontRecognitionResult> front = idcardfrontrecongnition(files.get(0)); ExceptionUtil.wrapAndThrow(new RuntimeException("缺少身份证正反面照片"));
if(front.getError_code() != 0){
ExceptionUtil.wrapAndThrow(new Exception(front.getReason()));
return false; return false;
} }
IdcardRespVO resp = idcardOcr(files.get(0), files.get(1));
// 验证反面 if (resp == null) {
Response<BackRecognitionResult> back = idcardbackrecongnition(files.get(1)); ExceptionUtil.wrapAndThrow(new RuntimeException("身份证识别调用失败"));
if(back.getError_code() != 0){ return false;
ExceptionUtil.wrapAndThrow(new Exception(back.getReason())); }
if (!SUCCESS_CODE.equals(resp.getCode())) {
ExceptionUtil.wrapAndThrow(new RuntimeException(resp.getMessage() == null ? "身份证识别失败" : resp.getMessage()));
return false; return false;
} }
// 信息是否一致 // 信息是否一致
if(!param.getCertificateCode().equals(front.getResult().getIdcardno())){ if (!param.getCertificateCode().equals(resp.getIdCardNo())) {
return false; return false;
} }
if(!param.getRealName().equals(front.getResult().getName())){ if (!param.getRealName().equals(resp.getName())) {
return false; return false;
} }
boolean b = LocalDateTime.parse(back.getResult().getEndDate()).compareTo(LocalDateTime.now()) > 0;
return b; // 有效期是否过期
String expireDate = resp.getExpireDate();
if (expireDate == null || expireDate.isEmpty()) {
return false;
} }
LocalDate expire;
@Override
public Response<FrontRecognitionResult> idcardfrontrecongnition(String imageUrl) {
String host = "https://zidv2.market.alicloudapi.com";
String path = "/thirdnode/ImageAI/idcardfrontrecongnition";
String method = "POST";
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appcode);
//根据API的要求定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<String, String>();
Map<String, String> bodys = new HashMap<String, String>();
bodys.put("base64Str", ImageUtil.ImageBase64(imageUrl));
try { try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys); expire = LocalDate.parse(expireDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity);
Response<FrontRecognitionResult> o = JSONObject.parseObject(string, new TypeReference<Response<FrontRecognitionResult>>() {
});
return o;
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); // 尝试按 yyyyMMdd 解析
}
return null;
}
@Override
public Response<BackRecognitionResult> idcardbackrecongnition(String imageUrl) {
String host = "https://zidv2.market.alicloudapi.com";
String path = "/thirdnode/ImageAI/idcardbackrecongnition";
String method = "POST";
Map<String, String> headers = new HashMap<String, String>();
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
headers.put("Authorization", "APPCODE " + appcode);
//根据API的要求定义相对应的Content-Type
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
Map<String, String> querys = new HashMap<String, String>();
Map<String, String> bodys = new HashMap<String, String>();
bodys.put("base64Str", ImageUtil.ImageBase64(imageUrl));
try { try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys); expire = LocalDate.parse(expireDate, DateTimeFormatter.ofPattern("yyyyMMdd"));
HttpEntity entity = response.getEntity(); } catch (Exception ex) {
String string = EntityUtils.toString(entity); return false;
Response<BackRecognitionResult> o = JSONObject.parseObject(string, new TypeReference<Response<BackRecognitionResult>>() {
});
return o;
} catch (Exception e) {
e.printStackTrace();
} }
}
return null; return expire.isAfter(LocalDate.now());
} }
} }

View File

@@ -1,12 +1,10 @@
package com.gxwebsoft.oa.service.impl; package com.gxwebsoft.oa.service.impl;
import com.alibaba.fastjson2.JSONObject; import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.gxwebsoft.common.core.utils.HttpUtils; import com.gxwebsoft.common.core.utils.HttpUtils;
import com.gxwebsoft.love.vo.idcheck.FrontRecognitionResult;
import com.gxwebsoft.love.vo.idcheck.Response;
import com.gxwebsoft.oa.service.IdcardService; import com.gxwebsoft.oa.service.IdcardService;
import com.gxwebsoft.oa.vo.IdcardRespVO; import com.gxwebsoft.oa.vo.IdcardRespVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity; import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils; import org.apache.http.util.EntityUtils;
@@ -16,42 +14,75 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
@Slf4j
@Service @Service
public class IdcardServiceImpl implements IdcardService { public class IdcardServiceImpl implements IdcardService {
/** 阿里云市场 - 身份证 OCR 供应商(蜜堂有信) */
private static final String HOST = "https://miitangs14.market.alicloudapi.com";
private static final String APP_CODE = "1390018309a443c695c98c8da2bed691";
private static final String SUCCESS_CODE = "FP00000";
/**
* @param frontImg 身份证正面图片的 base64 字符串(已带 ?x-oss-process 压缩,调用方负责生成)
* @param backImg 身份证反面图片的 base64 字符串
*/
@Override @Override
public IdcardRespVO verify(String frontImg, String backImg) { public IdcardRespVO verify(String frontImg, String backImg) {
// 1. 校验输入
if (frontImg == null || frontImg.isEmpty() || backImg == null || backImg.isEmpty()) {
log.warn("身份证 verify 调用缺少正/反面图片, frontIsBlank={}, backIsBlank={}",
frontImg == null || frontImg.isEmpty(),
backImg == null || backImg.isEmpty());
return null;
}
String host = "https://miitangs14.market.alicloudapi.com"; // 2. 调用新接口(正反面合并为一次请求)
String path = "/v1/tools/ocr/idCard"; IdcardRespVO resp;
String method = "POST"; try {
String appcode = "1390018309a443c695c98c8da2bed691"; resp = callOcr(frontImg, backImg);
Map<String, String> headers = new HashMap<String, String>(); } catch (Exception e) {
//最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105 log.error("身份证OCR识别调用异常", e);
headers.put("Authorization", "APPCODE " + appcode); return null;
//根据API的要求定义相对应的Content-Type }
if (resp == null) {
log.warn("身份证OCR识别返回为空接口无响应或解析失败");
return null;
}
// 3. 失败返回具体原因
if (!SUCCESS_CODE.equals(resp.getCode())) {
log.warn("身份证OCR识别失败 code={}, message={}", resp.getCode(), resp.getMessage());
IdcardRespVO fail = new IdcardRespVO();
fail.setCode(resp.getCode());
fail.setMessage(resp.getMessage());
return fail;
}
// 4. 成功
log.info("身份证识别成功 name={}, idCardNo={}", resp.getName(), resp.getIdCardNo());
return resp;
}
/**
* 调用蜜堂有信身份证 OCR 接口POST /v1/tools/ocr/idCard
*/
private IdcardRespVO callOcr(String frontImg, String backImg) throws Exception {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "APPCODE " + APP_CODE);
headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); headers.put("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
//需要给X-Ca-Nonce的值生成随机字符串每次请求不能相同
headers.put("X-Ca-Nonce", UUID.randomUUID().toString()); headers.put("X-Ca-Nonce", UUID.randomUUID().toString());
Map<String, String> querys = new HashMap<String, String>();
Map<String, String> bodys = new HashMap<String, String>(); Map<String, String> querys = new HashMap<>();
Map<String, String> bodys = new HashMap<>();
bodys.put("reqNo", UUID.randomUUID().toString().replace("-", ""));
bodys.put("frontImg", frontImg); bodys.put("frontImg", frontImg);
bodys.put("backImg", backImg); bodys.put("backImg", backImg);
HttpResponse response = HttpUtils.doPost(HOST, "/v1/tools/ocr/idCard", "POST", headers, querys, bodys);
try {
HttpResponse response = HttpUtils.doPost(host, path, method, headers, querys, bodys);
HttpEntity entity = response.getEntity(); HttpEntity entity = response.getEntity();
String string = EntityUtils.toString(entity); String body = EntityUtils.toString(entity);
IdcardRespVO respVO = JSONObject.parseObject(string, new TypeReference<IdcardRespVO>() { log.debug("OCR 调用 frontImgLen={}, backImgLen={}, respLen={}", frontImg.length(), backImg.length(), body.length());
}); return JSONObject.parseObject(body, IdcardRespVO.class);
return respVO;
//获取response的body
//System.out.println(EntityUtils.toString(response.getEntity()));
} catch (Exception e) {
e.printStackTrace();
}
return null;
} }
} }

View File

@@ -79,14 +79,14 @@ public class OpenAlipayController extends BaseController {
// 验证签名 // 验证签名
isCheckSign(); isCheckSign();
// 读取缓存信息 // 读取缓存信息
String key = "setting:register:" + getTenantId(); // String key = "setting:register:" + getTenantId();
String setting = redisUtil.get(key); // String setting = redisUtil.get(key);
// if (setting == null) { // if (setting == null) {
// throw new BusinessException("请先配置注册设置"); // throw new BusinessException("请先配置注册设置");
// } // }
JSONObject jsonObject = JSONObject.parseObject(setting); // JSONObject jsonObject = JSONObject.parseObject(setting);
String roleId = jsonObject.getString("roleId"); // String roleId = jsonObject.getString("roleId");
// 实例化客户端 // 实例化客户端
DefaultAlipayClient alipayClient = alipayConfig.alipayClient(param.getTenantId()); DefaultAlipayClient alipayClient = alipayConfig.alipayClient(param.getTenantId());
try { try {

View File

@@ -308,7 +308,13 @@ public class OpenEquipmentController extends BaseController {
// 订单信息 // 订单信息
Integer orderId = equipment.getOrderId(); Integer orderId = equipment.getOrderId();
Order order = orderService.getById(orderId); Order order = orderService.getById(orderId);
if (order == null) {
return fail("订单不存在");
}
Integer oldEqId = order.getEquipmentId(); Integer oldEqId = order.getEquipmentId();
if (oldEqId == null) {
return fail("订单未绑定设备");
}
// 新电池 // 新电池
Equipment one = equipmentService.getByEquipmentCode(equipmentCode); Equipment one = equipmentService.getByEquipmentCode(equipmentCode);
@@ -319,6 +325,9 @@ public class OpenEquipmentController extends BaseController {
String newMerchantCode = one.getMerchantCode(); String newMerchantCode = one.getMerchantCode();
// 旧电池 // 旧电池
Equipment old = equipmentService.getById(oldEqId); Equipment old = equipmentService.getById(oldEqId);
if (old == null) {
return fail("旧设备不存在");
}
String oldMerchantCode = order.getMerchantCode(); String oldMerchantCode = order.getMerchantCode();
Integer userId = one.getUserId(); Integer userId = one.getUserId();
@@ -376,9 +385,9 @@ public class OpenEquipmentController extends BaseController {
* } * }
*/ */
JSONObject param = new JSONObject(); JSONObject param = new JSONObject();
param.put("userId", loginUser.getUserId()); param.put("userId", order.getUserId());
param.put("userName", loginUser.getNickname()); param.put("userName", order.getNickname());
param.put("userPhone", loginUser.getPhone()); param.put("userPhone", order.getPhone());
param.put("battery_sn", one.getEquipmentCode()); param.put("battery_sn", one.getEquipmentCode());
System.out.println("param2 = " + param); System.out.println("param2 = " + param);
ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity("http://battery.zfdliot.com/api/battery/batteryBindUser", param, JSONObject.class); ResponseEntity<JSONObject> responseEntity = restTemplate.postForEntity("http://battery.zfdliot.com/api/battery/batteryBindUser", param, JSONObject.class);
@@ -402,31 +411,10 @@ public class OpenEquipmentController extends BaseController {
// if(receiptStatus == 20){ // if(receiptStatus == 20){
// return fail("订单已确认收货,不能重复确认!"); // return fail("订单已确认收货,不能重复确认!");
// } // }
// 验证身份证真实性 // 验证身份证真实性(校验不通过时返回 fail 结果,通过则返回 null
if(StringUtils.hasText(receiptParam.getOrderSourceData())) { ApiResult<?> idcardResult = verifyIdCard(receiptParam.getOrderSourceData(), order);
String orderSourceDataString = receiptParam.getOrderSourceData(); if (idcardResult != null) {
List<String> images = JSONObject.parseArray(orderSourceDataString, String.class); return idcardResult;
String front = ImageUtil.ImageBase64(images.get(0) + "?x-oss-process=image/resize,w_750/quality,Q_80");
String back = ImageUtil.ImageBase64(images.get(1) + "?x-oss-process=image/resize,w_750/quality,Q_80");
IdcardRespVO verify = idcardService.verify(front, back);
if(!"FP00000".equals(verify.getCode())) {
return fail("请上传身份证正面照片");
}
if(verify.getIssueDate() == null || verify.getIssueOrg() == null || verify.getExpireDate() == null) {
return fail("请上传身份证反面照片");
}
order.setRealName(verify.getName());
order.setIdCode(verify.getIdCardNo());
order.setAddress(verify.getAddress());
//更新用户信息
User user = userService.getById(order.getUserId());
user.setRealName(verify.getName());
user.setIdCard(verify.getIdCardNo());
user.setAddress(verify.getAddress());
userService.updateUser(user);
} }
// 短信验证码校验 // 短信验证码校验
@@ -450,6 +438,78 @@ public class OpenEquipmentController extends BaseController {
return success("确认收货成功"); return success("确认收货成功");
} }
/**
* 身份证 OCR 真实性校验,并把识别到的实名信息回填到 order / user。
* orderSourceData 约定顺序:[0]=身份证正面、[1]=身份证反面、[2]=人车合照、[3]=车子照片、[4]=安装照片
*
* @param orderSourceData 前端传入的图片地址 JSON 数组字符串
* @param order 当前订单(校验通过时回填实名信息)
* @return 校验失败返回 ApiResult(fail),校验通过返回 null
*/
/**
* 身份证 OCR 真实性校验当前为「尽力而为」模式OCR 调不通/未通过时仅记录日志,不阻断业务)。
* 等阿里云市场接口/AppCode 恢复正常后,本方法会在识别成功时自动回填实名信息,无需额外改动。
* orderSourceData 约定顺序:[0]=身份证正面、[1]=身份证反面、[2]=人车合照、[3]=车子照片、[4]=安装照片
*
* @param orderSourceData 前端传入的图片地址 JSON 数组字符串
* @param order 当前订单(校验通过时回填实名信息)
* @return 始终返回 null放行业务OCR 结果仅用于尽力回填
*/
private ApiResult<?> verifyIdCard(String orderSourceData, Order order) {
// 1. 解析图片数组(解析失败仅记录,不阻断业务)
List<String> images;
try {
images = JSONObject.parseArray(orderSourceData, String.class);
} catch (Exception e) {
log.warn("身份证图片数据解析失败跳过OCR校验, orderSourceData={}", orderSourceData);
return null;
}
// 2. 必须包含身份证正反面(约定下标 0=正面、1=反面);缺失则跳过 OCR不阻断
if (images == null || images.size() < 2
|| !StringUtils.hasText(images.get(0)) || !StringUtils.hasText(images.get(1))) {
log.warn("身份证正反面照片缺失跳过OCR校验业务继续");
return null;
}
// 3. 调第三方 OCR 识别best-effort异常/失败均仅记录日志,放行业务)
try {
String front = ImageUtil.ImageBase64(images.get(0) + "?x-oss-process=image/resize,w_750/quality,Q_80");
String back = ImageUtil.ImageBase64(images.get(1) + "?x-oss-process=image/resize,w_750/quality,Q_80");
IdcardRespVO verify = idcardService.verify(front, back);
if (verify == null) {
log.warn("身份证OCR识别返回为空跳过校验业务继续");
return null;
}
if (!"FP00000".equals(verify.getCode())) {
log.warn("身份证OCR识别未通过 code={}, message={},跳过校验,业务继续", verify.getCode(), verify.getMessage());
return null;
}
// 成功:回填实名信息
if (StringUtils.hasText(verify.getName()) && StringUtils.hasText(verify.getIdCardNo())) {
order.setRealName(verify.getName());
order.setIdCode(verify.getIdCardNo());
order.setAddress(verify.getAddress());
User user = userService.getById(order.getUserId());
if (user != null) {
user.setRealName(verify.getName());
user.setIdCard(verify.getIdCardNo());
user.setAddress(verify.getAddress());
userService.updateUser(user);
}
log.info("身份证OCR识别成功并回填实名 name={}, idCardNo={}", verify.getName(), verify.getIdCardNo());
} else {
log.warn("身份证OCR识别成功但字段缺失跳过回填业务继续");
}
} catch (Exception e) {
log.error("身份证OCR识别调用异常跳过校验业务继续", e);
}
return null;
}
/*@ApiOperation("确认收货") /*@ApiOperation("确认收货")
@PostMapping("/receipt") @PostMapping("/receipt")
public ApiResult<?> receipt(@RequestBody Order order) { public ApiResult<?> receipt(@RequestBody Order order) {
@@ -525,6 +585,7 @@ public class OpenEquipmentController extends BaseController {
refund.setApplyDesc(isRefund == 3?"强制退租":"申请退租");//3强制退租后台操作 refund.setApplyDesc(isRefund == 3?"强制退租":"申请退租");//3强制退租后台操作
refund.setRefundMoney(new BigDecimal(0)); refund.setRefundMoney(new BigDecimal(0));
refund.setMerchantCode(order.getMerchantCode()); refund.setMerchantCode(order.getMerchantCode());
refund.setUpdateTime(DateUtil.date());
} }
refund.setAuditStatus(10); refund.setAuditStatus(10);
refund.setOrderNo(order.getOrderNo()); refund.setOrderNo(order.getOrderNo());

View File

@@ -1,5 +1,6 @@
package com.gxwebsoft.shop.controller; package com.gxwebsoft.shop.controller;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
@@ -132,9 +133,13 @@ public class OrderController extends BaseController {
for (Order order : list) { for (Order order : list) {
//计算缴费总钱数 //计算缴费总钱数
List<OrderPay> orderPays = orderPayCollect.get(order.getOrderId()); List<OrderPay> orderPays = orderPayCollect.get(order.getOrderId());
order.setOrderPays(orderPays);
if (null != orderPays && !orderPays.isEmpty()) { if (null != orderPays && !orderPays.isEmpty()) {
BigDecimal sum = orderPays.stream().map(OrderPay::getOrderPrice).reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal sum = orderPays.stream().map(OrderPay::getOrderPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
order.setTotalPayPrice(sum); order.setTotalPayPrice(sum);
OrderPay lastOrderPay = orderPays.get(orderPays.size() - 1);
// 计算剩余天数
order.setRestDay((int) DateUtil.between(lastOrderPay.getExpirationTime(), new Date(), DateUnit.DAY));
} else { } else {
order.setTotalPayPrice(BigDecimal.ZERO); order.setTotalPayPrice(BigDecimal.ZERO);
} }
@@ -377,14 +382,14 @@ public class OrderController extends BaseController {
//.ge(Order::getCreateTime,DateUtil.offsetMonth(new Date(),-6))//最近三个6个月 //.ge(Order::getCreateTime,DateUtil.offsetMonth(new Date(),-6))//最近三个6个月
.orderByDesc(Order::getCreateTime) .orderByDesc(Order::getCreateTime)
.list(); .list();
if (overdueOrderList != null && !overdueOrderList.isEmpty()) { // if (overdueOrderList != null && !overdueOrderList.isEmpty()) {
for(Order overdueOrder:overdueOrderList){ // for(Order overdueOrder:overdueOrderList){
if (overdueOrder.getRestDay()<0){//如果剩余天数为负数 // if (overdueOrder.getRestDay()<0){//如果剩余天数为负数
log.warn("添加订单失败,有订单逾期未结 userId:{},orderId:{},orderNo:{},restDay:{}",loginUser.getUserId(),overdueOrder.getOrderId(),overdueOrder.getOrderNo(),overdueOrder.getRestDay()); // log.warn("添加订单失败,有订单逾期未结 userId:{},orderId:{},orderNo:{},restDay:{}",loginUser.getUserId(),overdueOrder.getOrderId(),overdueOrder.getOrderNo(),overdueOrder.getRestDay());
return fail("添加订单失败,有订单逾期未结"); // return fail("添加订单失败,有订单逾期未结");
} // }
} // }
} // }
// 历史订单 // 历史订单
order.setCurrPeriods(0); order.setCurrPeriods(0);

View File

@@ -4,6 +4,7 @@ import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateField; import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
@@ -17,6 +18,7 @@ import com.gxwebsoft.common.core.utils.JSONUtil;
import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.core.web.BaseController;
import com.gxwebsoft.common.system.entity.User; import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.shop.entity.FreezeOrder; import com.gxwebsoft.shop.entity.FreezeOrder;
import com.gxwebsoft.shop.entity.GoodsService;
import com.gxwebsoft.shop.entity.Order; import com.gxwebsoft.shop.entity.Order;
import com.gxwebsoft.shop.service.FreezeOrderService; import com.gxwebsoft.shop.service.FreezeOrderService;
import com.gxwebsoft.shop.service.OrderPayService; import com.gxwebsoft.shop.service.OrderPayService;
@@ -38,9 +40,9 @@ import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
import static com.gxwebsoft.common.core.constants.OrderConstants.ORDER_STATUS_OVER; import static com.gxwebsoft.common.core.constants.OrderConstants.*;
import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_NO_PAY;
/** /**
* 订单记录表控制器 * 订单记录表控制器
@@ -57,8 +59,6 @@ public class OrderPayController extends BaseController {
private OrderPayService orderPayService; private OrderPayService orderPayService;
@Resource @Resource
private FreezeOrderService freezeOrderService; private FreezeOrderService freezeOrderService;
@Resource @Resource
private OrderService orderService; private OrderService orderService;
@@ -98,9 +98,28 @@ public class OrderPayController extends BaseController {
@GetMapping("/{id}") @GetMapping("/{id}")
public ApiResult<OrderPay> get(@PathVariable("id") Integer id) { public ApiResult<OrderPay> get(@PathVariable("id") Integer id) {
return success(orderPayService.getById(id)); final OrderPay orderPay = orderPayService.getById(id);
// 使用关联查询 final BigDecimal rent = orderPay.getBatteryRent();
//return success(orderPayService.getByIdRel(id)); final OrderPay one = orderPayService.getOne(new LambdaQueryWrapper<OrderPay>()
.eq(OrderPay::getRentOrderId, orderPay.getRentOrderId())
.eq(OrderPay::getUserId, orderPay.getUserId())
.eq(OrderPay::getPayStatus, PAY_STATUS_SUCCESS)
.orderByDesc(OrderPay::getId)
.last("limit 1")
);
final Integer goodsId = orderPay.getGoodsId();
final EquipmentGoods goods = equipmentGoodsService.getByIdRel(goodsId);
final BigDecimal batteryRent = goods.getBatteryRent();
// 按新的续费价格
orderPay.setTotalPrice(batteryRent);
if (one != null) {
final long between = DateUtil.between(new Date(), one.getExpirationTime(), DateUnit.DAY, false);
if (between < 0) {
// 有逾期的订单不能享受折扣
orderPay.setTotalPrice(rent);
}
}
return success(orderPay);
} }
@GetMapping("/change-order-no") @GetMapping("/change-order-no")
@@ -143,8 +162,12 @@ public class OrderPayController extends BaseController {
one.setCurrPeriods(order.getCurrPeriods() + 1); one.setCurrPeriods(order.getCurrPeriods() + 1);
one.setPeriods(order.getPeriods()); one.setPeriods(order.getPeriods());
one.setRentOrderId(order.getOrderId()); one.setRentOrderId(order.getOrderId());
one.setStartTime(order.getExpirationTime()); // 续费无缝衔接:未逾期时从上一个到期日顺延一个月;已逾期时从今天顺延,
one.setExpirationTime(DateUtil.offset(order.getExpirationTime(), DateField.MONTH, 1)); // 避免“旧到期日~续费当天”这段空窗期被白送(表现为下半月续费有效期多给一个月)
Date baseExpire = order.getExpirationTime();
Date renewStart = (baseExpire != null && baseExpire.after(new Date())) ? baseExpire : new Date();
one.setStartTime(renewStart);
one.setExpirationTime(DateUtil.offset(renewStart, DateField.MONTH, 1));
one.setPayStatus(PAY_STATUS_NO_PAY); one.setPayStatus(PAY_STATUS_NO_PAY);
one.setBatteryDeposit(order.getBatteryDeposit()); one.setBatteryDeposit(order.getBatteryDeposit());
one.setBatteryInsurance(order.getBatteryInsurance()); one.setBatteryInsurance(order.getBatteryInsurance());
@@ -243,11 +266,19 @@ public class OrderPayController extends BaseController {
} }
Order order = new Order(); // 续费时间以父订单当前到期日为准无缝衔接,不信任前端传入的 startTime/expirationTime
order.setOrderId(orderPay.getRentOrderId()); // 避免后台操作时把有效期算多一个月
order.setStartTime(orderPay.getStartTime()); Order parentOrder = orderService.getById(orderPay.getRentOrderId());
order.setExpirationTime(orderPay.getExpirationTime()); if (parentOrder == null) {
orderService.updateById(order); return fail("关联订单不存在");
}
Date parentExpire = parentOrder.getExpirationTime();
Date renewStart = (parentExpire != null && parentExpire.after(new Date())) ? parentExpire : new Date();
Date renewExpire = DateUtil.offset(renewStart, DateField.MONTH, 1);
parentOrder.setStartTime(renewStart);
parentOrder.setExpirationTime(renewExpire);
orderService.updateById(parentOrder);
OrderPay olderPay = orderPayService.getOne(Wrappers.lambdaQuery(OrderPay.class).eq(OrderPay::getOrderNo, orderPay.getOrderNo())); OrderPay olderPay = orderPayService.getOne(Wrappers.lambdaQuery(OrderPay.class).eq(OrderPay::getOrderNo, orderPay.getOrderNo()));
OrderPay newOrderPay = new OrderPay(); OrderPay newOrderPay = new OrderPay();
@@ -255,10 +286,10 @@ public class OrderPayController extends BaseController {
newOrderPay.setOrderPrice(BigDecimal.valueOf(orderPay.getOrderPriceInt())); newOrderPay.setOrderPrice(BigDecimal.valueOf(orderPay.getOrderPriceInt()));
newOrderPay.setStartTime(orderPay.getStartTime()); newOrderPay.setStartTime(renewStart);
newOrderPay.setPayTime(new Date()); newOrderPay.setPayTime(new Date());
newOrderPay.setCreateTime(new Date()); newOrderPay.setCreateTime(new Date());
newOrderPay.setExpirationTime(orderPay.getExpirationTime()); newOrderPay.setExpirationTime(renewExpire);
newOrderPay.setOrderNo(IdUtil.getSnowflakeNextIdStr()); newOrderPay.setOrderNo(IdUtil.getSnowflakeNextIdStr());
newOrderPay.setIsAdminRenew(1);//是否管理员续费订单 newOrderPay.setIsAdminRenew(1);//是否管理员续费订单

View File

@@ -2,6 +2,7 @@ package com.gxwebsoft.shop.controller;
import cn.hutool.core.bean.copier.BeanCopier; import cn.hutool.core.bean.copier.BeanCopier;
import cn.hutool.core.bean.copier.CopyOptions; import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayApiException;
@@ -114,12 +115,12 @@ public class OrderRefundController extends BaseController {
@PutMapping() @PutMapping()
@Transactional @Transactional
public ApiResult<?> update(@RequestBody OrderRefund orderRefund) throws AlipayApiException { public ApiResult<?> update(@RequestBody OrderRefund orderRefund) throws AlipayApiException {
OrderRefund refund = orderRefundService.getById(orderRefund.getOrderRefundId());
BeanCopier.create(orderRefund, refund, CopyOptions.create().ignoreNullValue()).copy(); BeanCopier.create(orderRefund, orderRefund, CopyOptions.create().ignoreNullValue()).copy();
User loginUser = getLoginUser(); User loginUser = getLoginUser();
if (loginUser != null) { if (loginUser != null) {
refund.setOperator(loginUser.getUsername());//操作人 orderRefund.setOperator(loginUser.getUsername());//操作人
} }
Integer auditStatus = orderRefund.getAuditStatus(); Integer auditStatus = orderRefund.getAuditStatus();
@@ -141,6 +142,7 @@ public class OrderRefundController extends BaseController {
} }
order.setReceiptStatus(RECEIPT_STATUS_RETURN); order.setReceiptStatus(RECEIPT_STATUS_RETURN);
order.setOrderStatus(ORDER_STATUS_OVER); order.setOrderStatus(ORDER_STATUS_OVER);
order.setUpdateTime(DateUtil.date());
orderService.updateById(order); orderService.updateById(order);
try { try {
freezeOrderService.unfreeze(order.getOrderId(), refundMoney);//这里有可能出现没有找不到解冻订单的问题 freezeOrderService.unfreeze(order.getOrderId(), refundMoney);//这里有可能出现没有找不到解冻订单的问题
@@ -155,7 +157,8 @@ public class OrderRefundController extends BaseController {
order.setReceiptStatus(RECEIPT_STATUS_YES); order.setReceiptStatus(RECEIPT_STATUS_YES);
orderService.updateById(order); orderService.updateById(order);
} }
orderRefundService.updateById(refund); orderRefund.setUpdateTime(DateUtil.date());
orderRefundService.updateById(orderRefund);
return success("操作成功"); return success("操作成功");
} }

View File

@@ -297,7 +297,7 @@ public class PaymentController extends BaseController {
// log.info("支付宝支付返回信息:{}", response); // log.info("支付宝支付返回信息:{}", response);
if(!response.isSuccess()){ if(!response.isSuccess()){
String subCode = response.getSubCode(); String subCode = response.getSubCode();
if("ACQ.TRADE_HAS_CLOSE".equals(subCode)){ if("ACQ.TRADE_HAS_CLOSE".equals(subCode) || "ACQ.TRADE_HAS_SUCCESS".equals(subCode)){
String orderNoNew = IdUtil.getSnowflakeNextIdStr(); String orderNoNew = IdUtil.getSnowflakeNextIdStr();
log.warn("原支付宝支付订单{}已关闭!重新生成支付订单号{}", orderNo, orderNoNew); log.warn("原支付宝支付订单{}已关闭!重新生成支付订单号{}", orderNo, orderNoNew);
order.setOrderNo(orderNoNew); order.setOrderNo(orderNoNew);
@@ -408,6 +408,8 @@ public class PaymentController extends BaseController {
order.setCurrPeriods(parentOrder.getCurrPeriods()); order.setCurrPeriods(parentOrder.getCurrPeriods());
order.setBatteryRent(parentOrder.getBatteryRent()); order.setBatteryRent(parentOrder.getBatteryRent());
// order.setExpirationTime(DateUtil.offset(order.getExpirationTime(), DateField.MONTH, 1));
order.setExpirationTime(order.getExpirationTime());
orderPayService.updateById(order); orderPayService.updateById(order);
orderService.updateById(parentOrder); orderService.updateById(parentOrder);
@@ -573,7 +575,9 @@ public class PaymentController extends BaseController {
} }
Date expirationTime = parentOrder.getExpirationTime(); Date expirationTime = parentOrder.getExpirationTime();
DateTime nextMonthTime = DateUtil.offsetMonth(expirationTime, 1); // 逾期续费从今天衔接,避免“旧到期日~续费当天”空窗期白送一个月
Date renewBase = (expirationTime != null && expirationTime.after(new Date())) ? expirationTime : new Date();
DateTime nextMonthTime = DateUtil.offsetMonth(renewBase, 1);
parentOrder.setExpirationTime(nextMonthTime); parentOrder.setExpirationTime(nextMonthTime);
orderService.updateById(parentOrder); orderService.updateById(parentOrder);
// 保存续费订单状态 // 保存续费订单状态
@@ -581,7 +585,7 @@ public class PaymentController extends BaseController {
d.setDeliveryStatus(DELIVERY_STATUS_ACCEPT); d.setDeliveryStatus(DELIVERY_STATUS_ACCEPT);
d.setReceiptStatus(RECEIPT_STATUS_YES); d.setReceiptStatus(RECEIPT_STATUS_YES);
d.setOrderStatus(ORDER_STATUS_COMPLETED); d.setOrderStatus(ORDER_STATUS_COMPLETED);
d.setStartTime(expirationTime); d.setStartTime(renewBase);
d.setExpirationTime(nextMonthTime); d.setExpirationTime(nextMonthTime);
orderService.updateById(d); orderService.updateById(d);
} }

View File

@@ -343,6 +343,10 @@ public class Order implements Serializable {
@TableField(exist = false) @TableField(exist = false)
private BigDecimal totalPayPrice; private BigDecimal totalPayPrice;
@ApiModelProperty(value = "订单交费总数")
@TableField(exist = false)
private List<OrderPay> orderPays;
public Integer getRestDay() { public Integer getRestDay() {
if(null != orderStatus){ if(null != orderStatus){
switch(orderStatus){ switch(orderStatus){

View File

@@ -25,6 +25,9 @@
<if test="param.rentOrderId != null"> <if test="param.rentOrderId != null">
AND a.rent_order_id = #{param.rentOrderId} AND a.rent_order_id = #{param.rentOrderId}
</if> </if>
<if test="param.rentOrderIdNotNull != null and param.rentOrderIdNotNull">
AND a.rent_order_id IS NOT NULL
</if>
<if test="param.payStatus != null"> <if test="param.payStatus != null">
AND a.pay_status = #{param.payStatus} AND a.pay_status = #{param.payStatus}
</if> </if>

View File

@@ -9,6 +9,7 @@
LEFT JOIN sys_user b ON a.user_id = b.user_id LEFT JOIN sys_user b ON a.user_id = b.user_id
LEFT JOIN apps_equipment c ON a.order_id = c.order_id LEFT JOIN apps_equipment c ON a.order_id = c.order_id
LEFT JOIN shop_merchant d ON a.merchant_code = d.merchant_code LEFT JOIN shop_merchant d ON a.merchant_code = d.merchant_code
LEFT JOIN shop_order e ON a.order_id = e.order_id
<where> <where>
<if test="param.orderRefundId != null"> <if test="param.orderRefundId != null">
AND a.order_refund_id = #{param.orderRefundId} AND a.order_refund_id = #{param.orderRefundId}
@@ -80,7 +81,7 @@
AND a.create_time &lt;= #{param.createTimeEnd} AND a.create_time &lt;= #{param.createTimeEnd}
</if> </if>
<if test="param.orderNo != null"> <if test="param.orderNo != null">
AND a.order_no = #{param.orderNo} AND e.order_no LIKE CONCAT('%', #{param.orderNo}, '%')
</if> </if>
</where> </where>
</sql> </sql>

View File

@@ -116,6 +116,9 @@ public class OrderPayParam extends BaseParam {
@QueryField(type = QueryType.EQ) @QueryField(type = QueryType.EQ)
private Integer rentOrderId; private Integer rentOrderId;
@ApiModelProperty(value = "仅查询续费单rentOrderId 不为空)")
private Boolean rentOrderIdNotNull;
@ApiModelProperty(value = "电池租金") @ApiModelProperty(value = "电池租金")
@QueryField(type = QueryType.EQ) @QueryField(type = QueryType.EQ)
private BigDecimal batteryRent; private BigDecimal batteryRent;

View File

@@ -33,7 +33,7 @@ public class OrderPayServiceImpl extends ServiceImpl<OrderPayMapper, OrderPay> i
List<OrderPay> list = baseMapper.selectListRel(param); List<OrderPay> list = baseMapper.selectListRel(param);
// 排序 // 排序
PageParam<OrderPay, OrderPayParam> page = new PageParam<>(); PageParam<OrderPay, OrderPayParam> page = new PageParam<>();
page.setDefaultOrder("pay_time desc"); page.setDefaultOrder("id desc");
return page.sortRecords(list); return page.sortRecords(list);
} }

View File

@@ -260,16 +260,17 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
// 主订单 // 主订单
Order parentOrder = orderService.getById(order.getRentOrderId()); Order parentOrder = orderService.getById(order.getRentOrderId());
parentOrder.setCurrPeriods(count + 1); parentOrder.setCurrPeriods(count + 1);
// 更新过期时间延长一个月 // 更新过期时间延长一个月(逾期时从今天衔接,避免空窗期白送)
Date expirationTime = parentOrder.getExpirationTime(); Date expirationTime = parentOrder.getExpirationTime();
DateTime nextMonthTime = DateUtil.offsetMonth(expirationTime, 1); Date renewBase = (expirationTime != null && expirationTime.after(new Date())) ? expirationTime : new Date();
DateTime nextMonthTime = DateUtil.offsetMonth(renewBase, 1);
parentOrder.setExpirationTime(nextMonthTime); parentOrder.setExpirationTime(nextMonthTime);
// 保存续费订单状态 // 保存续费订单状态
// order.setDeliveryStatus(DELIVERY_STATUS_YES); // order.setDeliveryStatus(DELIVERY_STATUS_YES);
order.setDeliveryStatus(DELIVERY_STATUS_ACCEPT); order.setDeliveryStatus(DELIVERY_STATUS_ACCEPT);
order.setReceiptStatus(RECEIPT_STATUS_YES); order.setReceiptStatus(RECEIPT_STATUS_YES);
order.setOrderStatus(ORDER_STATUS_COMPLETED); order.setOrderStatus(ORDER_STATUS_COMPLETED);
order.setStartTime(expirationTime); order.setStartTime(renewBase);
order.setExpirationTime(nextMonthTime); order.setExpirationTime(nextMonthTime);
try { try {

View File

@@ -3,9 +3,9 @@
# 数据源配置 # 数据源配置
spring: spring:
datasource: datasource:
url: jdbc:mysql://1.14.159.185:3318/yunxinwei?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8 url: jdbc:mysql://1Panel-mysql-LCaM:3306/yunxinwei?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
username: yunxinwei username: yunxinwei
password: A56sK6aW2FA3wBy2 password: T4xEYiwTdEmS2ZED
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource type: com.alibaba.druid.pool.DruidDataSource

View File

@@ -1,6 +1,6 @@
# 端口 # 端口
server: server:
port: 9090 port: 9200
# socketIo # socketIo
socketio: socketio:
port: 9190 port: 9190
@@ -47,9 +47,9 @@ spring:
max-request-size: 100MB max-request-size: 100MB
redis: redis:
database: 7 database: 7
host: 127.0.0.1 host: 1Panel-redis-CnJa
port: 6379 port: 6379
password: password: redis_xGXDip
# 邮件服务器配置 # 邮件服务器配置
mail: mail:
@@ -71,7 +71,7 @@ mybatis-plus:
configuration: configuration:
map-underscore-to-camel-case: true map-underscore-to-camel-case: true
cache-enabled: true cache-enabled: true
log-impl: ${LOG_IMPL:org.apache.ibatis.logging.nologging.NoLoggingImpl} log-impl: ${LOG_IMPL:org.apache.ibatis.logging.stdout.StdOutImpl}
global-config: global-config:
:banner: false :banner: false
db-config: db-config:

View File

@@ -6,7 +6,6 @@ import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient; import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayFundAuthOrderUnfreezeRequest; import com.alipay.api.request.AlipayFundAuthOrderUnfreezeRequest;
import com.alipay.api.response.AlipayFundAuthOrderUnfreezeResponse; import com.alipay.api.response.AlipayFundAuthOrderUnfreezeResponse;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.gxwebsoft.apps.service.HualalaService; import com.gxwebsoft.apps.service.HualalaService;
import com.gxwebsoft.apps.service.TestDataService; import com.gxwebsoft.apps.service.TestDataService;
import com.gxwebsoft.common.core.utils.AlipayConfigUtil; import com.gxwebsoft.common.core.utils.AlipayConfigUtil;
@@ -15,7 +14,6 @@ import com.gxwebsoft.common.system.mapper.UserMapper;
import com.gxwebsoft.common.system.service.SettingService; import com.gxwebsoft.common.system.service.SettingService;
import com.gxwebsoft.common.system.service.UserService; import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.love.service.CertificateService; import com.gxwebsoft.love.service.CertificateService;
import com.gxwebsoft.shop.entity.FreezeOrder;
import com.gxwebsoft.shop.mapper.OrderGoodsMapper; import com.gxwebsoft.shop.mapper.OrderGoodsMapper;
import com.gxwebsoft.shop.mapper.OrderMapper; import com.gxwebsoft.shop.mapper.OrderMapper;
import com.gxwebsoft.shop.service.FreezeOrderService; import com.gxwebsoft.shop.service.FreezeOrderService;
@@ -23,14 +21,10 @@ import com.gxwebsoft.shop.service.OrderService;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/** /**
* Created by WebSoft on 2020-03-23 23:37 * Created by WebSoft on 2020-03-23 23:37