商城代码同步
parent
613300778f
commit
063a13f497
|
|
@ -30,6 +30,8 @@
|
|||
<artifactId>springfox-boot-starter</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
|
||||
<!-- 防止进入swagger页面报类型转换错误,排除3.0.0中的引用,手动增加1.6.2版本 -->
|
||||
<dependency>
|
||||
<groupId>io.swagger</groupId>
|
||||
|
|
@ -61,6 +63,23 @@
|
|||
<artifactId>ruoyi-generator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Hutool工具类 -->
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.8.25</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>1.2.37</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.5</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
package com.ruoyi.web.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.web.domain.Device;
|
||||
import com.ruoyi.web.service.DeviceService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
|
||||
/**
|
||||
* 设备信息管理
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mall/device")
|
||||
public class DeviceController extends BaseController {
|
||||
|
||||
@Resource
|
||||
private DeviceService deviceService;
|
||||
|
||||
/**
|
||||
* 获取指定门店的设备信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Anonymous
|
||||
@GetMapping(value = "/getDevice/{storeId}")
|
||||
public AjaxResult getBrandTree(@PathVariable String storeId) {
|
||||
return AjaxResult.success(deviceService.getDevice(storeId));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定门店的设备信息
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Anonymous
|
||||
@GetMapping(value = "/getSheXiangTou/{storeId}")
|
||||
public AjaxResult getSheXiangTou(@PathVariable String storeId) {
|
||||
return AjaxResult.success(deviceService.getSheXiangTou(storeId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加门店设备
|
||||
*
|
||||
* @param device
|
||||
* @return
|
||||
*/
|
||||
@Anonymous
|
||||
@PostMapping("/add")
|
||||
public AjaxResult addDevice(@RequestBody Device device) {
|
||||
device.setCreateBy(getUsername());
|
||||
return toAjax(deviceService.addDevice(device));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.ruoyi.web.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.web.domain.DoorScanRecord;
|
||||
import com.ruoyi.web.domain.Order;
|
||||
import com.ruoyi.web.service.DoorScanRecordService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
|
||||
|
||||
/**
|
||||
* 扫码记录管理
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mall/doorScanRecord")
|
||||
public class DoorScanRecordController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private DoorScanRecordService doorScanRecordService;
|
||||
|
||||
/**
|
||||
* 新增开门记录(扫码成功时调用)
|
||||
*/
|
||||
@Anonymous
|
||||
@Log(title = "扫码记录管理", businessType = BusinessType.INSERT)
|
||||
@PostMapping("/add")
|
||||
public AjaxResult addDoorScanRecord(@RequestBody DoorScanRecord doorScanRecord) {
|
||||
doorScanRecord.setCreateBy(getUsername());
|
||||
doorScanRecord.setScanTime(new Date());
|
||||
return toAjax(doorScanRecordService.addDoorScanRecord(doorScanRecord));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取扫码开门记录
|
||||
*
|
||||
* @param doorScanRecord
|
||||
* @return
|
||||
*/
|
||||
@Anonymous
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(DoorScanRecord doorScanRecord) {
|
||||
return AjaxResult.success(doorScanRecordService.selectScanRecordList(doorScanRecord));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据年月日查询进店人数
|
||||
*
|
||||
* @param date 日期 (格式: yyyy-MM-dd)
|
||||
* @return 进店人数统计结果
|
||||
*/
|
||||
@GetMapping("/count")
|
||||
public AjaxResult getEnterCount(@RequestParam String date,@RequestParam Integer storeId) {
|
||||
try {
|
||||
Integer count = doorScanRecordService.getEnterCountByDate(date,storeId);
|
||||
return AjaxResult.success(count);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return AjaxResult.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package com.ruoyi.web.controller;
|
||||
|
||||
import com.ruoyi.common.annotation.Anonymous;
|
||||
import com.ruoyi.common.core.controller.BaseController;
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
|
||||
import com.ruoyi.web.domain.InstallTask;
|
||||
import com.ruoyi.web.service.InstallTaskService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 安装任务查看
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mall/install/task")
|
||||
public class InstallTaskController extends BaseController {
|
||||
|
||||
@Autowired
|
||||
private InstallTaskService installTaskService;
|
||||
|
||||
/**
|
||||
* 门店提交安装任务
|
||||
*/
|
||||
@PostMapping("/submit")
|
||||
public AjaxResult submit(@RequestBody InstallTask installTask) {
|
||||
installTask.setCreateBy(SecurityUtils.getUsername());
|
||||
return toAjax(installTaskService.insertInstallTask(installTask));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询安装任务列表
|
||||
*/
|
||||
@Anonymous
|
||||
// @PreAuthorize("@ss.hasPermi('mall:install:list')")
|
||||
@GetMapping("/list")
|
||||
public TableDataInfo list(InstallTask installTask) {
|
||||
startPage();
|
||||
List<InstallTask> list = installTaskService.selectInstallTaskList(installTask);
|
||||
return getDataTable(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID查询详情
|
||||
*/
|
||||
@GetMapping("/{id}")
|
||||
public AjaxResult getInfo(@PathVariable Long id) {
|
||||
return AjaxResult.success(installTaskService.selectInstallTaskById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改安装任务
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('mall:install:edit')")
|
||||
@PutMapping("/edit")
|
||||
public AjaxResult edit(@RequestBody InstallTask installTask) {
|
||||
installTask.setUpdateBy(SecurityUtils.getUsername());
|
||||
return toAjax(installTaskService.updateInstallTask(installTask));
|
||||
}
|
||||
|
||||
/**
|
||||
* 指派任务
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('mall:install:assign')")
|
||||
@PostMapping("/assign")
|
||||
public AjaxResult assign(@RequestParam Long id,
|
||||
@RequestParam Long assigneeId,
|
||||
@RequestParam String assigneeName) {
|
||||
return toAjax(installTaskService.assignTask(id, assigneeId, assigneeName,
|
||||
SecurityUtils.getUsername()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 完成任务安装
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('mall:install:complete')")
|
||||
@PostMapping("/complete")
|
||||
public AjaxResult complete(@RequestBody InstallTask installTask) {
|
||||
installTask.setUpdateBy(SecurityUtils.getUsername());
|
||||
return toAjax(installTaskService.completeTask(installTask));
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除安装任务
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('mall:install:remove')")
|
||||
@DeleteMapping("/{id}")
|
||||
public AjaxResult remove(@PathVariable Long id) {
|
||||
return toAjax(installTaskService.deleteInstallTaskById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*/
|
||||
@PreAuthorize("@ss.hasPermi('mall:install:remove')")
|
||||
@DeleteMapping("/batch/{ids}")
|
||||
public AjaxResult batchRemove(@PathVariable Long[] ids) {
|
||||
return toAjax(installTaskService.deleteInstallTaskByIds(ids));
|
||||
}
|
||||
}
|
||||
|
|
@ -5,18 +5,26 @@ import com.ruoyi.common.annotation.Anonymous;
|
|||
import com.ruoyi.common.core.controller.BaseController;
|
||||
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.core.page.TableDataInfo;
|
||||
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.web.domain.Order;
|
||||
import com.ruoyi.web.dto.CreateOrderDTO;
|
||||
import com.ruoyi.web.dto.ScanPayRequest;
|
||||
import com.ruoyi.web.dto.TianquePayCallbackDTO;
|
||||
import com.ruoyi.web.dto.TradeQueryDTO;
|
||||
import com.ruoyi.web.service.OrderService;
|
||||
import org.checkerframework.checker.units.qual.A;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* 商品订单管理
|
||||
* 商品订单支付管理
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/mall/order")
|
||||
|
|
@ -25,14 +33,124 @@ public class OrderController extends BaseController {
|
|||
@Resource
|
||||
private OrderService orderService;
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OrderController.class);
|
||||
|
||||
|
||||
/**
|
||||
* 获取指定门店的支付订单数量,和支付金额总额
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Anonymous
|
||||
@GetMapping(value = "/getOrderCount/{storeId}")
|
||||
public AjaxResult getOrderCount(@PathVariable String storeId) {
|
||||
return AjaxResult.success(orderService.getOrderCount(storeId));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付结果主动查询,前端使用去查询支付状态
|
||||
*/
|
||||
@Anonymous
|
||||
@PostMapping("/tradeQuery")
|
||||
public AjaxResult tradeQuery(@RequestBody TradeQueryDTO tradeQueryDTO) throws Exception {
|
||||
return orderService.tradeQuery(tradeQueryDTO);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 被扫 创建订单,返回订单号
|
||||
*/
|
||||
@Anonymous
|
||||
@PostMapping("/addOrder")
|
||||
public AjaxResult addOrder(@Validated @RequestBody CreateOrderDTO createOrderDTO) {
|
||||
createOrderDTO.setCreateBy(getUsername());
|
||||
return orderService.addOrder(createOrderDTO);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 被扫 发起支付请求
|
||||
*/
|
||||
@Anonymous
|
||||
@PostMapping("/addOrderReverseScan")
|
||||
public AjaxResult addOrderReverseScan(@RequestBody ScanPayRequest scanPayRequest) throws Exception {
|
||||
scanPayRequest.setCreateBy(getUsername());
|
||||
return AjaxResult.success(orderService.addOrderReverseScan(scanPayRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单列表
|
||||
*/
|
||||
// @PreAuthorize("@ss.hasPermi('mall:order:list')")
|
||||
@Anonymous
|
||||
@GetMapping("/list")
|
||||
public AjaxResult list(Order order) {
|
||||
return AjaxResult.success( orderService.selectOrderList(order));
|
||||
@PostMapping("/list")
|
||||
public AjaxResult list(@RequestBody Order order) {
|
||||
return AjaxResult.success(orderService.selectOrderList(order));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建订单并支付
|
||||
* 用户点击支付时调用
|
||||
*/
|
||||
@Anonymous
|
||||
@PostMapping("/addPay")
|
||||
public AjaxResult addPay(@Validated @RequestBody CreateOrderDTO createOrderDTO) {
|
||||
createOrderDTO.setCreateBy(getUsername());
|
||||
return orderService.addPay(createOrderDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 天阙平台支付回调接口
|
||||
* 支付结果异步通知
|
||||
* <p>
|
||||
* 接口说明:
|
||||
* 交易成功后,天阙平台会将成功状态通知到交易上送的回调地址(notifyUrl)
|
||||
* 合作方接收到通知结果后需要返回:{"code":"success", "msg":"成功"}
|
||||
* 天阙平台收到正确应答后将不再进行通知,否则将继续推送10次
|
||||
* * 失败返回:
|
||||
* {
|
||||
* "code": "fail",
|
||||
* "msg": "错误信息"
|
||||
* }
|
||||
* 注:失败的交易不推送回调,交易成功才会回调
|
||||
*/
|
||||
@PostMapping("/notify")
|
||||
@Anonymous
|
||||
public Map<String, String> notify(@RequestBody String callbackData) {
|
||||
log.info("收到天阙平台支付回调, data:{}", callbackData);
|
||||
Map<String, String> response = new HashMap<>();
|
||||
try {
|
||||
//解析回调数据
|
||||
TianquePayCallbackDTO request = orderService.parseCallback(callbackData);
|
||||
//检查业务编码(0000表示成功)
|
||||
if (!request.isSuccess()) {
|
||||
log.info("交易失败回调, ordNo:{}, bizCode:{}, bizMsg:{}",
|
||||
request.getOrdNo(), request.getBizCode(), request.getBizMsg());
|
||||
response.put("code", "fail");
|
||||
response.put("msg", "交易失败");
|
||||
return response;
|
||||
}
|
||||
//处理支付回调业务逻辑
|
||||
orderService.handleTianquePayCallback(request);
|
||||
|
||||
//天阙平台要求的格式
|
||||
response.put("code", "success");
|
||||
response.put("msg", "成功");
|
||||
|
||||
log.info("天阙平台支付回调处理成功, ordNo:{}, uuid:{}",
|
||||
request.getOrdNo(), request.getUuid());
|
||||
} catch (ServiceException e) {
|
||||
log.error("天阙平台支付回调业务处理失败: {}", e.getMessage());
|
||||
response.put("code", "fail");
|
||||
response.put("msg", e.getMessage());
|
||||
} catch (Exception e) {
|
||||
log.error("天阙平台支付回调处理异常", e);
|
||||
response.put("code", "fail");
|
||||
response.put("msg", "系统异常");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.ruoyi.common.utils.file.FileUploadUtils;
|
|||
import com.ruoyi.common.utils.file.MimeTypeUtils;
|
||||
import com.ruoyi.common.utils.poi.ExcelUtil;
|
||||
import com.ruoyi.web.domain.Product;
|
||||
import com.ruoyi.web.dto.BatchBarcodeQueryDTO;
|
||||
import com.ruoyi.web.dto.BatchStoreDTO;
|
||||
import com.ruoyi.web.service.ProductService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
|
@ -33,6 +34,22 @@ public class ProductStoreController extends BaseController {
|
|||
@Autowired
|
||||
private ProductService productService;
|
||||
|
||||
/**
|
||||
* 批量根据条码查询商品
|
||||
* 支持一次查询多个条码,返回商品列表
|
||||
*/
|
||||
@PostMapping("/getByBarcodes")
|
||||
public AjaxResult getByBarcodes(@RequestBody BatchBarcodeQueryDTO dto) {
|
||||
if (dto.getBarcodes() == null || dto.getBarcodes().isEmpty()) {
|
||||
return AjaxResult.error("条码列表不能为空");
|
||||
}
|
||||
if (dto.getStoreId() == null) {
|
||||
return AjaxResult.error("门店id不能为空");
|
||||
}
|
||||
return AjaxResult.success(productService.getByBarcodes(dto));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取商品列表
|
||||
*/
|
||||
|
|
@ -77,7 +94,6 @@ public class ProductStoreController extends BaseController {
|
|||
}
|
||||
//todo 这里可以国码有的时候使用国码的图片路径
|
||||
|
||||
|
||||
// TODO: 2026/1/14 这个file 为空的时候 就使用国码里面的图片,不为空代表用户自己选择了图片上传 用自己服务器上的
|
||||
//国码后续添加
|
||||
if (file != null) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import lombok.Data;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 商品分类
|
||||
*/
|
||||
@Data
|
||||
public class Classification extends BaseEntity {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
package com.ruoyi.web.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 设备信息表 mall_device_info
|
||||
*
|
||||
*/
|
||||
@Data
|
||||
public class Device extends BaseEntity {
|
||||
|
||||
/** 设备ID */
|
||||
private Long id;
|
||||
|
||||
/** 所属门店ID,关联mall_store表的store_id */
|
||||
private Long storeId;
|
||||
|
||||
/** 设备编号(厂商唯一标识) */
|
||||
private String deviceCode;
|
||||
|
||||
/** 设备名称 */
|
||||
private String deviceName;
|
||||
|
||||
/** 设备类型:1-摄像头 2-扫码枪 3-打印机 4-收银机 */
|
||||
private Integer deviceType;
|
||||
|
||||
/** 设备品牌 */
|
||||
private String deviceBrand;
|
||||
|
||||
/** 设备型号 */
|
||||
private String deviceModel;
|
||||
|
||||
/** 通道 */
|
||||
private String channel;
|
||||
|
||||
/** MAC地址 */
|
||||
private String macAddress;
|
||||
|
||||
/** 设备授权码/密钥(前端调设备用) */
|
||||
private String authCode;
|
||||
|
||||
/** 安装日期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date installDate;
|
||||
|
||||
/** 有效期 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expireDate;
|
||||
|
||||
/** 状态:0-离线 1-在线 2-故障 3-已报废 */
|
||||
private Integer deviceStatus;
|
||||
|
||||
/** 最后心跳时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date lastHeartbeat;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package com.ruoyi.web.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 扫码记录
|
||||
*/
|
||||
@Data
|
||||
public class DoorScanRecord extends BaseEntity {
|
||||
|
||||
private Long id;
|
||||
|
||||
private String operatorName; // 扫码人姓名
|
||||
|
||||
private String operatorPhone; // 扫码人手机号
|
||||
|
||||
private Integer storeId; // 门店ID
|
||||
|
||||
private String shopName; // 门店名称
|
||||
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date scanTime; // 扫码时间
|
||||
|
||||
private String scanType; //'扫码类型 0:进门 1:出门'
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,107 @@
|
|||
package com.ruoyi.web.domain;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 安装任务表 mall_install_task
|
||||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Data
|
||||
public class InstallTask extends BaseEntity {
|
||||
|
||||
/** 任务ID */
|
||||
private Long id;
|
||||
|
||||
/** 任务编号 */
|
||||
private String taskNo;
|
||||
|
||||
/** 申请门店ID */
|
||||
private Long storeId;
|
||||
|
||||
/** 门店名称 */
|
||||
private String storeName;
|
||||
|
||||
/** 联系人 */
|
||||
private String contactPerson;
|
||||
|
||||
/** 联系电话 */
|
||||
private String contactPhone;
|
||||
|
||||
/** 设备类型:1-摄像头 2-扫码枪 3-打印机 4-收银机 */
|
||||
private Integer deviceType;
|
||||
|
||||
/** 期望设备型号 */
|
||||
private String deviceModel;
|
||||
|
||||
/** 安装地址 */
|
||||
private String installAddress;
|
||||
|
||||
/** 期望安装时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date expectedTime;
|
||||
|
||||
/** 备注/特殊要求 */
|
||||
private String remark;
|
||||
|
||||
/** 状态:0-待处理 1-已接单 2-安装中 3-已完成 4-已取消 */
|
||||
private Integer taskStatus;
|
||||
|
||||
/** 指派人ID */
|
||||
private Long assigneeId;
|
||||
|
||||
/** 指派人姓名 */
|
||||
private String assigneeName;
|
||||
|
||||
/** 指派时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date assignTime;
|
||||
|
||||
/** 安装的设备ID */
|
||||
private Long deviceId;
|
||||
|
||||
/** 设备编号 */
|
||||
private String deviceCode;
|
||||
|
||||
/** 设备授权码 */
|
||||
private String authCode;
|
||||
|
||||
/** 实际安装时间 */
|
||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date installTime;
|
||||
|
||||
/** 安装照片URL */
|
||||
private String installPhoto;
|
||||
|
||||
/** 完成备注 */
|
||||
private String completeRemark;
|
||||
|
||||
|
||||
/** 设备类型描述 */
|
||||
public String getDeviceTypeDesc() {
|
||||
if (deviceType == null) return "";
|
||||
switch (deviceType) {
|
||||
case 1: return "摄像头";
|
||||
case 2: return "扫码枪";
|
||||
case 3: return "打印机";
|
||||
case 4: return "收银机";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
|
||||
/** 任务状态描述 */
|
||||
public String getTaskStatusDesc() {
|
||||
if (taskStatus == null) return "";
|
||||
switch (taskStatus) {
|
||||
case 0: return "待处理";
|
||||
case 1: return "已接单";
|
||||
case 2: return "安装中";
|
||||
case 3: return "已完成";
|
||||
case 4: return "已取消";
|
||||
default: return "未知";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -33,7 +33,7 @@ public class Order extends BaseEntity {
|
|||
/**
|
||||
* 支付金额
|
||||
*/
|
||||
private BigDecimal payAmount;
|
||||
private String payAmount;
|
||||
|
||||
/**
|
||||
* 支付状态: 0-未支付,1-支付中,2-已支付,3-支付失败,4-已退款
|
||||
|
|
@ -46,6 +46,11 @@ public class Order extends BaseEntity {
|
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||
private Date payTime;
|
||||
|
||||
/**
|
||||
* 商品条码
|
||||
*/
|
||||
private String productBarCode;
|
||||
|
||||
/**
|
||||
* 订单商品明细列表
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import com.ruoyi.common.core.domain.BaseEntity;
|
|||
import lombok.Data;
|
||||
|
||||
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
|
|
@ -25,9 +27,16 @@ public class OrderItem extends BaseEntity {
|
|||
*/
|
||||
private Long orderId;
|
||||
|
||||
|
||||
/**
|
||||
* 商品条码
|
||||
*/
|
||||
private String productBarCode;
|
||||
|
||||
/**
|
||||
* 商品ID
|
||||
*/
|
||||
@NotNull(message = "商品ID不能为空")
|
||||
private Long productId;
|
||||
|
||||
/**
|
||||
|
|
@ -58,6 +67,8 @@ public class OrderItem extends BaseEntity {
|
|||
/**
|
||||
* 购买数量
|
||||
*/
|
||||
@NotNull(message = "数量不能为空")
|
||||
@Min(value = 1, message = "数量必须大于0")
|
||||
private Integer quantity;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -85,16 +85,16 @@ public class Product extends BaseEntity {
|
|||
/**
|
||||
* 门店销售价
|
||||
*/
|
||||
private BigDecimal storePrice;
|
||||
private String storePrice;
|
||||
/**
|
||||
* 成本价
|
||||
*/
|
||||
private BigDecimal costPrice;
|
||||
private String costPrice;
|
||||
|
||||
/**
|
||||
* 市场价
|
||||
*/
|
||||
private Long originalPrice;
|
||||
private String originalPrice;
|
||||
|
||||
/**
|
||||
* 门店ID
|
||||
|
|
@ -117,4 +117,15 @@ public class Product extends BaseEntity {
|
|||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 是否查询临期商品
|
||||
*/
|
||||
private Boolean isExpiring;
|
||||
|
||||
/**
|
||||
* 是否查询过期商品
|
||||
*/
|
||||
private Boolean isExpired;
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,79 @@
|
|||
package com.ruoyi.web.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 天阙支付响应
|
||||
*/
|
||||
@Data
|
||||
public class TianquePayResponse {
|
||||
|
||||
/** 返回码:0000-成功 */
|
||||
private String code;
|
||||
|
||||
/** 返回信息 */
|
||||
private String msg;
|
||||
|
||||
/** 签名 */
|
||||
private String sign;
|
||||
|
||||
/** 签名类型 */
|
||||
private String signType;
|
||||
|
||||
/** 组织机构ID */
|
||||
private String orgId;
|
||||
|
||||
/** 请求ID */
|
||||
private String reqId;
|
||||
|
||||
/** 响应数据 */
|
||||
private RespData respData;
|
||||
|
||||
@Data
|
||||
public static class RespData {
|
||||
/** 业务码:0000-成功 */
|
||||
private String bizCode;
|
||||
|
||||
/** 业务信息 */
|
||||
private String bizMsg;
|
||||
|
||||
/** 落单号 */
|
||||
private String sxfUuid;
|
||||
|
||||
/** 商户订单号 */
|
||||
private String ordNo;
|
||||
|
||||
/** 天阙平台订单号 */
|
||||
private String uuid;
|
||||
|
||||
/** 预支付ID */
|
||||
private String prepayId;
|
||||
|
||||
/** 支付AppID */
|
||||
private String payAppId;
|
||||
|
||||
/** 时间戳 */
|
||||
private String payTimeStamp;
|
||||
|
||||
/** 随机字符串 */
|
||||
private String paynonceStr;
|
||||
|
||||
/** 支付包 */
|
||||
private String payPackage;
|
||||
|
||||
/** 签名类型 */
|
||||
private String paySignType;
|
||||
|
||||
/** 支付签名 */
|
||||
private String paySign;
|
||||
|
||||
/** 商户号 */
|
||||
private String partnerId;
|
||||
|
||||
/** 跳转地址 */
|
||||
private String redirectUrl;
|
||||
|
||||
/** 来源 */
|
||||
private String source;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
package com.ruoyi.web.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
public class ApiRequestBean<T> implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = -9073910802005694017L;
|
||||
|
||||
/**
|
||||
* 合作机构id
|
||||
*/
|
||||
private String orgId;
|
||||
|
||||
/**
|
||||
* 请求id
|
||||
*/
|
||||
private String reqId;
|
||||
|
||||
/**
|
||||
* 请求接口版本1.0
|
||||
*/
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 代理商身份标识
|
||||
*/
|
||||
private String agentId;
|
||||
|
||||
/**
|
||||
* 业务员编号
|
||||
*/
|
||||
private String salesCode;
|
||||
|
||||
/**
|
||||
* 业务数据
|
||||
*/
|
||||
private T reqData;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 签名类型
|
||||
*/
|
||||
private String signType;
|
||||
|
||||
/**
|
||||
* 请求时间
|
||||
*/
|
||||
private String timestamp;
|
||||
|
||||
public String getOrgId() {
|
||||
return orgId;
|
||||
}
|
||||
|
||||
public void setOrgId(String orgId) {
|
||||
this.orgId = orgId;
|
||||
}
|
||||
|
||||
public String getReqId() {
|
||||
return reqId;
|
||||
}
|
||||
|
||||
public void setReqId(String reqId) {
|
||||
this.reqId = reqId;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getAgentId() {
|
||||
return agentId;
|
||||
}
|
||||
|
||||
public void setAgentId(String agentId) {
|
||||
this.agentId = agentId;
|
||||
}
|
||||
|
||||
public String getSalesCode() {
|
||||
return salesCode;
|
||||
}
|
||||
|
||||
public void setSalesCode(String salesCode) {
|
||||
this.salesCode = salesCode;
|
||||
}
|
||||
|
||||
public T getReqData() {
|
||||
return reqData;
|
||||
}
|
||||
|
||||
public void setReqData(T reqData) {
|
||||
this.reqData = reqData;
|
||||
}
|
||||
|
||||
public String getSign() {
|
||||
return sign;
|
||||
}
|
||||
|
||||
public void setSign(String sign) {
|
||||
this.sign = sign;
|
||||
}
|
||||
|
||||
public String getSignType() {
|
||||
return signType;
|
||||
}
|
||||
|
||||
public void setSignType(String signType) {
|
||||
this.signType = signType;
|
||||
}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ruoyi.web.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BatchBarcodeQueryDTO {
|
||||
|
||||
//商品条码
|
||||
private List<String> barcodes;
|
||||
|
||||
// 可选:门店ID,用于过滤该门店的商品
|
||||
private Long storeId;
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.ruoyi.web.dto;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import com.ruoyi.web.domain.OrderItem;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class CreateOrderDTO extends BaseEntity {
|
||||
|
||||
/** 门店ID */
|
||||
@NotNull(message = "门店ID不能为空")
|
||||
private Integer storeId;
|
||||
|
||||
/** 商品列表 */
|
||||
@NotEmpty(message = "商品列表不能为空")
|
||||
private List<OrderItem> items;
|
||||
|
||||
//总金额
|
||||
@NotNull(message = "订单商品总金额不能为空")
|
||||
private String totalAmount;
|
||||
|
||||
/** 用户openId */
|
||||
private String openId;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
package com.ruoyi.web.dto;
|
||||
|
||||
import com.ruoyi.common.core.domain.BaseEntity;
|
||||
import lombok.Data;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ScanPayRequest extends BaseEntity {
|
||||
|
||||
/**
|
||||
* 商户编号,必填,15位
|
||||
*/
|
||||
private String mno;
|
||||
|
||||
/**
|
||||
* 商户订单号,必填,64位,字母/数字/下划线,需保证在合作方系统中不重复
|
||||
*/
|
||||
private String ordNo;
|
||||
|
||||
/**
|
||||
* 订单总金额(元),必填,格式:#######.##
|
||||
*/
|
||||
private BigDecimal amt;
|
||||
|
||||
/**
|
||||
* 授权码(付款码),必填,64位
|
||||
* 通过扫码枪/声波获取设备获取的支付宝/微信/银联付款码
|
||||
*/
|
||||
private String authCode;
|
||||
|
||||
/**
|
||||
* 支付渠道,当scene枚举为3或6时必填
|
||||
* WECHAT:微信, ALIPAY:支付宝, UNIONPAY:银联, DCEP:数字人民币, YZF:翼支付
|
||||
*/
|
||||
private String payType;
|
||||
|
||||
/**
|
||||
* 支付场景,选填,默认1
|
||||
* 1:付款码, 3:刷脸, 6:离线刷脸
|
||||
*/
|
||||
private String scene = "1";
|
||||
|
||||
/**
|
||||
* 订单标题,必填,256位
|
||||
*/
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 商户交易终端ip地址,必填,16位
|
||||
*/
|
||||
private String trmIp;
|
||||
|
||||
/**
|
||||
* 微信subAppId,选填,32位
|
||||
*/
|
||||
private String subAppid;
|
||||
|
||||
/**
|
||||
* 微信优惠参数,选填,32位
|
||||
* 微信的商品标记,核销优惠券时必传
|
||||
*/
|
||||
private String wxGoodsTag;
|
||||
|
||||
/**
|
||||
* 订单优惠标识,选填,2位
|
||||
* 00:是, 01:否,有优惠参数上传时必传00
|
||||
*/
|
||||
private String goodsTag;
|
||||
|
||||
/**
|
||||
* 微信电子发票,选填,2位
|
||||
* 00:是, 01:否
|
||||
*/
|
||||
private String needReceipt;
|
||||
|
||||
/**
|
||||
* 支付宝参与优惠金额(元),选填
|
||||
*/
|
||||
private BigDecimal discountAmt;
|
||||
|
||||
/**
|
||||
* 支付宝不参与优惠金额(元),选填
|
||||
*/
|
||||
private BigDecimal unDiscountAmt;
|
||||
|
||||
/**
|
||||
* 支付宝花呗分期数,选填
|
||||
* 3:分3期, 6:分6期, 12:分12期
|
||||
*/
|
||||
private String hbFqNum;
|
||||
|
||||
/**
|
||||
* 商家承担手续费比例,选填
|
||||
* 0:用户承担, 100:商家承担
|
||||
*/
|
||||
private String hbFqPercent;
|
||||
|
||||
/**
|
||||
* 限制卡类型,选填,默认00
|
||||
* 00:全部支持, 01:限定不能使用信用卡等
|
||||
*/
|
||||
private String limitPay = "00";
|
||||
|
||||
/**
|
||||
* 订单失效时间(单位分钟),选填,1-1440,默认5分钟
|
||||
*/
|
||||
private Integer timeExpire = 5;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 单次分账类型,选填,默认01
|
||||
* 00:单次分账, 01:不分账, 04:多次分账
|
||||
*/
|
||||
private String ledgerAccountFlag = "01";
|
||||
|
||||
/**
|
||||
* 分账有效时间(单位天),选填
|
||||
* ledgerAccountFlag为00或04时必传
|
||||
*/
|
||||
private String ledgerAccountEffectTime;
|
||||
|
||||
/**
|
||||
* 同步分账规则,选填,与ledgerAccountFlag互斥
|
||||
*/
|
||||
private List<String> fusruleId;
|
||||
|
||||
/**
|
||||
* 支付结果通知地址,选填,256位
|
||||
* 被扫输密时上送,交易成功会异步通知
|
||||
*/
|
||||
private String notifyUrl;
|
||||
|
||||
/**
|
||||
* 终端号,选填,32位
|
||||
* 银联:8位,支付宝微信:不超过32位
|
||||
*/
|
||||
private String ylTrmNo;
|
||||
|
||||
/**
|
||||
* 证件类型,选填
|
||||
* identityFlag=="00"时必传,IDCARD:大陆
|
||||
*/
|
||||
private String buyerIdType;
|
||||
|
||||
/**
|
||||
* 证件号,选填,18位
|
||||
* identityFlag=="00"时必传
|
||||
*/
|
||||
private String buyerIdNo;
|
||||
|
||||
/**
|
||||
* 买家姓名,选填,32位
|
||||
* identityFlag=="00"时必传
|
||||
*/
|
||||
private String buyerName;
|
||||
|
||||
/**
|
||||
* 手机号,选填,11位
|
||||
* identityFlag=="00"时必传
|
||||
*/
|
||||
private String mobileNum;
|
||||
|
||||
/**
|
||||
* 门店编号,选填,32位,对应支付宝store_id
|
||||
*/
|
||||
private String storeId;
|
||||
|
||||
/**
|
||||
* 蚂蚁门店编号,选填
|
||||
*/
|
||||
private String alipayStoreId;
|
||||
|
||||
/**
|
||||
* 天阙终端编号,选填,128位
|
||||
* 天阙后台绑定终端后生成的终端唯一标识
|
||||
*/
|
||||
private String deviceNo;
|
||||
|
||||
/**
|
||||
* 天阙门店编号,选填,32位
|
||||
*/
|
||||
private String storeNum;
|
||||
|
||||
/**
|
||||
* 外部业务号,选填
|
||||
*/
|
||||
private String senceNo;
|
||||
|
||||
/**
|
||||
* 加密随机因子,选填,10位
|
||||
*/
|
||||
private String encryptRandNum;
|
||||
|
||||
/**
|
||||
* 密文数据,选填,16位
|
||||
*/
|
||||
private String secretText;
|
||||
|
||||
/**
|
||||
* 终端经度,选填,16位
|
||||
* +表示东经,-表示西经
|
||||
*/
|
||||
private String longitude;
|
||||
|
||||
/**
|
||||
* 终端纬度,选填,16位
|
||||
* +表示北纬,-表示南纬
|
||||
*/
|
||||
private String latitude;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 支付宝校园ID,选填,20位,支付宝未来校园活动必传
|
||||
*/
|
||||
private String eduSchoolId;
|
||||
|
||||
/**
|
||||
* 支付宝校园场景,选填,30位,支付宝未来校园活动必传
|
||||
*/
|
||||
private String eduScene;
|
||||
|
||||
/**
|
||||
* 支付宝业务拓展字段,选填
|
||||
* 对应支付宝business_params字段
|
||||
*/
|
||||
private String zfbBusinessParams;
|
||||
|
||||
/**
|
||||
* 扩展字段,选填,128位
|
||||
*/
|
||||
private String extend;
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
package com.ruoyi.web.dto;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 天阙平台支付回调请求
|
||||
* 对应文档中的回调参数
|
||||
*/
|
||||
@Data
|
||||
public class TianquePayCallbackDTO {
|
||||
|
||||
/** 响应时间戳 */
|
||||
private String timeStamp;
|
||||
|
||||
/** 业务数据签名结果 */
|
||||
private String sign;
|
||||
|
||||
/** 商户编号 */
|
||||
private String mno;
|
||||
|
||||
/** 商户订单号 */
|
||||
private String ordNo;
|
||||
|
||||
/** 天阙平台订单号 */
|
||||
private String uuid;
|
||||
|
||||
/** 交易支付完成时间,格式:YYYYMMDDHHmmss */
|
||||
private String payTime;
|
||||
|
||||
/** 订单总金额(元),格式:#######.## */
|
||||
private String amt;
|
||||
|
||||
/** 支付渠道 */
|
||||
private String payType;
|
||||
|
||||
/** 交易方式:00 主扫 01 被扫 02 公众号 03 小程序 */
|
||||
private String payWay;
|
||||
|
||||
/** 终端号 */
|
||||
private String ylTrmNo;
|
||||
|
||||
/** 原商户订单号(退款通知返回) */
|
||||
private String origOrdNo;
|
||||
|
||||
/** 原天阙订单号(退款通知返回) */
|
||||
private String origUuid;
|
||||
|
||||
/** 支付场景:1 付款码 2 声波 3 刷脸 */
|
||||
private String scene;
|
||||
|
||||
/** 买家用户号 */
|
||||
private String buyerId;
|
||||
|
||||
/** 支付宝买家登录账号 */
|
||||
private String buyerAccount;
|
||||
|
||||
/** 微信/支付宝流水号 */
|
||||
private String transactionId;
|
||||
|
||||
/** 借贷标识:1 借记卡 2 贷记卡 3 其他 */
|
||||
private String drType;
|
||||
|
||||
/** 消费者付款金额 */
|
||||
private String totalOffstAmt;
|
||||
|
||||
/** 商家入账金额 */
|
||||
private String settleAmt;
|
||||
|
||||
/** 付款银行 */
|
||||
private String payBank;
|
||||
|
||||
/** 代金券金额 */
|
||||
private String pointAmount;
|
||||
|
||||
/** 交易手续费率 */
|
||||
private String recFeeRate;
|
||||
|
||||
/** 交易手续费 */
|
||||
private String recFeeAmt;
|
||||
|
||||
/** 商家出款金额 */
|
||||
private String realRefundAmount;
|
||||
|
||||
/** 渠道商商户号 */
|
||||
private String channelId;
|
||||
|
||||
/** 子商户号 */
|
||||
private String subMechId;
|
||||
|
||||
/** 消费者到账金额 */
|
||||
private String refBuyerAmt;
|
||||
|
||||
/** 微信或支付宝的身份ID */
|
||||
private String openid;
|
||||
|
||||
/** 活动类型 */
|
||||
private String activityNo;
|
||||
|
||||
/** 落单号 */
|
||||
private String sxfUuid;
|
||||
|
||||
/** 扩展字段 */
|
||||
private String extend;
|
||||
|
||||
/** 优惠信息 */
|
||||
private String promotionDetail;
|
||||
|
||||
/** 天阙门店编号 */
|
||||
private String storeNum;
|
||||
|
||||
/** 清算日期 */
|
||||
private String clearDt;
|
||||
|
||||
/** 交易完成时间 */
|
||||
private String finishTime;
|
||||
|
||||
/** 天阙终端编号 */
|
||||
private String deviceNo;
|
||||
|
||||
/** 原交易金额 */
|
||||
private String origAmt;
|
||||
|
||||
/** 原交易订单号 */
|
||||
private String origOrderNo;
|
||||
|
||||
/** 优惠券退款金额 */
|
||||
private String couponRefundFee;
|
||||
|
||||
/** 是否收支两条线标识 */
|
||||
private String szltFlag;
|
||||
|
||||
/** 后收手续费 */
|
||||
private String szltRecfeeAmt;
|
||||
|
||||
/** 切批时间 */
|
||||
private String settlementBatchNo;
|
||||
|
||||
/** 支付宝异步刷脸支付参数 */
|
||||
private String asyncPaymentMode;
|
||||
|
||||
/** 分期信息 */
|
||||
private String hbFqPayInfo;
|
||||
|
||||
/** 业务编码 */
|
||||
private String bizCode;
|
||||
|
||||
/** 业务信息 */
|
||||
private String bizMsg;
|
||||
|
||||
/** 组织机构ID */
|
||||
private String orgId;
|
||||
|
||||
/** 交易类型 */
|
||||
private String tradeType;
|
||||
|
||||
/** 交易信息 */
|
||||
private String tradeMsg;
|
||||
|
||||
/** 原始回调数据 */
|
||||
private String rawData;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 判断是否支付成功
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return "0000".equals(bizCode);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.ruoyi.web.dto;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TradeQueryDTO {
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String ordNo;
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package com.ruoyi.web.mapper;
|
||||
|
||||
import com.ruoyi.web.domain.Device;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface DeviceMapper {
|
||||
|
||||
List<Device> getDevice(@Param("storeId") String storeId);
|
||||
|
||||
int addDevice(Device device);
|
||||
|
||||
List<Device> getSheXiangTou(String storeId);
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.ruoyi.web.mapper;
|
||||
|
||||
|
||||
import com.ruoyi.web.domain.DoorScanRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Mapper
|
||||
public interface DoorScanRecordMapper {
|
||||
|
||||
|
||||
int addDoorScanRecord(DoorScanRecord doorScanRecord);
|
||||
|
||||
List<DoorScanRecord> selectScanRecordList(DoorScanRecord doorScanRecord);
|
||||
|
||||
Integer getEnterCountByDate(@Param("date") String date,@Param("storeId") Integer storeId);
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.ruoyi.web.mapper;
|
||||
|
||||
import com.ruoyi.web.domain.InstallTask;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 安装任务Mapper接口
|
||||
*/
|
||||
public interface InstallTaskMapper {
|
||||
|
||||
/**
|
||||
* 新增安装任务
|
||||
*/
|
||||
int insertInstallTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 根据ID查询安装任务
|
||||
*/
|
||||
InstallTask selectInstallTaskById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 根据任务编号查询
|
||||
*/
|
||||
InstallTask selectInstallTaskByTaskNo(@Param("taskNo") String taskNo);
|
||||
|
||||
/**
|
||||
* 查询安装任务列表
|
||||
*/
|
||||
List<InstallTask> selectInstallTaskList(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 更新安装任务
|
||||
*/
|
||||
int updateInstallTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 删除安装任务(逻辑删除)
|
||||
*/
|
||||
int deleteInstallTaskById(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 批量删除安装任务
|
||||
*/
|
||||
int deleteInstallTaskByIds(@Param("ids") Long[] ids);
|
||||
|
||||
/**
|
||||
* 指派任务
|
||||
*/
|
||||
int assignTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 完成任务安装
|
||||
*/
|
||||
int completeTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 查询当天最大任务编号
|
||||
*/
|
||||
String selectMaxTaskNoByDate(@Param("prefix") String prefix);
|
||||
}
|
||||
|
|
@ -2,7 +2,10 @@ package com.ruoyi.web.mapper;
|
|||
|
||||
|
||||
import com.ruoyi.web.domain.Order;
|
||||
import com.ruoyi.web.domain.OrderItem;
|
||||
import com.ruoyi.web.vo.OrderCountVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -10,7 +13,22 @@ import java.util.List;
|
|||
@Mapper
|
||||
public interface OrderMapper {
|
||||
|
||||
|
||||
|
||||
List<Order> selectOrderList(Order order);
|
||||
|
||||
|
||||
int insert(Order order);
|
||||
|
||||
|
||||
int batchInsert(@Param("list") List<OrderItem> items);
|
||||
|
||||
Order selectForUpdateByOrderNo(@Param("ordNo") String ordNo);
|
||||
|
||||
List<OrderItem> selectByOrderId(@Param("orderId") Long orderId);
|
||||
|
||||
/**
|
||||
* 根据ID更新订单(选择性更新)
|
||||
*/
|
||||
int updateById(Order order);
|
||||
|
||||
OrderCountVO getOrderCount(String storeId);
|
||||
}
|
||||
|
|
@ -2,6 +2,8 @@ package com.ruoyi.web.mapper;
|
|||
|
||||
import com.ruoyi.web.domain.Product;
|
||||
import com.ruoyi.web.domain.Store;
|
||||
import com.ruoyi.web.dto.BatchBarcodeQueryDTO;
|
||||
import com.ruoyi.web.vo.ProductVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
|
@ -26,4 +28,29 @@ public interface ProductMapper {
|
|||
|
||||
|
||||
int batchProductById(@Param("ids") List<Long> ids);
|
||||
|
||||
|
||||
List<Product> selectBatchForUpdate(@Param("storeId") Integer storeId,
|
||||
@Param("ids") List<Long> ids);
|
||||
|
||||
|
||||
/**
|
||||
* 扣减库存(带库存校验,带门店ID)
|
||||
* @param storeId 门店ID
|
||||
* @param productId 商品ID
|
||||
* @param quantity 扣减数量
|
||||
* @return 影响行数(>0表示成功,=0表示库存不足或商品不存在)
|
||||
*/
|
||||
int reduceStock(@Param("storeId") Integer storeId,
|
||||
@Param("productId") Long productId,
|
||||
@Param("quantity") Integer quantity);
|
||||
|
||||
/**
|
||||
* 增加库存(用于回滚,带门店ID)
|
||||
*/
|
||||
int addStock(@Param("storeId") Integer storeId,
|
||||
@Param("productId") Long productId,
|
||||
@Param("quantity") Integer quantity);
|
||||
|
||||
List<Product> getByBarcodes(BatchBarcodeQueryDTO dto);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package com.ruoyi.web.mapper;
|
|||
|
||||
|
||||
import com.ruoyi.web.domain.Store;
|
||||
import com.ruoyi.web.vo.StoreVo;
|
||||
import com.ruoyi.web.vo.UserStoreVo;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ruoyi.web.service;
|
||||
|
||||
import com.ruoyi.web.domain.Brand;
|
||||
import com.ruoyi.web.domain.Device;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface DeviceService {
|
||||
|
||||
List<Device> getDevice(String storeId);
|
||||
|
||||
int addDevice(Device device);
|
||||
|
||||
List<Device> getSheXiangTou(String storeId);
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.ruoyi.web.service;
|
||||
|
||||
import com.ruoyi.web.domain.DoorScanRecord;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public interface DoorScanRecordService {
|
||||
|
||||
int addDoorScanRecord(DoorScanRecord doorScanRecord);
|
||||
|
||||
List<DoorScanRecord> selectScanRecordList(DoorScanRecord doorScanRecord);
|
||||
|
||||
Integer getEnterCountByDate(String date,Integer storeId);
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package com.ruoyi.web.service;
|
||||
|
||||
import com.ruoyi.web.domain.InstallTask;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 安装任务Service接口
|
||||
*/
|
||||
public interface InstallTaskService {
|
||||
|
||||
/**
|
||||
* 新增安装任务
|
||||
*/
|
||||
int insertInstallTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 根据ID查询
|
||||
*/
|
||||
InstallTask selectInstallTaskById(Long id);
|
||||
|
||||
/**
|
||||
* 根据任务编号查询
|
||||
*/
|
||||
InstallTask selectInstallTaskByTaskNo(String taskNo);
|
||||
|
||||
/**
|
||||
* 查询安装任务列表
|
||||
*/
|
||||
List<InstallTask> selectInstallTaskList(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 更新安装任务
|
||||
*/
|
||||
int updateInstallTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 删除安装任务
|
||||
*/
|
||||
int deleteInstallTaskById(Long id);
|
||||
|
||||
/**
|
||||
* 批量删除
|
||||
*/
|
||||
int deleteInstallTaskByIds(Long[] ids);
|
||||
|
||||
/**
|
||||
* 指派任务
|
||||
*/
|
||||
int assignTask(Long id, Long assigneeId, String assigneeName, String updateBy);
|
||||
|
||||
/**
|
||||
* 完成任务安装
|
||||
*/
|
||||
int completeTask(InstallTask installTask);
|
||||
|
||||
/**
|
||||
* 生成任务编号
|
||||
*/
|
||||
String generateTaskNo();
|
||||
}
|
||||
|
|
@ -1,7 +1,13 @@
|
|||
package com.ruoyi.web.service;
|
||||
|
||||
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.web.domain.Order;
|
||||
import com.ruoyi.web.dto.CreateOrderDTO;
|
||||
import com.ruoyi.web.dto.ScanPayRequest;
|
||||
import com.ruoyi.web.dto.TianquePayCallbackDTO;
|
||||
import com.ruoyi.web.dto.TradeQueryDTO;
|
||||
import com.ruoyi.web.vo.OrderCountVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -13,4 +19,21 @@ public interface OrderService {
|
|||
|
||||
|
||||
List<Order> selectOrderList(Order order);
|
||||
|
||||
AjaxResult addPay(CreateOrderDTO createOrderDTO);
|
||||
|
||||
TianquePayCallbackDTO parseCallback(String callbackData);
|
||||
|
||||
/**
|
||||
* 处理天阙平台支付回调
|
||||
*/
|
||||
void handleTianquePayCallback(TianquePayCallbackDTO request);
|
||||
|
||||
AjaxResult addOrder(CreateOrderDTO createOrderDTO);
|
||||
|
||||
String addOrderReverseScan(ScanPayRequest scanPayRequest) throws Exception;
|
||||
|
||||
AjaxResult tradeQuery(TradeQueryDTO tradeQueryDTO) throws Exception;
|
||||
|
||||
OrderCountVO getOrderCount(String storeId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
package com.ruoyi.web.service;
|
||||
|
||||
import com.ruoyi.web.domain.Product;
|
||||
import com.ruoyi.web.dto.BatchBarcodeQueryDTO;
|
||||
import com.ruoyi.web.dto.BatchStoreDTO;
|
||||
import com.ruoyi.web.vo.ProductVo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
|
@ -35,4 +37,6 @@ public interface ProductService {
|
|||
int updateProduct(Product product);
|
||||
|
||||
int batchProductById(BatchStoreDTO dto);
|
||||
|
||||
List<Product> getByBarcodes(BatchBarcodeQueryDTO dto);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.ruoyi.web.service.impl;
|
||||
|
||||
|
||||
import com.ruoyi.web.domain.Device;
|
||||
import com.ruoyi.web.mapper.DeviceMapper;
|
||||
import com.ruoyi.web.service.DeviceService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
public class DeviceServiceImpl implements DeviceService {
|
||||
|
||||
@Autowired
|
||||
private DeviceMapper deviceMapper;
|
||||
@Override
|
||||
public List<Device> getDevice(String storeId) {
|
||||
return deviceMapper.getDevice(storeId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addDevice(Device device) {
|
||||
return deviceMapper.addDevice(device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Device> getSheXiangTou(String storeId) {
|
||||
return deviceMapper.getSheXiangTou(storeId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.ruoyi.web.service.impl;
|
||||
|
||||
import com.ruoyi.web.domain.DoorScanRecord;
|
||||
import com.ruoyi.web.mapper.DoorScanRecordMapper;
|
||||
import com.ruoyi.web.service.DoorScanRecordService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Service
|
||||
public class DoorScanRecordServiceImpl implements DoorScanRecordService {
|
||||
|
||||
@Autowired
|
||||
private DoorScanRecordMapper doorScanRecordMapper;
|
||||
@Override
|
||||
public int addDoorScanRecord(DoorScanRecord doorScanRecord) {
|
||||
return doorScanRecordMapper.addDoorScanRecord(doorScanRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DoorScanRecord> selectScanRecordList(DoorScanRecord doorScanRecord) {
|
||||
return doorScanRecordMapper.selectScanRecordList(doorScanRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getEnterCountByDate(String date,Integer storeId) {
|
||||
return doorScanRecordMapper.getEnterCountByDate(date,storeId);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package com.ruoyi.web.service.impl;
|
||||
|
||||
import com.ruoyi.common.core.text.Convert;
|
||||
import com.ruoyi.common.utils.DateUtils;
|
||||
import com.ruoyi.web.domain.InstallTask;
|
||||
import com.ruoyi.web.mapper.InstallTaskMapper;
|
||||
import com.ruoyi.web.service.InstallTaskService;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 安装任务Service实现类
|
||||
*/
|
||||
@Service
|
||||
public class InstallTaskServiceImpl implements InstallTaskService {
|
||||
|
||||
@Resource
|
||||
private InstallTaskMapper installTaskMapper;
|
||||
|
||||
@Override
|
||||
public int insertInstallTask(InstallTask installTask) {
|
||||
// 生成任务编号
|
||||
installTask.setTaskNo(generateTaskNo());
|
||||
// 设置初始状态为待处理
|
||||
installTask.setTaskStatus(0);
|
||||
// 设置创建时间
|
||||
installTask.setCreateTime(DateUtils.getNowDate());
|
||||
return installTaskMapper.insertInstallTask(installTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallTask selectInstallTaskById(Long id) {
|
||||
return installTaskMapper.selectInstallTaskById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstallTask selectInstallTaskByTaskNo(String taskNo) {
|
||||
return installTaskMapper.selectInstallTaskByTaskNo(taskNo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<InstallTask> selectInstallTaskList(InstallTask installTask) {
|
||||
return installTaskMapper.selectInstallTaskList(installTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int updateInstallTask(InstallTask installTask) {
|
||||
installTask.setUpdateTime(DateUtils.getNowDate());
|
||||
return installTaskMapper.updateInstallTask(installTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteInstallTaskById(Long id) {
|
||||
return installTaskMapper.deleteInstallTaskById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteInstallTaskByIds(Long[] ids) {
|
||||
return installTaskMapper.deleteInstallTaskByIds(ids);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int assignTask(Long id, Long assigneeId, String assigneeName, String updateBy) {
|
||||
InstallTask task = new InstallTask();
|
||||
task.setId(id);
|
||||
task.setAssigneeId(assigneeId);
|
||||
task.setAssigneeName(assigneeName);
|
||||
task.setTaskStatus(1); // 1-已接单
|
||||
task.setAssignTime(DateUtils.getNowDate());
|
||||
task.setUpdateBy(updateBy);
|
||||
task.setUpdateTime(DateUtils.getNowDate());
|
||||
|
||||
return installTaskMapper.assignTask(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public int completeTask(InstallTask installTask) {
|
||||
installTask.setTaskStatus(3); // 3-已完成
|
||||
installTask.setInstallTime(DateUtils.getNowDate());
|
||||
installTask.setUpdateTime(DateUtils.getNowDate());
|
||||
|
||||
return installTaskMapper.completeTask(installTask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generateTaskNo() {
|
||||
// 格式:INS + 年月日 + 4位序号
|
||||
// 例如:INS202503160001
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
|
||||
String today = sdf.format(new Date());
|
||||
String prefix = "INS" + today;
|
||||
|
||||
// 查询当天最大的任务编号
|
||||
String maxTaskNo = installTaskMapper.selectMaxTaskNoByDate(prefix);
|
||||
|
||||
int seq = 1;
|
||||
if (maxTaskNo != null && maxTaskNo.length() >= 14) {
|
||||
// 截取后4位序号
|
||||
String seqStr = maxTaskNo.substring(11);
|
||||
try {
|
||||
seq = Integer.parseInt(seqStr) + 1;
|
||||
} catch (NumberFormatException e) {
|
||||
seq = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// 生成4位序号,不足补0
|
||||
String seqStr = String.format("%04d", seq);
|
||||
|
||||
return prefix + seqStr;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,26 +1,803 @@
|
|||
package com.ruoyi.web.service.impl;
|
||||
|
||||
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.parser.Feature;
|
||||
|
||||
import com.ruoyi.common.core.domain.AjaxResult;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.web.domain.Order;
|
||||
import com.ruoyi.web.domain.OrderItem;
|
||||
import com.ruoyi.web.domain.Product;
|
||||
import com.ruoyi.web.dto.*;
|
||||
import com.ruoyi.web.mapper.OrderMapper;
|
||||
import com.ruoyi.web.mapper.ProductMapper;
|
||||
import com.ruoyi.web.service.OrderService;
|
||||
import com.ruoyi.web.utils.HttpUtils;
|
||||
import com.ruoyi.web.utils.RSASignature;
|
||||
import com.ruoyi.web.vo.OrderCountVO;
|
||||
import com.ruoyi.web.vo.OrderVo;
|
||||
import com.ruoyi.web.vo.PaymentResponseVO;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.DigestUtils;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.Duration;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 商品订单 服务层实现
|
||||
*/
|
||||
@Service
|
||||
public class OrderServiceImpl implements OrderService {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class);
|
||||
@Resource
|
||||
private OrderMapper orderMapper;
|
||||
@Resource
|
||||
private ProductMapper productMapper;
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, String> redisTemplate;
|
||||
|
||||
private static final String ORDER_LOCK_PREFIX = "order:lock:";
|
||||
|
||||
@Override
|
||||
public List<Order> selectOrderList(Order order) {
|
||||
return orderMapper.selectOrderList(order);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AjaxResult addPay(CreateOrderDTO createOrderDTO) {
|
||||
//订单号
|
||||
String orderNo = null;
|
||||
//订单id
|
||||
Long orderId = null;
|
||||
//锁key
|
||||
String lockKey = null;
|
||||
//订单明细
|
||||
List<OrderItem> orderItems = new ArrayList<>();
|
||||
//金额
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
//支付控件参数
|
||||
String pay = null;
|
||||
try {
|
||||
// 1. 生成锁key(基于门店ID + 商品组合)
|
||||
lockKey = generateLockKey(createOrderDTO.getStoreId(), createOrderDTO.getItems());
|
||||
log.info("尝试获取锁: {}", lockKey);
|
||||
|
||||
// 2. 获取分布式锁
|
||||
Boolean locked = redisTemplate.opsForValue()
|
||||
.setIfAbsent(lockKey, "1", Duration.ofSeconds(6));
|
||||
|
||||
if (Boolean.FALSE.equals(locked)) {
|
||||
log.warn("获取锁失败,该门店的商品组合正在被处理: {}", lockKey);
|
||||
throw new ServiceException("系统繁忙,请稍后重试");
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. 批量查询商品并加行锁(带上门店ID条件)
|
||||
List<Long> productIds = createOrderDTO.getItems().stream()
|
||||
.map(OrderItem::getProductId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Product> products = productMapper.selectBatchForUpdate(
|
||||
createOrderDTO.getStoreId(), productIds);
|
||||
|
||||
if (products.size() != productIds.size()) {
|
||||
// 找出不存在的商品
|
||||
Set<Long> existIds = products.stream()
|
||||
.map(Product::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<Long> notExistIds = productIds.stream()
|
||||
.filter(id -> !existIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.error("部分商品在该门店不存在, 门店id storeId:{}, 商品id productIds:{}",
|
||||
createOrderDTO.getStoreId(), notExistIds);
|
||||
// return AjaxResult.error("部分商品在该门店不存在");
|
||||
throw new ServiceException("部分商品在该门店不存在");
|
||||
|
||||
}
|
||||
|
||||
Map<Long, Product> productMap = products.stream()
|
||||
.collect(Collectors.toMap(Product::getId, p -> p));
|
||||
|
||||
// 5. 校验库存并计算金额
|
||||
for (OrderItem item : createOrderDTO.getItems()) {
|
||||
Product product = productMap.get(item.getProductId());
|
||||
|
||||
if (product.getStockQuantity() < item.getQuantity()) {
|
||||
log.error("商品[" + product.getProductName() + "]库存不足,当前门店剩余库存数量:" + product.getStockQuantity());
|
||||
return AjaxResult.error("商品[" + product.getProductName() + "]库存不足,当前门店剩余库存数量:" + product.getStockQuantity());
|
||||
}
|
||||
|
||||
// 计算小计
|
||||
BigDecimal storePrice = new BigDecimal(product.getStorePrice());
|
||||
BigDecimal subtotal = storePrice
|
||||
.multiply(new BigDecimal(item.getQuantity()))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
|
||||
totalAmount = totalAmount.add(subtotal);
|
||||
// 创建订单明细
|
||||
OrderItem orderItem = new OrderItem();
|
||||
orderItem.setProductId(product.getId());
|
||||
orderItem.setProductName(product.getProductName());
|
||||
orderItem.setMainImage(product.getMainImage());
|
||||
orderItem.setQuantity(item.getQuantity());
|
||||
orderItem.setSubtotal(item.getSubtotal());
|
||||
orderItem.setCreateBy(createOrderDTO.getCreateBy());
|
||||
orderItems.add(orderItem);
|
||||
}
|
||||
|
||||
// 6. 校验前端传递的金额和后端计算的金额是否一致
|
||||
BigDecimal frontendAmount = new BigDecimal(createOrderDTO.getTotalAmount());
|
||||
if (frontendAmount.compareTo(totalAmount) != 0) {
|
||||
log.error("金额不一致,前端传参金额:{},后端计算金额:{}",
|
||||
frontendAmount, totalAmount);
|
||||
return AjaxResult.error("订单总金额异常");
|
||||
}
|
||||
|
||||
// 7. 生成订单号
|
||||
orderNo = generateOrderNo();
|
||||
|
||||
// 8. 创建订单主表
|
||||
Order order = new Order();
|
||||
order.setOrderNo(orderNo);
|
||||
order.setStoreId(createOrderDTO.getStoreId());
|
||||
order.setPayAmount(String.valueOf(totalAmount));
|
||||
order.setPayStatus(0); // 未支付
|
||||
order.setCreateBy(createOrderDTO.getCreateBy());
|
||||
int insert = orderMapper.insert(order);
|
||||
if (insert <= 0) {
|
||||
log.error("主订单创建失败");
|
||||
throw new ServiceException("主订单创建失败");
|
||||
}
|
||||
orderId = order.getId();
|
||||
|
||||
// 9. 批量插入订单明细
|
||||
for (OrderItem item : orderItems) {
|
||||
item.setOrderId(orderId);
|
||||
}
|
||||
int batchInsertResult = orderMapper.batchInsert(orderItems);
|
||||
|
||||
if (batchInsertResult != orderItems.size()) {
|
||||
log.error("订单明细创建失败");
|
||||
throw new ServiceException("订单明细创建失败");
|
||||
}
|
||||
} finally {
|
||||
// 释放锁
|
||||
redisTemplate.delete(lockKey);
|
||||
log.info("释放锁: {}", lockKey);
|
||||
}
|
||||
// ============ 锁已释放,开始调用支付接口 ============
|
||||
pay = pay(orderNo, String.valueOf(totalAmount)); //调用第三方支付成功,返回调用支付控件参数
|
||||
} catch (Exception e) {
|
||||
log.error("创建订单失败", e);
|
||||
throw new ServiceException("创建订单失败" + e.getMessage());
|
||||
}
|
||||
log.info("订单创建成功, orderNo:{}, 订单ID:{}, storeId:{},支付控件参数:{}",
|
||||
orderNo, orderId, createOrderDTO.getStoreId(), pay);
|
||||
return AjaxResult.success("订单创建成功: " + pay);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TianquePayCallbackDTO parseCallback(String callbackData) {
|
||||
try {
|
||||
return JSON.parseObject(callbackData, TianquePayCallbackDTO.class);
|
||||
} catch (Exception e) {
|
||||
log.error("解析天阙平台回调数据失败", e);
|
||||
throw new RuntimeException("解析回调数据失败");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void handleTianquePayCallback(TianquePayCallbackDTO request) {
|
||||
log.info("处理天阙平台支付回调, ordNo:{}, uuid:{}, amt:{}",
|
||||
request.getOrdNo(), request.getUuid(), request.getAmt());
|
||||
|
||||
// 1. 参数校验
|
||||
if (request.getOrdNo() == null || request.getUuid() == null) {
|
||||
log.error("回调参数缺失");
|
||||
throw new ServiceException("回调参数缺失");
|
||||
}
|
||||
|
||||
// 2. 幂等处理(防止重复回调)
|
||||
String idempotentKey = "tianque:callback:" + request.getUuid();
|
||||
Boolean isFirst = redisTemplate.opsForValue()
|
||||
.setIfAbsent(idempotentKey, "1", Duration.ofHours(24));
|
||||
|
||||
if (Boolean.FALSE.equals(isFirst)) {
|
||||
log.info("天阙平台回调已处理过, uuid:{}", request.getUuid());
|
||||
throw new ServiceException("天阙平台回调已处理过");
|
||||
}
|
||||
|
||||
// 3. 获取订单锁(防止并发处理同一个订单)
|
||||
String orderLockKey = ORDER_LOCK_PREFIX + request.getOrdNo();
|
||||
Boolean locked = redisTemplate.opsForValue()
|
||||
.setIfAbsent(orderLockKey, "1", Duration.ofSeconds(5));
|
||||
|
||||
if (Boolean.FALSE.equals(locked)) {
|
||||
log.warn("订单正在处理中, ordNo:{}", request.getOrdNo());
|
||||
throw new ServiceException("订单正在处理中");
|
||||
}
|
||||
|
||||
try {
|
||||
// 4. 查询订单(加行锁)
|
||||
Order order = orderMapper.selectForUpdateByOrderNo(request.getOrdNo());
|
||||
if (order == null) {
|
||||
log.error("订单不存在, orderNo:{}", request.getOrdNo());
|
||||
throw new ServiceException("订单不存在");
|
||||
}
|
||||
|
||||
// 5. 检查订单状态
|
||||
if (order.getPayStatus() == 2) {
|
||||
log.info("订单已支付, orderNo:{}", request.getOrdNo());
|
||||
throw new ServiceException("订单已支付");
|
||||
}
|
||||
|
||||
if (order.getPayStatus() != 0) {
|
||||
log.error("订单状态错误, orderNo:{}, status:{}",
|
||||
request.getOrdNo(), order.getPayStatus());
|
||||
throw new ServiceException("订单状态错误");
|
||||
}
|
||||
|
||||
// 6. 金额校验
|
||||
if (order.getPayAmount().compareTo(request.getAmt()) != 0) {
|
||||
log.error("支付金额不匹配, orderAmount:{}, payAmount:{}",
|
||||
order.getPayAmount(), request.getAmt());
|
||||
throw new ServiceException("支付金额不匹配");
|
||||
}
|
||||
|
||||
// 7. 查询订单明细
|
||||
List<OrderItem> items = orderMapper.selectByOrderId(order.getId());
|
||||
|
||||
// 8. 扣减实际库存
|
||||
for (OrderItem item : items) {
|
||||
int result = productMapper.reduceStock(
|
||||
order.getStoreId(),
|
||||
item.getProductId(),
|
||||
item.getQuantity()
|
||||
);
|
||||
if (result <= 0) {
|
||||
log.error("库存扣减失败, storeId:{}, productId:{}, quantity:{}",
|
||||
order.getStoreId(), item.getProductId(), item.getQuantity());
|
||||
throw new ServiceException("库存扣减失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 9. 更新订单状态为已支付
|
||||
Order updateOrder = new Order();
|
||||
updateOrder.setId(order.getId());
|
||||
updateOrder.setPayStatus(2);
|
||||
updateOrder.setPayTime(new Date());
|
||||
|
||||
int updated = orderMapper.updateById(updateOrder);
|
||||
if (updated <= 0) {
|
||||
log.error("订单状态更新失败, orderNo:{}", request.getOrdNo());
|
||||
throw new ServiceException("订单状态更新失败");
|
||||
}
|
||||
log.info("支付回调处理成功, orderNo:{}, storeId:{}",
|
||||
request.getOrdNo(), order.getStoreId());
|
||||
} finally {
|
||||
// 释放订单锁
|
||||
redisTemplate.delete(orderLockKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public AjaxResult addOrder(CreateOrderDTO createOrderDTO) {
|
||||
String orderNo = null;
|
||||
BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
//锁key
|
||||
String lockKey = null;
|
||||
orderNo = generateOrderNo();
|
||||
try {
|
||||
// 1. 生成锁key(基于门店ID + 商品组合)
|
||||
lockKey = generateLockKey(createOrderDTO.getStoreId(), createOrderDTO.getItems());
|
||||
log.info("尝试获取锁: {}", lockKey);
|
||||
|
||||
// 2. 获取分布式锁
|
||||
Boolean locked = redisTemplate.opsForValue()
|
||||
.setIfAbsent(lockKey, "1", Duration.ofSeconds(6));
|
||||
|
||||
if (Boolean.FALSE.equals(locked)) {
|
||||
log.warn("获取锁失败,该门店的商品组合正在被处理: {}", lockKey);
|
||||
throw new ServiceException("系统繁忙,请稍后重试");
|
||||
}
|
||||
|
||||
try {
|
||||
// 3. 批量查询商品并加行锁(带上门店ID条件)
|
||||
List<Long> productIds = createOrderDTO.getItems().stream()
|
||||
.map(OrderItem::getProductId)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
List<Product> products = productMapper.selectBatchForUpdate(
|
||||
createOrderDTO.getStoreId(), productIds);
|
||||
|
||||
if (products.size() != productIds.size()) {
|
||||
// 找出不存在的商品
|
||||
Set<Long> existIds = products.stream()
|
||||
.map(Product::getId)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<Long> notExistIds = productIds.stream()
|
||||
.filter(id -> !existIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
log.error("部分商品在该门店不存在, 门店id storeId:{}, 商品id productIds:{}",
|
||||
createOrderDTO.getStoreId(), notExistIds);
|
||||
throw new ServiceException("部分商品在该门店不存在");
|
||||
}
|
||||
Map<Long, Product> productMap = products.stream()
|
||||
.collect(Collectors.toMap(Product::getId, p -> p));
|
||||
// 5. 校验库存并计算金额
|
||||
for (OrderItem item : createOrderDTO.getItems()) {
|
||||
Product product = productMap.get(item.getProductId());
|
||||
|
||||
if (product.getStockQuantity() < item.getQuantity()) {
|
||||
log.error("商品[" + product.getProductName() + "]库存不足,当前门店剩余库存数量:" + product.getStockQuantity());
|
||||
return AjaxResult.error("商品[" + product.getProductName() + "]库存不足,当前门店剩余库存数量:" + product.getStockQuantity());
|
||||
}
|
||||
// 计算小计
|
||||
BigDecimal storePrice = new BigDecimal(product.getStorePrice());
|
||||
BigDecimal subtotal = storePrice
|
||||
.multiply(new BigDecimal(item.getQuantity()))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
|
||||
totalAmount = totalAmount.add(subtotal);
|
||||
}
|
||||
|
||||
// 6. 校验前端传递的金额和后端计算的金额是否一致
|
||||
BigDecimal frontendAmount = new BigDecimal(createOrderDTO.getTotalAmount());
|
||||
if (frontendAmount.compareTo(totalAmount) != 0) {
|
||||
log.error("金额不一致,前端传参金额:{},后端计算金额:{}",
|
||||
frontendAmount, totalAmount);
|
||||
return AjaxResult.error("订单总金额异常");
|
||||
}
|
||||
|
||||
//订单id
|
||||
Long orderId = null;
|
||||
Order order = new Order();
|
||||
order.setOrderNo(orderNo);
|
||||
order.setStoreId(createOrderDTO.getStoreId());
|
||||
order.setPayAmount(String.valueOf(createOrderDTO.getTotalAmount()));
|
||||
order.setPayStatus(0); // 未支付
|
||||
order.setCreateBy(createOrderDTO.getCreateBy());
|
||||
int insert = orderMapper.insert(order);
|
||||
if (insert <= 0) {
|
||||
log.error("主订单创建失败");
|
||||
throw new ServiceException("主订单创建失败");
|
||||
}
|
||||
orderId = order.getId();
|
||||
|
||||
for (OrderItem item : createOrderDTO.getItems()) {
|
||||
item.setOrderId(orderId);
|
||||
}
|
||||
orderMapper.batchInsert(createOrderDTO.getItems());
|
||||
log.info("被扫订单创建成功");
|
||||
} finally {
|
||||
// 释放锁
|
||||
redisTemplate.delete(lockKey);
|
||||
log.info("释放锁: {}", lockKey);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("创建订单失败", e);
|
||||
throw new ServiceException("创建订单失败" + e.getMessage());
|
||||
}
|
||||
OrderVo orderVo = new OrderVo();
|
||||
orderVo.setOrderNo(orderNo);
|
||||
orderVo.setAmount(String.valueOf(totalAmount));
|
||||
return AjaxResult.success(orderVo);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public String addOrderReverseScan(ScanPayRequest scanPayRequest) throws Exception {
|
||||
//再加个校验,根据订单号去查询金额,如果前端传的金额和后端存的不一样,不允许支付
|
||||
|
||||
//合作方私钥(替换成自己的)
|
||||
String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM35qbWVNdBAU+oai8EVE3iSpA9wHAt4wOs+SufbkE/FNoMgp4EMrFNrdNkaKLQq4cRzqoNlJY+DZSL54dm7ekKZP/SLhWwNi9tJvhKsFpw6SRwthlKyA8lCW2mzFffFqLhGAayNoZTmHwkq8dotvecARVbiFtakzzFy/O7ATmzw3i57yaVgX2RtD8WWA7MGg5CQP1vvIDlXqd40SQcrJY6GNNv07Brv3fpyW2nXbvrxbOx+87NnG77/R7o8eFcWJF+yrqE1xeN3hDvHBFMFyf1M39Zig2I75vKRvHGqftAZuvlDG0nInwcSiMTE7wDsXvYzRnOsgJNVET7aFhzOvFAgMBAAECggEATjB4nRl2S2Whasnf0Ab77CoZSjn7HUXv2hymBkJNPq3eV0Hh5Pjjqmi7hIs0cdm9AWXd6RzySKpScQNjYQFxEOIdbayfTdzD24L2ivT6ZBca09Z9J/9Rik0wJ4ULTtny5JRc6VwGgOn9Twdsde8V4sr1nHUvpMaRH6WVxW8mX7+udgbK0Uxm5IFROeKGz0DSuG8SBpDhD2EB6P2Q+1fpna17yP1R08HtZNU6lQx7yMesaJuvKkwf4YiSjIhUtdbUXGhmbG5LFzoj3oRs25W68bIOQjT9i196hPQOYj6C7yz72nuY2rwFHhtsocfQRULWOHtsnWts9epvuFruDTdq8QKBgQD8bZhQkyQm6VTP0Hmv6sV9SXXKH0rb2V8hml7DxcpFl+l2/9UesH3qMoFKZ5C1YdPKZBNlD7fp5RxjtJ8MdwIp8MxRgMf/bZpIOx1Lfum1H+dKh5LF+oC4GGtJ5jqnwozNPog4keUn+bqKhiza9snA+6jy446A1Y8GGWlZwUGkVwKBgQDPxb7FZmOcotNnh8mS5pcx6d22UhBHIHz/X/dcjNQuI8tIRaIvkJm/4lfuxejCz3LY9k+VSQ96ZM2L5OqZGDlR7aMXQ3/FhuS4PEkNcDvccQPoig7U5zUUn2Jdqom6Lngh7Ka8B1v24ght0yXHHNChmMg/DJ42EzCnjpydWs6/QwKBgA9qGd0BvzlpEjbGgkfNzFWEQN8g3g9izL5ekN7fmyR4zFbp9He1S1sbzm1euaV53dcEGXMYbKCpzvv/sZ6vPcCV5cQsWwosBTnX8kgD7f2Tfyo78SiJzYZwZ0zR9E7+QF7gLK1Xq2ivhUakPuT4IQXZ2E1MvAz9/Yff0WEbvghtAoGAejFzC8c2yDUenaHHU+TXgNxor0Q+HIan3M0EvmJ4mxYkBMInK1AgjDBCxMOSK5gzlBPwI/0O5E2KcT7VFeqgM2XN5+2jpHi75PpXgFbEbdXtlYI0TNQZbKJ8CFg2nc+ciV8ThDvTwzOV/3kRm7N/o7ol8qaqGWVZ1QFTbFuugd8CgYBAsNVKCEHGZAwS5Csv/IBopcrJOJknmEHeAT+VwxtZK7yOJ4ZSNizzofu847YGO4uEhfcaC8lYxnj8XskuMG2KChbbxEA+qlnL/oMIwVrpE0ZbFmQ4WRTUfVQT+zCHMPuuuj5tLPTSMjQLGlq+r3XRzwUaX98J9GLKgHG7tXlphw==";
|
||||
//随行付公钥
|
||||
String sxfPublic = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjo1+KBcvwDSIo+nMYLeOJ19Ju4ii0xH66ZxFd869EWFWk/EJa3xIA2+4qGf/Ic7m7zi/NHuCnfUtUDmUdP0JfaZiYwn+1Ek7tYAOc1+1GxhzcexSJLyJlR2JLMfEM+rZooW4Ei7q3a8jdTWUNoak/bVPXnLEVLrbIguXABERQ0Ze0X9Fs0y/zkQFg8UjxUN88g2CRfMC6LldHm7UBo+d+WlpOYH7u0OTzoLLiP/04N1cfTgjjtqTBI7qkOGxYs6aBZHG1DJ6WdP+5w+ho91sBTVajsCxAaMoExWQM2ipf/1qGdsWmkZScPflBqg7m0olOD87ymAVP/3Tcbvi34bDfwIDAQAB";
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
|
||||
String dataStr = df.format(new Date());
|
||||
|
||||
ApiRequestBean<JSONObject> reqBean = new ApiRequestBean<JSONObject>();
|
||||
|
||||
reqBean.setOrgId("57594783"); //机构id
|
||||
reqBean.setReqId(UUID.randomUUID().toString().replaceAll("-", ""));
|
||||
reqBean.setSignType("RSA");
|
||||
reqBean.setTimestamp(dataStr);
|
||||
reqBean.setVersion("1.0");
|
||||
JSONObject reqData = new JSONObject();
|
||||
|
||||
//业务参数
|
||||
reqData.put("ordNo", scanPayRequest.getOrdNo()); //商户订单号
|
||||
reqData.put("mno", "399260311280126"); //商户编号
|
||||
//reqData.put("subMechId", ""); //子商户号
|
||||
reqData.put("authCode", scanPayRequest.getAuthCode()); //授权码 需要前端传
|
||||
reqData.put("amt", scanPayRequest.getAmt()); //订单总金额
|
||||
//reqData.put("discountAmt", ""); //参与优惠金额
|
||||
//reqData.put("unDiscountAmt", ""); //不参与优惠金额
|
||||
reqData.put("payType", scanPayRequest.getPayType()); //支付渠道
|
||||
reqData.put("scene", "1"); //支付场景,1: 刷卡 2:声波 3:刷脸 不上传默认为 1
|
||||
reqData.put("subject", "B扫C测试"); //订单标题
|
||||
reqData.put("tradeSource", "02"); //交易来源 01服务商,02收银台,03硬件
|
||||
reqData.put("trmIp", "172.16.2.1");
|
||||
//reqData.put("limitPay", "00"); //限制卡类型: 00-全部 01-限定不能使 用信用卡支付 默认值 00
|
||||
//reqData.put("hbFqNum", "6"); //花呗分期数,仅可上送 6 或 12
|
||||
//reqData.put("hbFqPercent", "0"); //卖家承担分期 服务费比例,仅支持上送 0 或 100
|
||||
//reqData.put("goodsTag", "00"); //订单优惠标识 00:是,01: 否
|
||||
|
||||
//reqData.put("couponDetail", ""); //优惠详情信息,见下面三个字段
|
||||
//reqData.put("costPrice", "200"); //订单原价保留两 位小数;微信 独有
|
||||
//reqData.put("receiptId ", "123456789"); //商品小票
|
||||
|
||||
//reqData.put("goodsDetail", "123456789"); //单品优惠信息使用 json 数组格式提交
|
||||
//reqData.put("goodsId", "200"); //商品编码
|
||||
//reqData.put("thirdGoodsId", "12345678"); //微信/支付宝侧商品码
|
||||
//reqData.put("goodsName", "苹果电脑"); //商品名称
|
||||
//reqData.put("quantity", "1"); //商品数量
|
||||
//reqData.put("price", "1.01"); //商品单价
|
||||
//reqData.put("goodsCategory", ""); //商品类目;支 付宝独有
|
||||
//reqData.put("categoriesTree", "124868003|126232002|126252004"); //商品类目树
|
||||
//reqData.put("goodsDesc", ""); //商品描述;支 付宝独有
|
||||
//reqData.put("showUrl", ""); //商品展示地址 url;支付宝独有
|
||||
|
||||
//reqData.put("needReceipt", "00"); //电子发票功能 微信开具电子 发票使用
|
||||
//reqData.put("ledgerAccountFlag", "00"); //是否做分账 分账交易使 用;00:做; 01:不做;不传默认为不做分账
|
||||
//reqData.put("ledgerAccountEffectTime", "00"); //分账有效时间 单位为天;是 否做分账选择 00 时该字段必传
|
||||
// TODO: 2026/3/11 后端回调地址
|
||||
reqData.put("notifyUrl", "http://193.112.94.36:8081/mall/order/notify"); //回调地址
|
||||
//reqData.put("ylTrmNo", ""); //银联终端号
|
||||
//reqData.put("terminalId", ""); //TQ机具编号
|
||||
//reqData.put("deviceNo ", ""); //设备号
|
||||
//reqData.put("identityFlag", ""); //是否是实名支付
|
||||
//reqData.put("buyerIdType", "IDCARD"); //证件类型
|
||||
//reqData.put("buyerIdNo", "410523198701054018"); //证件号
|
||||
//reqData.put("buyerName", "张三"); //买家姓名
|
||||
//reqData.put("mobileNum", ""); //手机号
|
||||
//reqData.put("extend", ""); //备用
|
||||
|
||||
|
||||
reqBean.setReqData(reqData);
|
||||
String req = JSONObject.toJSONString(reqBean);
|
||||
//请求数据json字符串示例
|
||||
//String req= "{\"orgId\":\"26680846\",\"reqData\":{\"ordNo\":\"35081904944040745\",\"payType\":\"WECHAT\",\"customerIp\":\"\",\"subject\":\"聚合支付测试\",\"amt\":\"0.01\",\"payWay\":\"02\",\"tradeSource\":\"02\",\"userId\":\"2088101117955611\",\"trmIp\":\"172.16.2.1\",\"mno\":\"399190910000387\"},\"reqId\":\"08794fa24dcb467992a06b126e542be4\",\"signType\":\"RSA\",\"timestamp\":\"2020-04-03 17:51:34\",\"version\":\"1.0\"}";
|
||||
System.out.println("req:" + req);
|
||||
//此处不要改变reqData里面值的顺序用LinkedHashMap
|
||||
HashMap reqMap = JSON.parseObject(req, LinkedHashMap.class, Feature.OrderedField);
|
||||
//组装加密串
|
||||
String signContent = RSASignature.getOrderContent(reqMap);
|
||||
System.out.println("拼接后的参数:" + signContent);
|
||||
//sign
|
||||
String sign = RSASignature.encryptBASE64(RSASignature.sign(signContent, privateKey));
|
||||
System.out.println("============签名:" + sign);
|
||||
reqMap.put("sign", sign);
|
||||
|
||||
String reqStr = JSON.toJSONString(reqMap);
|
||||
System.out.println("请求参数:" + reqMap);
|
||||
System.out.println("请求参数:" + reqStr);
|
||||
String url = "https://openapi.tianquetech.com/order/reverseScan";
|
||||
|
||||
String resultJson = HttpUtils.connectPostUrl(url, reqStr);
|
||||
log.info("返回信息" + resultJson);
|
||||
System.out.println("返回信息:" + resultJson);
|
||||
//不要对reqData排序 所以用LinkedHashMap
|
||||
HashMap<String, Object> result = JSON.parseObject(resultJson, LinkedHashMap.class, Feature.OrderedField);
|
||||
if ("0000".equals(result.get("code"))) {
|
||||
//验签
|
||||
String signResult = result.get("sign").toString();
|
||||
result.remove("sign");
|
||||
String resultStr = RSASignature.getOrderContent(result);
|
||||
System.out.println(resultStr);
|
||||
//sign
|
||||
String resultSign = RSASignature.encryptBASE64(RSASignature.sign(signContent, privateKey));
|
||||
System.out.println("resultSign:" + resultSign);
|
||||
//组装加密串
|
||||
if (RSASignature.doCheck(resultStr, signResult, sxfPublic)) {
|
||||
log.info("===================验签成功==============");
|
||||
|
||||
String respDataStr = result.get("respData").toString();
|
||||
HashMap<String, Object> respData = JSON.parseObject(respDataStr, LinkedHashMap.class);
|
||||
// 获取 tranSts 和 ordNo
|
||||
String tranSts = Optional.ofNullable(respData)
|
||||
.map(m -> m.get("tranSts"))
|
||||
.map(Object::toString)
|
||||
.orElse("");
|
||||
|
||||
String ordNo = Optional.ofNullable(respData)
|
||||
.map(m -> m.get("ordNo"))
|
||||
.map(Object::toString)
|
||||
.orElse("");
|
||||
//被扫免密同步返回支付结果,不推送异步通知。 同步去修改支付状态,扣减库存
|
||||
//被扫输密,推送异步通知。 通过回调接口修改支付状态,扣减库存
|
||||
if ("SUCCESS".equals(tranSts)) {
|
||||
// 4. 查询订单(加行锁)
|
||||
Order order = orderMapper.selectForUpdateByOrderNo(ordNo);
|
||||
if (order == null) {
|
||||
log.error("订单不存在, orderNo:{}", ordNo);
|
||||
throw new ServiceException("订单不存在");
|
||||
}
|
||||
|
||||
// 5. 检查订单状态
|
||||
if (order.getPayStatus() == 2) {
|
||||
log.info("订单已支付, orderNo:{}", ordNo);
|
||||
throw new ServiceException("订单已支付");
|
||||
}
|
||||
|
||||
if (order.getPayStatus() != 0) {
|
||||
log.error("订单状态错误, orderNo:{}, status:{}",
|
||||
ordNo, order.getPayStatus());
|
||||
throw new ServiceException("订单状态错误");
|
||||
}
|
||||
|
||||
// 7. 查询订单明细
|
||||
List<OrderItem> items = orderMapper.selectByOrderId(order.getId());
|
||||
|
||||
// 8. 扣减实际库存
|
||||
for (OrderItem item : items) {
|
||||
int result1 = productMapper.reduceStock(
|
||||
order.getStoreId(),
|
||||
item.getProductId(),
|
||||
item.getQuantity()
|
||||
);
|
||||
if (result1 <= 0) {
|
||||
log.error("库存扣减失败, storeId:{}, productId:{}, quantity:{}",
|
||||
order.getStoreId(), item.getProductId(), item.getQuantity());
|
||||
throw new ServiceException("库存扣减失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 9. 更新订单状态为已支付
|
||||
Order updateOrder = new Order();
|
||||
updateOrder.setId(order.getId());
|
||||
updateOrder.setPayStatus(2);
|
||||
updateOrder.setPayTime(new Date());
|
||||
|
||||
int updated = orderMapper.updateById(updateOrder);
|
||||
if (updated <= 0) {
|
||||
log.error("订单状态更新失败, orderNo:{}",ordNo);
|
||||
throw new ServiceException("订单状态更新失败");
|
||||
}
|
||||
log.info("支付同步处理成功, orderNo:{}, storeId:{}",
|
||||
ordNo, order.getStoreId());
|
||||
}
|
||||
System.out.println("===================验签成功==============");
|
||||
}
|
||||
log.info("===================被扫支付==============" + resultJson);
|
||||
return resultJson;
|
||||
}
|
||||
throw new ServiceException(resultJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AjaxResult tradeQuery(TradeQueryDTO tradeQueryDTO) throws Exception {
|
||||
//合作方私钥(替换成自己的)
|
||||
String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM35qbWVNdBAU+oai8EVE3iSpA9wHAt4wOs+SufbkE/FNoMgp4EMrFNrdNkaKLQq4cRzqoNlJY+DZSL54dm7ekKZP/SLhWwNi9tJvhKsFpw6SRwthlKyA8lCW2mzFffFqLhGAayNoZTmHwkq8dotvecARVbiFtakzzFy/O7ATmzw3i57yaVgX2RtD8WWA7MGg5CQP1vvIDlXqd40SQcrJY6GNNv07Brv3fpyW2nXbvrxbOx+87NnG77/R7o8eFcWJF+yrqE1xeN3hDvHBFMFyf1M39Zig2I75vKRvHGqftAZuvlDG0nInwcSiMTE7wDsXvYzRnOsgJNVET7aFhzOvFAgMBAAECggEATjB4nRl2S2Whasnf0Ab77CoZSjn7HUXv2hymBkJNPq3eV0Hh5Pjjqmi7hIs0cdm9AWXd6RzySKpScQNjYQFxEOIdbayfTdzD24L2ivT6ZBca09Z9J/9Rik0wJ4ULTtny5JRc6VwGgOn9Twdsde8V4sr1nHUvpMaRH6WVxW8mX7+udgbK0Uxm5IFROeKGz0DSuG8SBpDhD2EB6P2Q+1fpna17yP1R08HtZNU6lQx7yMesaJuvKkwf4YiSjIhUtdbUXGhmbG5LFzoj3oRs25W68bIOQjT9i196hPQOYj6C7yz72nuY2rwFHhtsocfQRULWOHtsnWts9epvuFruDTdq8QKBgQD8bZhQkyQm6VTP0Hmv6sV9SXXKH0rb2V8hml7DxcpFl+l2/9UesH3qMoFKZ5C1YdPKZBNlD7fp5RxjtJ8MdwIp8MxRgMf/bZpIOx1Lfum1H+dKh5LF+oC4GGtJ5jqnwozNPog4keUn+bqKhiza9snA+6jy446A1Y8GGWlZwUGkVwKBgQDPxb7FZmOcotNnh8mS5pcx6d22UhBHIHz/X/dcjNQuI8tIRaIvkJm/4lfuxejCz3LY9k+VSQ96ZM2L5OqZGDlR7aMXQ3/FhuS4PEkNcDvccQPoig7U5zUUn2Jdqom6Lngh7Ka8B1v24ght0yXHHNChmMg/DJ42EzCnjpydWs6/QwKBgA9qGd0BvzlpEjbGgkfNzFWEQN8g3g9izL5ekN7fmyR4zFbp9He1S1sbzm1euaV53dcEGXMYbKCpzvv/sZ6vPcCV5cQsWwosBTnX8kgD7f2Tfyo78SiJzYZwZ0zR9E7+QF7gLK1Xq2ivhUakPuT4IQXZ2E1MvAz9/Yff0WEbvghtAoGAejFzC8c2yDUenaHHU+TXgNxor0Q+HIan3M0EvmJ4mxYkBMInK1AgjDBCxMOSK5gzlBPwI/0O5E2KcT7VFeqgM2XN5+2jpHi75PpXgFbEbdXtlYI0TNQZbKJ8CFg2nc+ciV8ThDvTwzOV/3kRm7N/o7ol8qaqGWVZ1QFTbFuugd8CgYBAsNVKCEHGZAwS5Csv/IBopcrJOJknmEHeAT+VwxtZK7yOJ4ZSNizzofu847YGO4uEhfcaC8lYxnj8XskuMG2KChbbxEA+qlnL/oMIwVrpE0ZbFmQ4WRTUfVQT+zCHMPuuuj5tLPTSMjQLGlq+r3XRzwUaX98J9GLKgHG7tXlphw==";
|
||||
//随行付公钥
|
||||
String sxfPublic = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjo1+KBcvwDSIo+nMYLeOJ19Ju4ii0xH66ZxFd869EWFWk/EJa3xIA2+4qGf/Ic7m7zi/NHuCnfUtUDmUdP0JfaZiYwn+1Ek7tYAOc1+1GxhzcexSJLyJlR2JLMfEM+rZooW4Ei7q3a8jdTWUNoak/bVPXnLEVLrbIguXABERQ0Ze0X9Fs0y/zkQFg8UjxUN88g2CRfMC6LldHm7UBo+d+WlpOYH7u0OTzoLLiP/04N1cfTgjjtqTBI7qkOGxYs6aBZHG1DJ6WdP+5w+ho91sBTVajsCxAaMoExWQM2ipf/1qGdsWmkZScPflBqg7m0olOD87ymAVP/3Tcbvi34bDfwIDAQAB";
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
|
||||
String dataStr = df.format(new Date());
|
||||
|
||||
ApiRequestBean<JSONObject> reqBean = new ApiRequestBean<JSONObject>();
|
||||
|
||||
reqBean.setOrgId("57594783"); //机构id
|
||||
reqBean.setReqId(UUID.randomUUID().toString().replaceAll("-", ""));
|
||||
reqBean.setSignType("RSA");
|
||||
reqBean.setTimestamp(dataStr);
|
||||
reqBean.setVersion("1.0");
|
||||
JSONObject reqData = new JSONObject();
|
||||
//业务参数
|
||||
reqData.put("ordNo", tradeQueryDTO.getOrdNo()); //商户订单号
|
||||
reqData.put("mno", "399260311280126"); //商户编号
|
||||
reqData.put("trmIp", "172.16.2.1");
|
||||
reqBean.setReqData(reqData);
|
||||
String req = JSONObject.toJSONString(reqBean);
|
||||
//此处不要改变reqData里面值的顺序用LinkedHashMap
|
||||
HashMap reqMap = JSON.parseObject(req, LinkedHashMap.class, Feature.OrderedField);
|
||||
//组装加密串
|
||||
String signContent = RSASignature.getOrderContent(reqMap);
|
||||
//sign
|
||||
String sign = RSASignature.encryptBASE64(RSASignature.sign(signContent, privateKey));
|
||||
reqMap.put("sign", sign);
|
||||
|
||||
String reqStr = JSON.toJSONString(reqMap);
|
||||
String url = "https://openapi.tianquetech.com/query/tradeQuery";
|
||||
String resultJson = HttpUtils.connectPostUrl(url, reqStr);
|
||||
//不要对reqData排序 所以用LinkedHashMap
|
||||
HashMap<String, Object> result = JSON.parseObject(resultJson, LinkedHashMap.class, Feature.OrderedField);
|
||||
if ("0000".equals(result.get("code"))) {
|
||||
//验签
|
||||
String signResult = result.get("sign").toString();
|
||||
result.remove("sign");
|
||||
String resultStr = RSASignature.getOrderContent(result);
|
||||
System.out.println(resultStr);
|
||||
//sign
|
||||
String resultSign = RSASignature.encryptBASE64(RSASignature.sign(signContent, privateKey));
|
||||
System.out.println("resultSign:" + resultSign);
|
||||
//组装加密串
|
||||
if (RSASignature.doCheck(resultStr, signResult, sxfPublic)) {
|
||||
log.info("===================验签成功==============");
|
||||
PaymentResponseVO paymentResponseVO = JSON.parseObject(resultJson, PaymentResponseVO.class);
|
||||
return AjaxResult.success(paymentResponseVO);
|
||||
}
|
||||
}
|
||||
return AjaxResult.error(resultJson);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OrderCountVO getOrderCount(String storeId) {
|
||||
return orderMapper.getOrderCount(storeId);
|
||||
}
|
||||
|
||||
|
||||
//调用支付
|
||||
public String pay(String orderNo, String totalAmount) throws Exception {
|
||||
//合作方私钥(替换成自己的)
|
||||
String privateKey = "MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDM35qbWVNdBAU+oai8EVE3iSpA9wHAt4wOs+SufbkE/FNoMgp4EMrFNrdNkaKLQq4cRzqoNlJY+DZSL54dm7ekKZP/SLhWwNi9tJvhKsFpw6SRwthlKyA8lCW2mzFffFqLhGAayNoZTmHwkq8dotvecARVbiFtakzzFy/O7ATmzw3i57yaVgX2RtD8WWA7MGg5CQP1vvIDlXqd40SQcrJY6GNNv07Brv3fpyW2nXbvrxbOx+87NnG77/R7o8eFcWJF+yrqE1xeN3hDvHBFMFyf1M39Zig2I75vKRvHGqftAZuvlDG0nInwcSiMTE7wDsXvYzRnOsgJNVET7aFhzOvFAgMBAAECggEATjB4nRl2S2Whasnf0Ab77CoZSjn7HUXv2hymBkJNPq3eV0Hh5Pjjqmi7hIs0cdm9AWXd6RzySKpScQNjYQFxEOIdbayfTdzD24L2ivT6ZBca09Z9J/9Rik0wJ4ULTtny5JRc6VwGgOn9Twdsde8V4sr1nHUvpMaRH6WVxW8mX7+udgbK0Uxm5IFROeKGz0DSuG8SBpDhD2EB6P2Q+1fpna17yP1R08HtZNU6lQx7yMesaJuvKkwf4YiSjIhUtdbUXGhmbG5LFzoj3oRs25W68bIOQjT9i196hPQOYj6C7yz72nuY2rwFHhtsocfQRULWOHtsnWts9epvuFruDTdq8QKBgQD8bZhQkyQm6VTP0Hmv6sV9SXXKH0rb2V8hml7DxcpFl+l2/9UesH3qMoFKZ5C1YdPKZBNlD7fp5RxjtJ8MdwIp8MxRgMf/bZpIOx1Lfum1H+dKh5LF+oC4GGtJ5jqnwozNPog4keUn+bqKhiza9snA+6jy446A1Y8GGWlZwUGkVwKBgQDPxb7FZmOcotNnh8mS5pcx6d22UhBHIHz/X/dcjNQuI8tIRaIvkJm/4lfuxejCz3LY9k+VSQ96ZM2L5OqZGDlR7aMXQ3/FhuS4PEkNcDvccQPoig7U5zUUn2Jdqom6Lngh7Ka8B1v24ght0yXHHNChmMg/DJ42EzCnjpydWs6/QwKBgA9qGd0BvzlpEjbGgkfNzFWEQN8g3g9izL5ekN7fmyR4zFbp9He1S1sbzm1euaV53dcEGXMYbKCpzvv/sZ6vPcCV5cQsWwosBTnX8kgD7f2Tfyo78SiJzYZwZ0zR9E7+QF7gLK1Xq2ivhUakPuT4IQXZ2E1MvAz9/Yff0WEbvghtAoGAejFzC8c2yDUenaHHU+TXgNxor0Q+HIan3M0EvmJ4mxYkBMInK1AgjDBCxMOSK5gzlBPwI/0O5E2KcT7VFeqgM2XN5+2jpHi75PpXgFbEbdXtlYI0TNQZbKJ8CFg2nc+ciV8ThDvTwzOV/3kRm7N/o7ol8qaqGWVZ1QFTbFuugd8CgYBAsNVKCEHGZAwS5Csv/IBopcrJOJknmEHeAT+VwxtZK7yOJ4ZSNizzofu847YGO4uEhfcaC8lYxnj8XskuMG2KChbbxEA+qlnL/oMIwVrpE0ZbFmQ4WRTUfVQT+zCHMPuuuj5tLPTSMjQLGlq+r3XRzwUaX98J9GLKgHG7tXlphw==";
|
||||
//随行付公钥
|
||||
String sxfPublic = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjo1+KBcvwDSIo+nMYLeOJ19Ju4ii0xH66ZxFd869EWFWk/EJa3xIA2+4qGf/Ic7m7zi/NHuCnfUtUDmUdP0JfaZiYwn+1Ek7tYAOc1+1GxhzcexSJLyJlR2JLMfEM+rZooW4Ei7q3a8jdTWUNoak/bVPXnLEVLrbIguXABERQ0Ze0X9Fs0y/zkQFg8UjxUN88g2CRfMC6LldHm7UBo+d+WlpOYH7u0OTzoLLiP/04N1cfTgjjtqTBI7qkOGxYs6aBZHG1DJ6WdP+5w+ho91sBTVajsCxAaMoExWQM2ipf/1qGdsWmkZScPflBqg7m0olOD87ymAVP/3Tcbvi34bDfwIDAQAB";
|
||||
|
||||
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
|
||||
String dataStr = df.format(new Date());
|
||||
|
||||
ApiRequestBean<JSONObject> reqBean = new ApiRequestBean<JSONObject>();
|
||||
|
||||
reqBean.setOrgId("57594783"); //机构id
|
||||
reqBean.setReqId(UUID.randomUUID().toString().replaceAll("-", ""));
|
||||
reqBean.setSignType("RSA");
|
||||
reqBean.setTimestamp(dataStr);
|
||||
reqBean.setVersion("1.0");
|
||||
JSONObject reqData = new JSONObject();
|
||||
|
||||
//业务参数
|
||||
reqData.put("ordNo", orderNo); //商户订单号
|
||||
reqData.put("mno", "399260311280126"); //商户编号
|
||||
//reqData.put("subMechId", ""); //子商户号
|
||||
reqData.put("amt", totalAmount); //订单总金额
|
||||
//reqData.put("discountAmt", ""); //参与优惠金额
|
||||
//reqData.put("unDiscountAmt", ""); //不参与优惠金额
|
||||
// TODO: 2026/3/11 后续前端传
|
||||
reqData.put("payType", "WECHAT"); //支付渠道
|
||||
// TODO: 2026/3/11 后续前端传
|
||||
reqData.put("payWay", "02"); //支付方式 02 公众号/服 务窗/js支付 03 小程序
|
||||
//reqData.put("timeExpire", "10"); //订单失效时间, 以分钟为单位
|
||||
//reqData.put("limitPay", "00"); //限制卡类型: 00-全部 01-限定不能使 用信用卡支付 默认值 00
|
||||
reqData.put("subject", "聚合支付测试"); //订单标题
|
||||
//reqData.put("hbFqNum", "6"); //花呗分期数,仅可上送 6 或 12
|
||||
//reqData.put("hbFqPercent", "0"); //卖家承担分期 服务费比例,仅支持上送 0 或 100
|
||||
// TODO: 2026/3/11 后续前端传
|
||||
reqData.put("tradeSource", "02"); //交易来源 01服务商,02收银台,03硬件
|
||||
reqData.put("trmIp", "172.16.2.1");
|
||||
reqData.put("customerIp", ""); //持卡人ip地址,银联js支付时必传
|
||||
// TODO: 2026/3/11 后续前端传code,后端去获取对应的id
|
||||
reqData.put("userId", "o3LXk25WUnC4U2lGeuEvLLu0fjBs"); //用户号 微信:openid; 支付宝:userid;银联:userid;微信&支付宝必传,银联js为非必传
|
||||
//reqData.put("subAppid", "wx24210004370ec43b"); //微信子公众号
|
||||
//reqData.put("outFrontUrl", ""); //js 支付,前台 成功通知地址
|
||||
//reqData.put("outFrontFailUrl", ""); //js 支付,前台 事变通知地址
|
||||
// TODO: 2026/3/11 后端回调地址
|
||||
reqData.put("notifyUrl", "http://193.112.94.36:8081/mall/order/notify"); //回调地址
|
||||
//reqData.put("needReceipt", "00"); //电子发票功能 微信开具电子 发票使用
|
||||
//reqData.put("ledgerAccountFlag", "00"); //是否做分账 分账交易使 用;00:做; 01:不做;不传默认为不做分账
|
||||
//reqData.put("ledgerAccountEffectTime", "00"); //分账有效时间 单位为天;是 否做分账选择 00 时该字段必传
|
||||
//reqData.put("ylTrmNo", ""); //银联终端号
|
||||
//reqData.put("terminalId", ""); //TQ机具编号
|
||||
//reqData.put("identityFlag", ""); //是否是实名支付
|
||||
//reqData.put("buyerIdType", "IDCARD"); //证件类型
|
||||
//reqData.put("buyerIdNo", "410523198701054018"); //证件号
|
||||
//reqData.put("buyerName", "张三"); //买家姓名
|
||||
//reqData.put("mobileNum", ""); //手机号
|
||||
//reqData.put("extend", ""); //备用
|
||||
//reqData.put("goodsTag", "00"); //订单优惠标识 00:是,01: 否
|
||||
|
||||
//reqData.put("couponDetail", ""); //优惠详情信息,见下面三个字段
|
||||
//reqData.put("costPrice", "200"); //订单原价保留两 位小数;微信 独有
|
||||
//reqData.put("receiptId ", "123456789"); //商品小票
|
||||
|
||||
//reqData.put("goodsDetail", "123456789"); //单品优惠信息使用 json 数组格式提交
|
||||
//reqData.put("goodsId", "200"); //商品编码
|
||||
//reqData.put("thirdGoodsId", "12345678"); //微信/支付宝侧商品码
|
||||
//reqData.put("goodsName", "苹果电脑"); //商品名称
|
||||
//reqData.put("quantity", "1"); //商品数量
|
||||
//reqData.put("price", "1.01"); //商品单价
|
||||
//reqData.put("goodsCategory", ""); //商品类目;支 付宝独有
|
||||
//reqData.put("categoriesTree", "124868003|126232002|126252004"); //商品类目树
|
||||
//reqData.put("goodsDesc", ""); //商品描述;支 付宝独有
|
||||
//reqData.put("showUrl", ""); //商品展示地址 url;支付宝独有
|
||||
|
||||
reqBean.setReqData(reqData);
|
||||
String req = JSONObject.toJSONString(reqBean);
|
||||
//请求数据json字符串示例
|
||||
//String req= "{\"orgId\":\"26680846\",\"reqData\":{\"ordNo\":\"35081904944040745\",\"payType\":\"WECHAT\",\"customerIp\":\"\",\"subject\":\"聚合支付测试\",\"amt\":\"0.01\",\"payWay\":\"02\",\"tradeSource\":\"02\",\"userId\":\"2088101117955611\",\"trmIp\":\"172.16.2.1\",\"mno\":\"399190910000387\"},\"reqId\":\"08794fa24dcb467992a06b126e542be4\",\"signType\":\"RSA\",\"timestamp\":\"2020-04-03 17:51:34\",\"version\":\"1.0\"}";
|
||||
System.out.println("req:" + req);
|
||||
//此处不要改变reqData里面值的顺序用LinkedHashMap
|
||||
HashMap reqMap = JSON.parseObject(req, LinkedHashMap.class, Feature.OrderedField);
|
||||
//组装加密串
|
||||
String signContent = RSASignature.getOrderContent(reqMap);
|
||||
System.out.println("拼接后的参数:" + signContent);
|
||||
//sign
|
||||
String sign = RSASignature.encryptBASE64(RSASignature.sign(signContent, privateKey));
|
||||
System.out.println("============签名:" + sign);
|
||||
reqMap.put("sign", sign);
|
||||
|
||||
String reqStr = JSON.toJSONString(reqMap);
|
||||
System.out.println("请求参数:" + reqMap);
|
||||
System.out.println("请求参数:" + reqStr);
|
||||
String url = "https://openapi.tianquetech.com/order/jsapiScan";
|
||||
|
||||
String resultJson = HttpUtils.connectPostUrl(url, reqStr);
|
||||
log.info("返回信息" + resultJson);
|
||||
System.out.println("返回信息:" + resultJson);
|
||||
//不要对reqData排序 所以用LinkedHashMap
|
||||
HashMap<String, Object> result = JSON.parseObject(resultJson, LinkedHashMap.class, Feature.OrderedField);
|
||||
if ("0000".equals(result.get("code"))) {
|
||||
//验签
|
||||
String signResult = result.get("sign").toString();
|
||||
result.remove("sign");
|
||||
String resultStr = RSASignature.getOrderContent(result);
|
||||
System.out.println(resultStr);
|
||||
//sign
|
||||
String resultSign = RSASignature.encryptBASE64(RSASignature.sign(signContent, privateKey));
|
||||
System.out.println("resultSign:" + resultSign);
|
||||
//组装加密串
|
||||
if (RSASignature.doCheck(resultStr, signResult, sxfPublic)) {
|
||||
log.info("===================验签成功==============");
|
||||
|
||||
System.out.println("===================验签成功==============");
|
||||
}
|
||||
return resultJson;
|
||||
}
|
||||
throw new ServiceException(resultJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成锁key(包含门店ID)
|
||||
*/
|
||||
private String generateLockKey(Integer storeId, List<OrderItem> items) {
|
||||
// 对商品ID排序
|
||||
String productIds = items.stream()
|
||||
.map(item -> String.valueOf(item.getProductId()))
|
||||
.sorted()
|
||||
.collect(Collectors.joining("_"));
|
||||
|
||||
// 组合门店ID和商品ID
|
||||
String originalKey = storeId + ":" + productIds;
|
||||
|
||||
// 计算MD5避免key过长
|
||||
String hash = DigestUtils.md5DigestAsHex(originalKey.getBytes());
|
||||
return "lock:store:products:" + hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成订单号
|
||||
*/
|
||||
private String generateOrderNo() {
|
||||
// 格式:年月日时分秒 + 雪花算法ID后8位
|
||||
String dateStr = LocalDateTime.now()
|
||||
.format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
|
||||
long snowflakeId = IdUtil.getSnowflake().nextId();
|
||||
String suffix = String.valueOf(snowflakeId).substring(8);
|
||||
return dateStr + suffix;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,11 @@ package com.ruoyi.web.service.impl;
|
|||
import com.ruoyi.common.constant.UserConstants;
|
||||
import com.ruoyi.common.utils.StringUtils;
|
||||
import com.ruoyi.web.domain.Product;
|
||||
import com.ruoyi.web.dto.BatchBarcodeQueryDTO;
|
||||
import com.ruoyi.web.dto.BatchStoreDTO;
|
||||
import com.ruoyi.web.mapper.ProductMapper;
|
||||
import com.ruoyi.web.service.ProductService;
|
||||
import com.ruoyi.web.vo.ProductVo;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
|
@ -79,4 +81,9 @@ public class ProductServiceImpl implements ProductService {
|
|||
}
|
||||
return productMapper.batchProductById(dto.getIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Product> getByBarcodes(BatchBarcodeQueryDTO dto) {
|
||||
return productMapper.getByBarcodes(dto);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,144 @@
|
|||
package com.ruoyi.web.utils;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.SSLContextBuilder;
|
||||
import org.apache.http.conn.ssl.TrustStrategy;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class HttpUtils {
|
||||
|
||||
/**
|
||||
* 创建没有证书的SSL链接工厂类
|
||||
* @return
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws KeyManagementException
|
||||
*/
|
||||
public static SSLConnectionSocketFactory getSSLConnectionSocketFactory() throws NoSuchAlgorithmException,
|
||||
KeyStoreException, KeyManagementException {
|
||||
SSLContextBuilder context = new SSLContextBuilder().useProtocol("TLSv1.2");
|
||||
context.loadTrustMaterial(null, new TrustStrategy() {
|
||||
@Override
|
||||
public boolean isTrusted(X509Certificate[] arg0, String arg1)
|
||||
throws CertificateException {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return new SSLConnectionSocketFactory(context.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 链接GET请求
|
||||
* @param url
|
||||
* @return
|
||||
* @throws KeyManagementException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws ClientProtocolException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String connectGetUrl(String url) throws KeyManagementException,
|
||||
NoSuchAlgorithmException, KeyStoreException, ClientProtocolException, IOException {
|
||||
SSLConnectionSocketFactory sslsf = getSSLConnectionSocketFactory();
|
||||
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
|
||||
RequestConfig config = RequestConfig.custom().setSocketTimeout(10000).setConnectTimeout(5000).build();
|
||||
HttpGet httpGet = null;
|
||||
CloseableHttpResponse resp = null;
|
||||
String jsonString = "";
|
||||
try {
|
||||
httpGet = new HttpGet(url);
|
||||
httpGet.setConfig(config);
|
||||
resp = client.execute(httpGet);
|
||||
HttpEntity entity = resp.getEntity();
|
||||
jsonString = EntityUtils.toString(entity, "UTF-8");
|
||||
} finally {
|
||||
if (resp != null) {
|
||||
try {
|
||||
resp.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (httpGet != null) {
|
||||
httpGet.releaseConnection();
|
||||
}
|
||||
}
|
||||
return jsonString;
|
||||
}
|
||||
|
||||
/**
|
||||
* 链接POST请求
|
||||
* @param url
|
||||
* @param jsonParm
|
||||
* @return
|
||||
* @throws KeyManagementException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws KeyStoreException
|
||||
* @throws ClientProtocolException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static String connectPostUrl(String url, String jsonParm) throws KeyManagementException,
|
||||
NoSuchAlgorithmException, KeyStoreException, ClientProtocolException, IOException {
|
||||
SSLConnectionSocketFactory sslsf = getSSLConnectionSocketFactory();
|
||||
CloseableHttpClient client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
|
||||
RequestConfig config = RequestConfig.custom().setSocketTimeout(40000).setConnectTimeout(10000).build();
|
||||
HttpPost httpPost = null;
|
||||
CloseableHttpResponse resp = null;
|
||||
try {
|
||||
httpPost = new HttpPost(url);
|
||||
httpPost.setConfig(config);
|
||||
StringEntity params = new StringEntity(jsonParm,"UTF-8");
|
||||
params.setContentType("application/json");
|
||||
httpPost.setEntity(params);
|
||||
resp = client.execute(httpPost);
|
||||
HttpEntity entity = resp.getEntity();
|
||||
String jsonString = EntityUtils.toString(entity, "UTF-8");
|
||||
|
||||
if (resp.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
|
||||
return jsonString;
|
||||
}
|
||||
}catch (IOException e) {
|
||||
if (e instanceof org.apache.http.conn.ConnectTimeoutException) {
|
||||
throw new org.apache.http.conn.ConnectTimeoutException("connect timed out");
|
||||
}
|
||||
if (e instanceof java.net.SocketTimeoutException) {
|
||||
throw new java.net.SocketTimeoutException("Read timed out");
|
||||
}
|
||||
throw new IOException("IOException");
|
||||
} finally {
|
||||
if (resp != null) {
|
||||
try {
|
||||
resp.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
if (httpPost != null) {
|
||||
httpPost.releaseConnection();
|
||||
}
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
package com.ruoyi.web.utils;
|
||||
|
||||
import java.security.KeyFactory;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.spec.PKCS8EncodedKeySpec;
|
||||
import java.security.spec.X509EncodedKeySpec;
|
||||
import java.util.*;
|
||||
|
||||
|
||||
/**
|
||||
* RSA签名验签类
|
||||
*/
|
||||
public class RSASignature {
|
||||
|
||||
/**
|
||||
* 签名算法
|
||||
*/
|
||||
public static final String SIGN_ALGORITHMS = "SHA1WithRSA";
|
||||
|
||||
/**
|
||||
* 针对参数进行排序拼装
|
||||
* @param sortedParams
|
||||
* @return
|
||||
*/
|
||||
public static String getOrderContent(Map<String, Object> requestParam) {
|
||||
Map<String, Object> sortedParams = new TreeMap<String, Object>();
|
||||
if ((requestParam != null) && (requestParam.size() > 0)) {
|
||||
sortedParams.putAll(requestParam);
|
||||
}
|
||||
StringBuffer content = new StringBuffer();
|
||||
List<String> keys = new ArrayList<String>(sortedParams.keySet());
|
||||
Collections.sort(keys);
|
||||
int index = 0;
|
||||
for (int i = 0; i < keys.size(); i++) {
|
||||
String key = keys.get(i);
|
||||
Object value = sortedParams.get(key);
|
||||
if (key!=null &&!"".equals(key)&& value != null) {
|
||||
content.append((index == 0 ? "" : "&") + key + "=" + value);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* rsa 签名
|
||||
* @param content 待签名内容
|
||||
* @param privateKey 私钥
|
||||
* @return
|
||||
*/
|
||||
public static byte[] sign(String content, String privateKey) {
|
||||
try {
|
||||
PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(decryptBASE64(privateKey));
|
||||
KeyFactory keyf = KeyFactory.getInstance("RSA");
|
||||
PrivateKey priKey = keyf.generatePrivate(priPKCS8);
|
||||
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
|
||||
signature.initSign(priKey);
|
||||
signature.update(content.getBytes("UTF-8"));
|
||||
byte[] signed = signature.sign();
|
||||
return signed;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* RSA验签名检查
|
||||
* @param content 待签名数据
|
||||
* @param sign 签名值
|
||||
* @param publicKey 分配给开发商公钥
|
||||
* @return 布尔值
|
||||
*/
|
||||
public static boolean doCheck(String content, String sign, String publicKey) {
|
||||
try {
|
||||
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
||||
byte[] encodedKey = decryptBASE64(publicKey);
|
||||
PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
|
||||
|
||||
|
||||
java.security.Signature signature = java.security.Signature.getInstance(SIGN_ALGORITHMS);
|
||||
|
||||
signature.initVerify(pubKey);
|
||||
signature.update(content.getBytes("UTF-8"));
|
||||
|
||||
boolean bverify = signature.verify(decryptBASE64(sign));
|
||||
return bverify;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static String encryptBASE64(byte[] key) throws Exception {
|
||||
String base64encodedString = Base64.getEncoder().encodeToString(key);
|
||||
return base64encodedString.replaceAll("[\\s*\t\n\r]", "");
|
||||
}
|
||||
public static byte[] decryptBASE64(String key) throws Exception {
|
||||
byte[] base64decodedBytes = Base64.getDecoder().decode(key.replaceAll("[\\s*\t\n\r]", ""));
|
||||
return base64decodedBytes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.ruoyi.web.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OrderCountVO {
|
||||
|
||||
//支付订单数量
|
||||
private String paidOrderCount;
|
||||
|
||||
//支付订单金额
|
||||
private String totalPayAmount;
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.ruoyi.web.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class OrderVo {
|
||||
|
||||
private String orderNo;
|
||||
|
||||
private String amount;
|
||||
}
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
package com.ruoyi.web.vo;
|
||||
|
||||
import lombok.Data;
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 支付响应VO
|
||||
*/
|
||||
@Data
|
||||
public class PaymentResponseVO implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 消息
|
||||
*/
|
||||
private String msg;
|
||||
|
||||
/**
|
||||
* 业务码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String sign;
|
||||
|
||||
/**
|
||||
* 签名类型
|
||||
*/
|
||||
private String signType;
|
||||
|
||||
/**
|
||||
* 机构ID
|
||||
*/
|
||||
private String orgId;
|
||||
|
||||
/**
|
||||
* 请求ID
|
||||
*/
|
||||
private String reqId;
|
||||
|
||||
/**
|
||||
* 响应数据
|
||||
*/
|
||||
private RespData respData;
|
||||
|
||||
/**
|
||||
* 响应数据内部类
|
||||
*/
|
||||
@Data
|
||||
public static class RespData implements Serializable {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 支付时间 yyyyMMddHHmmss
|
||||
*/
|
||||
private String payTime;
|
||||
|
||||
/**
|
||||
* 业务码
|
||||
*/
|
||||
private String bizCode;
|
||||
|
||||
/**
|
||||
* 商品标题
|
||||
*/
|
||||
private String subject;
|
||||
|
||||
/**
|
||||
* 结算金额
|
||||
*/
|
||||
private String settleAmt;
|
||||
|
||||
/**
|
||||
* 支付方式 01-微信 02-支付宝
|
||||
*/
|
||||
private String payWay;
|
||||
|
||||
/**
|
||||
* 收单机构流水号
|
||||
*/
|
||||
private String sxfUuid;
|
||||
|
||||
/**
|
||||
* 支付银行
|
||||
*/
|
||||
private String payBank;
|
||||
|
||||
/**
|
||||
* 交易流水号
|
||||
*/
|
||||
private String uuid;
|
||||
|
||||
/**
|
||||
* 场景
|
||||
*/
|
||||
private String scene;
|
||||
|
||||
/**
|
||||
* 交易时间 yyyyMMddHHmmss
|
||||
*/
|
||||
private String tranTime;
|
||||
|
||||
/**
|
||||
* 结算批次号
|
||||
*/
|
||||
private String settlementBatchNo;
|
||||
|
||||
/**
|
||||
* 支付类型 WECHAT/ALIPAY
|
||||
*/
|
||||
private String payType;
|
||||
|
||||
/**
|
||||
* 订单号
|
||||
*/
|
||||
private String ordNo;
|
||||
|
||||
/**
|
||||
* 收单机构标识
|
||||
*/
|
||||
private String szltFlag;
|
||||
|
||||
/**
|
||||
* 业务消息
|
||||
*/
|
||||
private String bizMsg;
|
||||
|
||||
/**
|
||||
* 手续费金额
|
||||
*/
|
||||
private String recFeeAmt;
|
||||
|
||||
/**
|
||||
* 手续费费率
|
||||
*/
|
||||
private String recFeeRate;
|
||||
|
||||
/**
|
||||
* 收单机构手续费
|
||||
*/
|
||||
private String szltRecfeeAmt;
|
||||
|
||||
/**
|
||||
* 原始交易金额
|
||||
*/
|
||||
private String oriTranAmt;
|
||||
|
||||
/**
|
||||
* 渠道ID
|
||||
*/
|
||||
private String channelId;
|
||||
|
||||
/**
|
||||
* 完成时间 yyyyMMddHHmmss
|
||||
*/
|
||||
private String finishTime;
|
||||
|
||||
/**
|
||||
* 交易状态 SUCCESS-成功
|
||||
*/
|
||||
private String tranSts;
|
||||
|
||||
/**
|
||||
* 积分金额
|
||||
*/
|
||||
private String pointAmount;
|
||||
|
||||
/**
|
||||
* 记账标识
|
||||
*/
|
||||
private String ledgerAccountFlag;
|
||||
|
||||
/**
|
||||
* 交易码
|
||||
*/
|
||||
private String tradeCode;
|
||||
|
||||
/**
|
||||
* 清算日期 yyyyMMdd
|
||||
*/
|
||||
private String clearDt;
|
||||
|
||||
/**
|
||||
* 第三方交易流水号
|
||||
*/
|
||||
private String transactionId;
|
||||
|
||||
/**
|
||||
* 子商户号
|
||||
*/
|
||||
private String subMechId;
|
||||
|
||||
/**
|
||||
* 总抵扣金额
|
||||
*/
|
||||
private String totalOffstAmt;
|
||||
|
||||
/**
|
||||
* 交易消息
|
||||
*/
|
||||
private String tradeMsg;
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
private String drType;
|
||||
|
||||
/**
|
||||
* 获取结算金额(转为BigDecimal)
|
||||
*/
|
||||
public BigDecimal getSettleAmtDecimal() {
|
||||
return settleAmt != null ? new BigDecimal(settleAmt) : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取原始交易金额(转为BigDecimal)
|
||||
*/
|
||||
public BigDecimal getOriTranAmtDecimal() {
|
||||
return oriTranAmt != null ? new BigDecimal(oriTranAmt) : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否支付成功
|
||||
*/
|
||||
public boolean isSuccess() {
|
||||
return "SUCCESS".equals(tranSts) && "0000".equals(bizCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.ruoyi.web.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ProductVo {
|
||||
private Long id;
|
||||
private String productName;
|
||||
private String storePrice;
|
||||
private String mainImage;
|
||||
private String productBarCode; // 商品条码
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.web.mapper.DeviceMapper">
|
||||
<resultMap id="BaseResultMap" type="com.ruoyi.web.domain.Device">
|
||||
<id property="id" column="id"/>
|
||||
<result column="store_id" property="storeId"/>
|
||||
<result column="device_code" property="deviceCode"/>
|
||||
<result column="device_name" property="deviceName"/>
|
||||
<result column="device_type" property="deviceType"/>
|
||||
<result column="device_brand" property="deviceBrand"/>
|
||||
<result column="device_model" property="deviceModel"/>
|
||||
<result column="channel" property="channel"/>
|
||||
<result column="mac_address" property="macAddress"/>
|
||||
<result column="auth_code" property="authCode"/>
|
||||
<result column="install_date" property="installDate"/>
|
||||
<result column="expire_date" property="expireDate"/>
|
||||
<result column="device_status" property="deviceStatus"/>
|
||||
<result column="last_heartbeat" property="lastHeartbeat"/>
|
||||
<result column="remark" property="remark"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="getDevice" resultMap="BaseResultMap">
|
||||
select *
|
||||
from mall_device_info
|
||||
where store_id = #{storeId}
|
||||
and del_flag = 0
|
||||
</select>
|
||||
|
||||
<select id="getSheXiangTou" resultMap="BaseResultMap">
|
||||
select *
|
||||
from mall_device_info
|
||||
where store_id = #{storeId}
|
||||
and del_flag = 0 and device_type = 1
|
||||
</select>
|
||||
|
||||
<insert id="addDevice" parameterType="Device">
|
||||
insert into mall_device_info (
|
||||
<if test="storeId != null">store_id,</if>
|
||||
<if test="deviceCode != null and deviceCode != ''">device_code,</if>
|
||||
<if test="deviceName != null and deviceName != ''">device_name,</if>
|
||||
<if test="deviceType != null">device_type,</if>
|
||||
<if test="deviceBrand != null and deviceBrand != ''">device_brand,</if>
|
||||
<if test="deviceModel != null and deviceModel != ''">device_model,</if>
|
||||
<if test="channel != null and channel != ''">channel,</if>
|
||||
<if test="macAddress != null and macAddress != ''">mac_address,</if>
|
||||
<if test="authCode != null and authCode != ''">auth_code,</if>
|
||||
<if test="installDate != null ">install_date,</if>
|
||||
<if test="expireDate != null">expire_date,</if>
|
||||
<if test="deviceStatus != null ">device_status,</if>
|
||||
<if test="lastHeartbeat != null ">last_heartbeat,</if>
|
||||
<if test="remark != null and remark != ''">remark,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="storeId != null">#{storeId},</if>
|
||||
<if test="deviceCode != null and deviceCode != ''">#{deviceCode},</if>
|
||||
<if test="deviceName != null and deviceName != ''">#{deviceName},</if>
|
||||
<if test="deviceType != null">#{deviceType},</if>
|
||||
<if test="deviceBrand != null and deviceBrand != ''">#{deviceBrand},</if>
|
||||
<if test="deviceModel != null and deviceModel != ''">#{deviceModel},</if>
|
||||
<if test="channel != null and channel != ''">#{channel},</if>
|
||||
<if test="macAddress != null and macAddress != ''">#{macAddress},</if>
|
||||
<if test="authCode != null and authCode != ''">#{authCode},</if>
|
||||
<if test="installDate != null">#{installDate},</if>
|
||||
<if test="expireDate != null">#{expireDate},</if>
|
||||
<if test="deviceStatus != null">#{deviceStatus},</if>
|
||||
<if test="lastHeartbeat != null">#{lastHeartbeat},</if>
|
||||
<if test="remark != null and remark != ''">#{remark},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="com.ruoyi.web.mapper.DoorScanRecordMapper">
|
||||
<resultMap id="BaseResultMap" type="com.ruoyi.web.domain.DoorScanRecord">
|
||||
<id property="id" column="id"/>
|
||||
<result column="operator_name" property="operatorName"/>
|
||||
<result column="operator_phone" property="operatorPhone"/>
|
||||
<result column="store_id" property="storeId"/>
|
||||
<result column="shop_name" property="shopName"/>
|
||||
<result column="scan_type" property="scanType"/>
|
||||
<result column="scan_time" property="scanTime"/>
|
||||
<result column="create_time" property="createTime"/>
|
||||
<result column="update_time" property="updateTime"/>
|
||||
<result column="create_by" property="createBy"/>
|
||||
<result column="update_by" property="updateBy"/>
|
||||
</resultMap>
|
||||
|
||||
|
||||
<insert id="addDoorScanRecord">
|
||||
insert into mall_scan_record (
|
||||
<if test="storeId != null">store_id,</if>
|
||||
<if test="operatorName != null and operatorName != ''">operator_name ,</if>
|
||||
<if test="operatorPhone != null and operatorPhone != ''">operator_phone ,</if>
|
||||
<if test="scanType != null and scanType != ''">scan_type ,</if>
|
||||
<if test="shopName != null and shopName != ''">shop_name ,</if>
|
||||
<if test="scanTime != null">scan_time ,</if>
|
||||
<if test="createBy != null and createBy != ''">create_by,</if>
|
||||
create_time
|
||||
)values(
|
||||
<if test="storeId != null">#{storeId},</if>
|
||||
<if test="operatorName != null and operatorName != ''">#{operatorName},</if>
|
||||
<if test="operatorPhone != null and operatorPhone != ''">#{operatorPhone},</if>
|
||||
<if test="scanType != null and scanType != ''">#{scanType},</if>
|
||||
<if test="shopName != null and shopName != ''">#{shopName},</if>
|
||||
<if test="scanTime != null">#{scanTime},</if>
|
||||
<if test="createBy != null and createBy != ''">#{createBy},</if>
|
||||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<select id="selectScanRecordList" parameterType="DoorScanRecord" resultMap="BaseResultMap">
|
||||
select * from mall_scan_record
|
||||
<where>
|
||||
del_flag = '0' and store_id = #{storeId}
|
||||
<if test="operatorName != null and operatorName != ''">
|
||||
AND operator_name = #{operatorName}
|
||||
</if>
|
||||
<if test="createTime != null">
|
||||
AND DATE(create_time) = DATE(#{createTime})
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
<select id="getEnterCountByDate" resultType="java.lang.Integer">
|
||||
SELECT COUNT(*)
|
||||
FROM mall_scan_record
|
||||
where scan_type = 0 and store_id = #{storeId}
|
||||
AND DATE (create_time) = DATE (#{date})
|
||||
</select>
|
||||
|
||||
|
||||
</mapper>
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
|
||||
<mapper namespace="com.ruoyi.web.mapper.InstallTaskMapper">
|
||||
|
||||
<resultMap id="BaseResultMap" type="com.ruoyi.web.domain.InstallTask">
|
||||
<id property="id" column="id" />
|
||||
<result property="taskNo" column="task_no" />
|
||||
<result property="storeId" column="store_id" />
|
||||
<result property="storeName" column="store_name" />
|
||||
<result property="contactPerson" column="contact_person" />
|
||||
<result property="contactPhone" column="contact_phone" />
|
||||
<result property="deviceType" column="device_type" />
|
||||
<result property="deviceModel" column="device_model" />
|
||||
<result property="installAddress" column="install_address" />
|
||||
<result property="expectedTime" column="expected_time" />
|
||||
<result property="remark" column="remark" />
|
||||
<result property="taskStatus" column="task_status" />
|
||||
<result property="assigneeId" column="assignee_id" />
|
||||
<result property="assigneeName" column="assignee_name" />
|
||||
<result property="assignTime" column="assign_time" />
|
||||
<result property="deviceId" column="device_id" />
|
||||
<result property="deviceCode" column="device_code" />
|
||||
<result property="authCode" column="auth_code" />
|
||||
<result property="installTime" column="install_time" />
|
||||
<result property="installPhoto" column="install_photo" />
|
||||
<result property="completeRemark" column="complete_remark" />
|
||||
<result property="createBy" column="create_by" />
|
||||
<result property="createTime" column="create_time" />
|
||||
<result property="updateBy" column="update_by" />
|
||||
<result property="updateTime" column="update_time" />
|
||||
</resultMap>
|
||||
|
||||
<!-- 新增安装任务 -->
|
||||
<insert id="insertInstallTask" parameterType="com.ruoyi.web.domain.InstallTask" useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO mall_install_task (
|
||||
task_no, store_id, store_name, contact_person, contact_phone,
|
||||
device_type, device_model, install_address, expected_time, remark,
|
||||
task_status, del_flag, create_by, create_time
|
||||
) VALUES (
|
||||
#{taskNo}, #{storeId}, #{storeName}, #{contactPerson}, #{contactPhone},
|
||||
#{deviceType}, #{deviceModel}, #{installAddress}, #{expectedTime}, #{remark},
|
||||
0, '0', #{createBy}, #{createTime}
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 根据ID查询 -->
|
||||
<select id="selectInstallTaskById" parameterType="long" resultMap="BaseResultMap">
|
||||
SELECT * FROM mall_install_task
|
||||
WHERE id = #{id} AND del_flag = '0'
|
||||
</select>
|
||||
|
||||
<!-- 根据任务编号查询 -->
|
||||
<select id="selectInstallTaskByTaskNo" parameterType="string" resultMap="BaseResultMap">
|
||||
SELECT * FROM mall_install_task
|
||||
WHERE task_no = #{taskNo} AND del_flag = '0'
|
||||
</select>
|
||||
|
||||
<!-- 查询安装任务列表 -->
|
||||
<select id="selectInstallTaskList" parameterType="com.ruoyi.web.domain.InstallTask" resultMap="BaseResultMap">
|
||||
SELECT * FROM mall_install_task
|
||||
WHERE del_flag = '0'
|
||||
<if test="taskNo != null and taskNo != ''">
|
||||
AND task_no LIKE CONCAT('%', #{taskNo}, '%')
|
||||
</if>
|
||||
<if test="storeId != null">
|
||||
AND store_id = #{storeId}
|
||||
</if>
|
||||
<if test="storeName != null and storeName != ''">
|
||||
AND store_name LIKE CONCAT('%', #{storeName}, '%')
|
||||
</if>
|
||||
<if test="contactPerson != null and contactPerson != ''">
|
||||
AND contact_person LIKE CONCAT('%', #{contactPerson}, '%')
|
||||
</if>
|
||||
<if test="contactPhone != null and contactPhone != ''">
|
||||
AND contact_phone LIKE CONCAT('%', #{contactPhone}, '%')
|
||||
</if>
|
||||
<if test="deviceType != null">
|
||||
AND device_type = #{deviceType}
|
||||
</if>
|
||||
<if test="taskStatus != null">
|
||||
AND task_status = #{taskStatus}
|
||||
</if>
|
||||
<if test="assigneeId != null">
|
||||
AND assignee_id = #{assigneeId}
|
||||
</if>
|
||||
<if test="assigneeName != null and assigneeName != ''">
|
||||
AND assignee_name LIKE CONCAT('%', #{assigneeName}, '%')
|
||||
</if>
|
||||
<if test="params.beginTime != null and params.beginTime != ''">
|
||||
AND create_time >= #{params.beginTime}
|
||||
</if>
|
||||
<if test="params.endTime != null and params.endTime != ''">
|
||||
AND create_time <= #{params.endTime}
|
||||
</if>
|
||||
ORDER BY create_time DESC
|
||||
</select>
|
||||
|
||||
<!-- 更新安装任务 -->
|
||||
<update id="updateInstallTask" parameterType="com.ruoyi.web.domain.InstallTask">
|
||||
UPDATE mall_install_task
|
||||
<set>
|
||||
<if test="contactPerson != null">contact_person = #{contactPerson},</if>
|
||||
<if test="contactPhone != null">contact_phone = #{contactPhone},</if>
|
||||
<if test="deviceType != null">device_type = #{deviceType},</if>
|
||||
<if test="deviceModel != null">device_model = #{deviceModel},</if>
|
||||
<if test="installAddress != null">install_address = #{installAddress},</if>
|
||||
<if test="expectedTime != null">expected_time = #{expectedTime},</if>
|
||||
<if test="remark != null">remark = #{remark},</if>
|
||||
<if test="taskStatus != null">task_status = #{taskStatus},</if>
|
||||
<if test="assigneeId != null">assignee_id = #{assigneeId},</if>
|
||||
<if test="assigneeName != null">assignee_name = #{assigneeName},</if>
|
||||
<if test="assignTime != null">assign_time = #{assignTime},</if>
|
||||
<if test="deviceId != null">device_id = #{deviceId},</if>
|
||||
<if test="deviceCode != null">device_code = #{deviceCode},</if>
|
||||
<if test="authCode != null">auth_code = #{authCode},</if>
|
||||
<if test="installTime != null">install_time = #{installTime},</if>
|
||||
<if test="installPhoto != null">install_photo = #{installPhoto},</if>
|
||||
<if test="completeRemark != null">complete_remark = #{completeRemark},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE id = #{id} AND del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- 指派任务 -->
|
||||
<update id="assignTask" parameterType="com.ruoyi.web.domain.InstallTask">
|
||||
UPDATE mall_install_task
|
||||
<set>
|
||||
<if test="taskStatus != null">task_status = #{taskStatus},</if>
|
||||
<if test="assigneeId != null">assignee_id = #{assigneeId},</if>
|
||||
<if test="assigneeName != null">assignee_name = #{assigneeName},</if>
|
||||
<if test="assignTime != null">assign_time = #{assignTime},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE id = #{id} AND del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- 完成任务安装 -->
|
||||
<update id="completeTask" parameterType="com.ruoyi.web.domain.InstallTask">
|
||||
UPDATE mall_install_task
|
||||
<set>
|
||||
<if test="taskStatus != null">task_status = #{taskStatus},</if>
|
||||
<if test="deviceId != null">device_id = #{deviceId},</if>
|
||||
<if test="deviceCode != null">device_code = #{deviceCode},</if>
|
||||
<if test="authCode != null">auth_code = #{authCode},</if>
|
||||
<if test="installTime != null">install_time = #{installTime},</if>
|
||||
<if test="installPhoto != null">install_photo = #{installPhoto},</if>
|
||||
<if test="completeRemark != null">complete_remark = #{completeRemark},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE id = #{id} AND del_flag = '0'
|
||||
</update>
|
||||
|
||||
<!-- 查询当天最大任务编号 -->
|
||||
<select id="selectMaxTaskNoByDate" parameterType="string" resultType="string">
|
||||
SELECT MAX(task_no) FROM mall_install_task
|
||||
WHERE task_no LIKE CONCAT(#{prefix}, '%')
|
||||
</select>
|
||||
|
||||
<!-- 逻辑删除单个 -->
|
||||
<update id="deleteInstallTaskById" parameterType="long">
|
||||
UPDATE mall_install_task
|
||||
SET del_flag = '2', update_time = NOW()
|
||||
WHERE id = #{id}
|
||||
</update>
|
||||
|
||||
<!-- 批量逻辑删除 -->
|
||||
<update id="deleteInstallTaskByIds" parameterType="string">
|
||||
UPDATE mall_install_task
|
||||
SET del_flag = '2', update_time = NOW()
|
||||
WHERE id IN
|
||||
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
</update>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -40,41 +40,88 @@
|
|||
<collection property="orderItems" ofType="OrderItem" resultMap="OrderItemResult"/>
|
||||
</resultMap>
|
||||
|
||||
<!-- 插入订单 -->
|
||||
<insert id="insert" parameterType="com.ruoyi.web.domain.Order" useGeneratedKeys="true" keyProperty="id">
|
||||
INSERT INTO mall_order (order_no, store_id, pay_amount, pay_status,
|
||||
create_time, create_by)
|
||||
VALUES (#{orderNo}, #{storeId}, #{payAmount}, #{payStatus},
|
||||
NOW(), #{createBy})
|
||||
</insert>
|
||||
|
||||
<!-- 批量插入订单明细 -->
|
||||
<insert id="batchInsert" parameterType="list">
|
||||
INSERT INTO mall_order_item (
|
||||
order_id,
|
||||
product_id,
|
||||
product_name,
|
||||
product_no,
|
||||
main_image,
|
||||
spec,
|
||||
unit_price,
|
||||
quantity,
|
||||
subtotal,
|
||||
create_time,
|
||||
create_by
|
||||
) VALUES
|
||||
<foreach collection="list" item="item" separator=",">
|
||||
(
|
||||
#{item.orderId},
|
||||
#{item.productId},
|
||||
#{item.productName},
|
||||
#{item.productNo},
|
||||
#{item.mainImage},
|
||||
#{item.spec},
|
||||
#{item.unitPrice},
|
||||
#{item.quantity},
|
||||
#{item.subtotal},
|
||||
NOW(),
|
||||
#{item.createBy}
|
||||
)
|
||||
</foreach>
|
||||
</insert>
|
||||
|
||||
<!-- 根据ID更新订单(选择性更新) -->
|
||||
<update id="updateById" parameterType="com.ruoyi.web.domain.Order">
|
||||
UPDATE mall_order
|
||||
<set>
|
||||
<if test="orderNo != null">order_no = #{orderNo},</if>
|
||||
<if test="payTime != null">pay_time = #{payTime},</if>
|
||||
<if test="payStatus != null">pay_status = #{payStatus},</if>
|
||||
<if test="updateBy != null">update_by = #{updateBy},</if>
|
||||
update_time = NOW()
|
||||
</set>
|
||||
WHERE
|
||||
id = #{id}
|
||||
AND del_flag = '0'
|
||||
</update>
|
||||
|
||||
|
||||
<!-- 查询订单列表(包含商品明细) -->
|
||||
<select id="selectOrderList" parameterType="Order" resultMap="OrderWithItemsResult">
|
||||
<select id="selectOrderList" resultMap="OrderWithItemsAndProductResultMap">
|
||||
SELECT
|
||||
o.id,
|
||||
o.order_no,
|
||||
o.store_id,
|
||||
o.pay_amount,
|
||||
o.pay_time,
|
||||
o.pay_status,
|
||||
o.create_time,
|
||||
o.update_time,
|
||||
o.del_flag,
|
||||
o.pay_time,
|
||||
o.create_by,
|
||||
o.update_by,
|
||||
o.create_time,
|
||||
oi.id as item_id,
|
||||
oi.order_id,
|
||||
oi.product_id,
|
||||
oi.product_name,
|
||||
oi.product_no,
|
||||
oi.main_image,
|
||||
oi.spec,
|
||||
oi.unit_price,
|
||||
oi.quantity,
|
||||
oi.subtotal,
|
||||
oi.del_flag as item_del_flag,
|
||||
oi.create_by as item_create_by,
|
||||
oi.create_time as item_create_time
|
||||
oi.order_id as item_order_id,
|
||||
oi.product_id as item_product_id,
|
||||
oi.quantity as item_quantity,
|
||||
oi.subtotal as item_subtotal,
|
||||
p.product_bar_code as product_bar_code,
|
||||
p.product_name as item_product_name,
|
||||
p.main_image as item_main_image
|
||||
FROM mall_order o
|
||||
LEFT JOIN mall_order_item oi ON o.id = oi.order_id AND oi.del_flag = '0'
|
||||
LEFT JOIN mall_order_item oi ON o.id = oi.order_id
|
||||
LEFT JOIN mall_product_store p ON oi.product_id = p.id
|
||||
<where>
|
||||
o.del_flag = '0'
|
||||
<if test="orderNo != null and orderNo != ''">
|
||||
AND o.order_no like concat('%', #{orderNo}, '%')
|
||||
AND o.order_no = #{orderNo}
|
||||
</if>
|
||||
<if test="storeId != null">
|
||||
AND o.store_id = #{storeId}
|
||||
|
|
@ -82,9 +129,56 @@
|
|||
<if test="payStatus != null">
|
||||
AND o.pay_status = #{payStatus}
|
||||
</if>
|
||||
<if test="productBarCode != null and productBarCode != ''">
|
||||
AND p.product_bar_code = #{productBarCode}
|
||||
</if>
|
||||
</where>
|
||||
ORDER BY o.create_time DESC, oi.create_time ASC
|
||||
ORDER BY o.create_time DESC
|
||||
</select>
|
||||
|
||||
<resultMap id="OrderWithItemsAndProductResultMap" type="com.ruoyi.web.domain.Order">
|
||||
<id property="id" column="id"/>
|
||||
<result property="orderNo" column="order_no"/>
|
||||
<result property="storeId" column="store_id"/>
|
||||
<result property="payAmount" column="pay_amount"/>
|
||||
<result property="payStatus" column="pay_status"/>
|
||||
<result property="payTime" column="pay_time"/>
|
||||
<result property="createBy" column="create_by"/>
|
||||
<result property="createTime" column="create_time"/>
|
||||
<collection property="orderItems" ofType="com.ruoyi.web.domain.OrderItem">
|
||||
<id property="id" column="item_id"/>
|
||||
<result property="orderId" column="item_order_id"/>
|
||||
<result property="productId" column="item_product_id"/>
|
||||
<result property="productBarCode" column="product_bar_code"/>
|
||||
<result property="productName" column="item_product_name"/>
|
||||
<result property="mainImage" column="item_main_image"/>
|
||||
<result property="quantity" column="item_quantity"/>
|
||||
<result property="subtotal" column="item_subtotal"/>
|
||||
</collection>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectForUpdateByOrderNo" parameterType="Order" resultMap="OrderResult">
|
||||
SELECT *
|
||||
FROM mall_order
|
||||
WHERE order_no = #{ordNo}
|
||||
AND del_flag = '0' FOR UPDATE
|
||||
</select>
|
||||
|
||||
<select id="selectByOrderId" parameterType="OrderItem" resultMap="OrderItemResult">
|
||||
SELECT *
|
||||
FROM mall_order_item
|
||||
WHERE order_id = #{orderId}
|
||||
AND del_flag = '0'
|
||||
ORDER BY id ASC
|
||||
</select>
|
||||
|
||||
<select id="getOrderCount" resultType="com.ruoyi.web.vo.OrderCountVO">
|
||||
select
|
||||
count(1) as paidOrderCount,
|
||||
ifnull(sum(pay_amount), 0) as totalPayAmount
|
||||
from mall_order
|
||||
where del_flag = '0'
|
||||
and pay_status = 2 and store_id = #{storeId}
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
@ -73,6 +73,21 @@
|
|||
<if test="status != null and status != '' ">
|
||||
AND status = #{status}
|
||||
</if>
|
||||
<!-- 临期商品筛选(根据传入的临期天数参数) -->
|
||||
<if test="isExpiring != null and isExpiring == true">
|
||||
AND production_date IS NOT NULL
|
||||
AND shelf_life IS NOT NULL
|
||||
AND DATEDIFF(
|
||||
DATE_ADD(production_date, INTERVAL shelf_life DAY),
|
||||
NOW()
|
||||
) BETWEEN 1 AND approaching
|
||||
</if>
|
||||
<!-- 过期商品筛选 -->
|
||||
<if test="isExpired != null and isExpired == true">
|
||||
AND production_date IS NOT NULL
|
||||
AND shelf_life IS NOT NULL
|
||||
AND DATE_ADD(production_date, INTERVAL shelf_life DAY) < NOW()
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
|
||||
|
|
@ -90,6 +105,28 @@
|
|||
where id=#{id} and del_flag = '0'
|
||||
</select>
|
||||
|
||||
<select id="selectBatchForUpdate" parameterType="Product" resultMap="MallProductResult">
|
||||
SELECT * FROM mall_product_store
|
||||
WHERE store_id = #{storeId} and del_flag = 0
|
||||
AND id IN
|
||||
<foreach item="id" collection="ids" open="(" separator="," close=")">
|
||||
#{id}
|
||||
</foreach>
|
||||
FOR UPDATE
|
||||
</select>
|
||||
|
||||
<select id="getByBarcodes" parameterType="com.ruoyi.web.dto.BatchBarcodeQueryDTO" resultMap="MallProductResult">
|
||||
SELECT * FROM mall_product_store
|
||||
WHERE del_flag = '0'
|
||||
<if test="storeId != null">
|
||||
AND store_id = #{storeId}
|
||||
</if>
|
||||
AND product_bar_code IN
|
||||
<foreach item="barcode" collection="barcodes" open="(" separator="," close=")">
|
||||
#{barcode}
|
||||
</foreach>
|
||||
</select>
|
||||
|
||||
|
||||
<insert id="insertProductStore" parameterType="Product">
|
||||
insert into mall_product_store (
|
||||
|
|
@ -134,7 +171,16 @@
|
|||
sysdate()
|
||||
)
|
||||
</insert>
|
||||
|
||||
<!-- 增加库存(用于回滚,带门店ID) -->
|
||||
<update id="addStock">
|
||||
UPDATE mall_product_store
|
||||
SET stock_quantity = stock_quantity + #{quantity},
|
||||
sold_quantity = sold_quantity - #{quantity},
|
||||
update_time = NOW()
|
||||
WHERE store_id = #{storeId}
|
||||
AND id = #{productId}
|
||||
AND del_flag = 0
|
||||
</update>
|
||||
|
||||
<update id="updateProduct">
|
||||
update mall_product_store
|
||||
|
|
@ -188,6 +234,17 @@
|
|||
where product_bar_code = #{productBarCode}
|
||||
</update>
|
||||
|
||||
<update id="reduceStock">
|
||||
UPDATE mall_product_store
|
||||
SET stock_quantity = stock_quantity - #{quantity}, -- 扣减库存
|
||||
sold_quantity = sold_quantity + #{quantity}, -- 增加销量
|
||||
update_time = NOW() -- 更新时间
|
||||
WHERE store_id = #{storeId}
|
||||
AND id = #{productId}
|
||||
AND stock_quantity >= #{quantity} -- 确保库存充足
|
||||
AND del_flag = 0 -- 未删除
|
||||
</update>
|
||||
|
||||
<delete id="deleteConfigById" parameterType="Long">
|
||||
delete
|
||||
from sys_config
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
<result property="storeId" column="store_id"/>
|
||||
<result property="storeCode" column="store_code"/>
|
||||
</collection>
|
||||
|
||||
</resultMap>
|
||||
|
||||
<resultMap id="userStoresMap" type="com.ruoyi.web.vo.StoreVo">
|
||||
|
|
|
|||
Loading…
Reference in New Issue