Merge remote-tracking branch 'origin/master'

# Conflicts:
#	pjcloud-thirdparty/pjcloud-thirdparty-biz/src/main/java/com/bsd/pjcloud/thirdparty/controller/ThirdPartyOAth2Cotroller.java
This commit is contained in:
zjf committed 2026-05-07 22:17:30 +08:00
commit 1666184356
80 files changed
+1669 -455

No files matched your search

+16 -15
View File
@@ -6,15 +6,15 @@ create table act_cancel
notice_id BIGINT(19) not null comment '公告id',
active_org_price INT(10) null comment '活动原价',
active_cancel_price INT(10) not null comment '活动核销价',
start_time DATETIME null comment '开始时间',
expire_time DATETIME not null comment '到期时间',
act_status CHAR(32) not null comment '活动状态(1:正常 2:关闭)',
act_title CHAR(32) not null comment '活动标题',
act_content CHAR(32) null comment '活动内容',
create_by VARCHAR(64) null comment '创建人',
create_time DATETIME null comment '创建时间',
update_by VARCHAR(64) null comment '修改人',
update_time DATETIME null comment '修改时间',
start_time DATETIME null comment '开始时间',
expire_time DATETIME not null comment '到期时间',
act_status CHAR(32) not null comment '活动状态(1:正常 2:关闭)',
act_title CHAR(32) not null comment '活动标题',
act_content CHAR(32) null comment '活动内容',
create_by VARCHAR(64) null comment '创建人',
create_time DATETIME null comment '创建时间',
update_by VARCHAR(64) null comment '修改人',
update_time DATETIME null comment '修改时间',
del_flag CHAR default '0' null comment '删除标记',
tenant_id BIGINT(19) null comment '租户ID'
);
@@ -47,6 +47,7 @@ DROP TABLE IF EXISTS act_coupon;
CREATE TABLE act_coupon
(
id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '优惠券ID',
parent_act_id BIGINT(20) DEFAULT NULL COMMENT '父活动id',
name VARCHAR(128) NOT NULL COMMENT '优惠券名称',
type VARCHAR(64) NOT NULL COMMENT '优惠券类型 (1:满减券 2:折扣券 3:立减券 4:兑换券)',
face_value INT DEFAULT 0 COMMENT '面额/折扣 (满减/立减单位:分, 折扣单位:百分比)',
@@ -71,8 +72,8 @@ CREATE TABLE act_coupon
del_flag char(1) COLLATE utf8mb4_general_ci DEFAULT '0' COMMENT '删除标记',
tenant_id bigint DEFAULT NULL COMMENT '租户ID',
PRIMARY KEY (id),
KEY idx_status (status),
KEY idx_valid_end_time (valid_end_time)
KEY idx_status (status),
KEY idx_valid_end_time (valid_end_time)
) ENGINE = InnoDB COMMENT ='优惠券活动';
DROP TABLE IF EXISTS act_coupon_record;
@@ -109,8 +110,8 @@ CREATE TABLE act_coupon_record
PRIMARY KEY (id),
UNIQUE KEY uk_coupon_code (coupon_code),
KEY idx_user_id (user_id),
KEY idx_coupon_id (coupon_id),
KEY idx_end_time (end_time),
KEY idx_status (status)
KEY idx_user_id (user_id),
KEY idx_coupon_id (coupon_id),
KEY idx_end_time (end_time),
KEY idx_status (status)
) ENGINE = InnoDB COMMENT ='优惠券领取记录表';
@@ -110,6 +110,12 @@ public class AppUser extends Model<AppUser> {
@Schema(description = "第三方登录扩展字段")
private String loginExtJson;
/**
* openId
*/
@Schema(description = "openId")
private String openid;
/**
* 创建人
*/
@@ -164,10 +170,4 @@ public class AppUser extends Model<AppUser> {
@Schema(description = "锁定标记")
private String lockFlag;
/**
* openId
*/
@Schema(description = "openId")
private String openid;
}
@@ -10,6 +10,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import com.bsd.pjcloud.common.core.util.TenantTable;
import java.time.LocalDateTime;
/**
@@ -26,182 +27,188 @@ import java.time.LocalDateTime;
public class CanteenSpuEntity extends Model<CanteenSpuEntity> {
/**
* 商品 SPU 编号,自增
*/
/**
* 商品 SPU 编号,自增
*/
@TableId(type = IdType.ASSIGN_ID)
@Schema(description="商品 SPU 编号,自增")
@Schema(description = "商品 SPU 编号,自增")
private Long id;
/**
* 商品名称
*/
@Schema(description="商品名称")
/**
* 商品名称
*/
@Schema(description = "商品名称")
private String name;
/**
* 商品简介
*/
@Schema(description="商品简介")
/**
* 商品简介
*/
@Schema(description = "商品简介")
private String introduction;
/**
* 商品详情
*/
@Schema(description="商品详情")
/**
* 商品详情
*/
@Schema(description = "商品详情")
private String description;
/**
* 商品分类编号
*/
@Schema(description="商品分类编号")
/**
* 商品分类编号
*/
@Schema(description = "商品分类编号")
private Long categoryId;
/**
* 商品封面图
*/
@Schema(description="商品封面图")
/**
* 商品封面图
*/
@Schema(description = "商品封面图")
private String picUrl;
/**
* 商品轮播图地址
数组,以逗号分隔
最多上传15张
*/
@Schema(description="商品轮播图")
/**
* 商品轮播图地址
* 数组,以逗号分隔
* 最多上传15张
*/
@Schema(description = "商品轮播图")
private String sliderPicUrls;
/**
* 排序字段
*/
@Schema(description="排序字段")
/**
* 排序字段
*/
@Schema(description = "排序字段")
private Integer sort;
/**
* 商品状态: 1 上架(开启) 0 下架(禁用) -1 回收
*/
@Schema(description="商品状态: 1 上架(开启) 0 下架(禁用) -1 回收")
/**
* 商品状态: 1 上架(开启) 0 下架(禁用) -1 回收
*/
@Schema(description = "商品状态: 1 上架(开启) 0 下架(禁用) -1 回收")
private Integer status;
/**
* 规格类型:0 单规格 1 多规格
*/
@Schema(description="规格类型:0 单规格 1 多规格")
/**
* 规格类型:0 单规格 1 多规格
*/
@Schema(description = "规格类型:0 单规格 1 多规格")
private Boolean specType;
/**
* 商品价格,单位使用:分
*/
@Schema(description="商品价格,单位使用:分")
/**
* 商品价格,单位使用:分
*/
@Schema(description = "商品价格,单位使用:分")
// @JsonSerialize(using = MoneySerializer.class) // 分 -> 元
// @JsonDeserialize(using = MoneyDeserializer.class) // 元 -> 分
private Integer price;
/**
* 市场价,单位使用:分
*/
@Schema(description="市场价,单位使用:分")
/**
* 市场价,单位使用:分
*/
@Schema(description = "市场价,单位使用:分")
// @JsonSerialize(using = MoneySerializer.class) // 分 -> 元
// @JsonDeserialize(using = MoneyDeserializer.class) // 元 -> 分
private Integer marketPrice;
/**
* 成本价,单位: 分
*/
@Schema(description="成本价,单位: 分")
/**
* 成本价,单位: 分
*/
@Schema(description = "成本价,单位: 分")
// @JsonSerialize(using = MoneySerializer.class) // 分 -> 元
// @JsonDeserialize(using = MoneyDeserializer.class) // 元 -> 分
private Integer costPrice;
/**
* 库存
*/
@Schema(description="库存")
/**
* 库存
*/
@Schema(description = "库存")
private Integer stock;
/**
* 赠送积分
*/
@Schema(description="赠送积分")
/**
* 赠送积分
*/
@Schema(description = "赠送积分")
private Integer giveIntegral;
/**
* 商品销量
*/
@Schema(description="商品销量")
/**
* 商品销量
*/
@Schema(description = "商品销量")
private Integer salesCount;
/**
* 虚拟销量
*/
@Schema(description="虚拟销量")
/**
* 虚拟销量
*/
@Schema(description = "虚拟销量")
private Integer virtualSalesCount;
/**
* 商品点击量
*/
@Schema(description="商品点击量")
/**
* 商品点击量
*/
@Schema(description = "商品点击量")
private Integer browseCount;
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "是否删除 -1:已删除 0:正常")
private String delFlag;
@TableLogic
@TableField(fill = FieldFill.INSERT)
@Schema(description = "是否删除 -1:已删除 0:正常")
private String delFlag;
/**
* 系统类型 2每日 3在线订餐
*/
@Schema(description="系统类型 2每日 3在线订餐")
/**
* 系统类型 2每日 3在线订餐
*/
@Schema(description = "系统类型 2每日 3在线订餐")
private Long systemType;
/**
* 商户编号
*/
@Schema(description="商户编号")
/**
* 商户编号
*/
@Schema(description = "商户编号")
private Long merchantId;
/**
* 部门id
*/
@Schema(description="部门id")
/**
* 部门id
*/
@Schema(description = "部门id")
private Long deptId;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description="创建人")
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建人")
private String createBy;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description="创建时间")
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
@Schema(description = "创建时间")
private LocalDateTime createTime;
/**
* 更新人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description="更新人")
/**
* 更新人
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新人")
private String updateBy;
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description="更新时间")
/**
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
@Schema(description = "更新时间")
private LocalDateTime updateTime;
/**
* 租户编号
*/
@Schema(description="租户编号")
/**
* 租户编号
*/
@Schema(description = "租户编号")
private Long tenantId;
@Schema(description="是否显示")
private String isShow;
@Schema(description = "是否显示")
private String isShow;
@Schema(description = "是否涨价")
private String isPriceIncreased;
@Schema(description = "大于等于的价格")
@TableField(exist = false)
private Long gePrice;
}
@@ -1,5 +1,6 @@
package com.bsd.pjcloud.canteen.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@@ -44,4 +45,9 @@ public class ADCanteenDailyMenuListVO {
* 排序
*/
private Integer sort;
/**
* 是否涨价
*/
private String isPriceIncreased;
}
@@ -15,71 +15,72 @@ public class ADCanteenSpuListVO {
/**
* 商品 SPU 编号,自增
*/
@Schema(description="商品 SPU 编号,自增")
@Schema(description = "商品 SPU 编号,自增")
private Long id;
/**
* 商品名称
*/
@Schema(description="商品名称")
@Schema(description = "商品名称")
private String name;
/**
* 商品分类编号
*/
@Schema(description="商品分类编号")
@Schema(description = "商品分类编号")
private Long categoryId;
/**
* 商品封面图
*/
@Schema(description="商品封面图")
@Schema(description = "商品封面图")
private String picUrl;
/**
* 商品价格,单位使用:分
*/
@Schema(description="商品价格,单位使用:分")
@Schema(description = "商品价格,单位使用:分")
private Integer price;
/**
* 市场价,单位使用:分
*/
@Schema(description="市场价,单位使用:分")
@Schema(description = "市场价,单位使用:分")
private Integer marketPrice;
/**
* 库存
*/
@Schema(description="库存")
@Schema(description = "库存")
private Integer stock;
/**
* 商品销量
*/
@Schema(description="商品销量")
@Schema(description = "商品销量")
private Integer salesCount;
/**
* 商户编号
*/
@Schema(description="商户编号")
@Schema(description = "商户编号")
private Long merchantId;
/**
* 部门id
*/
@Schema(description="部门id")
@Schema(description = "部门id")
private Long deptId;
@Schema(description = "是否涨价")
private String isPriceIncreased;
@Schema(description="餐段类型")
@Schema(description = "餐段类型")
private String categoryType;
}
@@ -1,5 +1,6 @@
package com.bsd.pjcloud.canteen.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -46,6 +47,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
private final CanteenCategoryMapper canteenCategoryMapper;
private final CanteenStockMapper canteenStockMapper;
private final CanteenOnlineSpuMapper canteenOnlineSpuMapper;
@Override
public Object selectFoodList(LambdaQueryWrapper<CanteenSpuEntity> wrapper) {
return baseMapper.selectListByScope(wrapper, DataScope.of());
@@ -59,11 +61,11 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
// 查询当前库存
CanteenSpuEntity spu = baseMapper.selectById(item.getId());
if (spu == null) {
R.failed(spu.getName()+"商品不存在");
R.failed(spu.getName() + "商品不存在");
}
// 检查库存是否足够
if (item.getIncrCount() < 0 && Math.abs(item.getIncrCount()) > spu.getStock()) {
R.failed(spu.getName()+"库存不足,无法完成操作");
R.failed(spu.getName() + "库存不足,无法完成操作");
}
int i = baseMapper.updateStock(item.getId(), item.getIncrCount());
// if (item.getIncrCount() <= 0) {
@@ -89,7 +91,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
Map<Long, List<CanteenCategoryEntity>> listMap = canteenCategoryEntities.stream().collect(Collectors.groupingBy(CanteenCategoryEntity::getId));
bean.forEach(item -> {
if (null!=listMap.get(item.getCategoryId()).get(0).getCategoryType()){
if (null != listMap.get(item.getCategoryId()).get(0).getCategoryType()) {
item.setCategoryType(listMap.get(item.getCategoryId()).get(0).getCategoryType());
}
});
@@ -127,7 +129,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
LambdaQueryWrapper<CanteenCategoryEntity> canteenCategoryEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
canteenCategoryEntityLambdaQueryWrapper.like(CanteenCategoryEntity::getCategoryType, canteenDailySpuDTO.getHourType());
List<CanteenCategoryEntity> canteenCategoryEntities = canteenCategoryMapper.selectList(canteenCategoryEntityLambdaQueryWrapper);
List<Long> caIds = canteenCategoryEntities.stream().map(CanteenCategoryEntity::getId).collect(Collectors.toList());
List<Long> caIds = canteenCategoryEntities.stream().map(CanteenCategoryEntity::getId).collect(Collectors.toList());
wrapper.in(!CollectionUtils.isEmpty(caIds), CanteenSpuEntity::getCategoryId, caIds);
// wrapper.orderByAsc(CanteenSpuEntity::getSort);
@@ -164,32 +166,32 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
// 查询SPU分页数据
Page<CanteenSpuEntity> pageData = baseMapper.selectPageByScope(page, wrapper, DataScope.of());
List<CanteenSpuEntity> records = pageData.getRecords();
// 构建返回的VO列表
List<CanteenSpuStockVO> resultList = new ArrayList<>();
if (!CollectionUtils.isEmpty(records)) {
// 获取所有SPU ID
List<Long> spuIds = records.stream()
.map(CanteenSpuEntity::getId)
.collect(Collectors.toList());
// 查询对应的库存信息
LambdaQueryWrapper<CanteenStockEntity> stockWrapper = Wrappers.lambdaQuery();
stockWrapper.in(CanteenStockEntity::getItemId, spuIds);
stockWrapper.eq(CanteenStockEntity::getItemType, "2"); // 假设"成品"表示菜品的类型
List<CanteenStockEntity> stockList = canteenStockMapper.selectList(stockWrapper);
// 将库存信息映射到SPU ID
Map<Long, CanteenStockEntity> stockMap = stockList.stream()
.collect(Collectors.toMap(CanteenStockEntity::getItemId, stock -> stock, (v1, v2) -> v1));
// 构建返回结果
for (CanteenSpuEntity spu : records) {
CanteenSpuStockVO vo = new CanteenSpuStockVO();
vo.setName(spu.getName());
vo.setPicUrl(spu.getPicUrl());
// 设置库存信息
CanteenStockEntity stock = stockMap.get(spu.getId());
if (stock != null) {
@@ -205,11 +207,11 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
vo.setInventoryUpperLimit(0);
vo.setInventoryLowerLimit(0);
}
resultList.add(vo);
}
}
// 构建分页返回对象
Page<CanteenSpuStockVO> resultPage = new Page<>();
resultPage.setRecords(resultList);
@@ -217,7 +219,7 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
resultPage.setSize(pageData.getSize());
resultPage.setCurrent(pageData.getCurrent());
resultPage.setPages(pageData.getPages());
return resultPage;
}
@@ -226,6 +228,10 @@ public class CanteenSpuServiceImpl extends ServiceImpl<CanteenSpuMapper, Canteen
LambdaQueryWrapper<CanteenSpuEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.like(StrUtil.isNotBlank(canteenSpu.getName()), CanteenSpuEntity::getName, canteenSpu.getName());
wrapper.eq(Objects.nonNull(canteenSpu.getCategoryId()), CanteenSpuEntity::getCategoryId, canteenSpu.getCategoryId());
wrapper.eq(Objects.nonNull(canteenSpu.getIsPriceIncreased()), CanteenSpuEntity::getIsPriceIncreased, canteenSpu.getIsPriceIncreased());
if (ObjectUtil.isNotEmpty(canteenSpu.getGePrice()) && canteenSpu.getGePrice() > 0) {
wrapper.ge(CanteenSpuEntity::getPrice, canteenSpu.getGePrice() * 100);
}
wrapper.orderByAsc(CanteenSpuEntity::getSort).orderByDesc(CanteenSpuEntity::getId);
LambdaQueryWrapper<CanteenOnlineSpuEntity> canteenOnlineSpuEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
@@ -28,6 +28,7 @@
<result property="stock" column="stock"/>
<result property="sort" column="sort"/>
<result property="hourType" column="hour_type"/>
<result property="isPriceIncreased" column="is_price_increased"/>
</resultMap>
<select id="selectTimeFood" resultType="com.bsd.pjcloud.canteen.vo.DailyMenuVO">
SELECT
@@ -63,6 +64,7 @@
sp.stock,
sp.category_id,
sp.sort,
sp.is_price_increased,
t.dept_id,
t.create_by
FROM canteen_daily_menu t
@@ -98,6 +98,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.bsd</groupId>
<artifactId>pjcloud-common-idempotent</artifactId>
</dependency>
</dependencies>
<profiles>
<profile>
@@ -1,6 +1,7 @@
package com.bsd.pjcloud.flow.task.controller;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
import com.bsd.pjcloud.flow.task.dto.BatchCompleteTaskDto;
import com.bsd.pjcloud.flow.task.dto.TaskParamDto;
import com.bsd.pjcloud.flow.task.service.ITaskService;
@@ -51,8 +52,8 @@ public class TaskController {
*/
@SneakyThrows
@PostMapping("completeTask")
@Idempotent(expireTime = 3, key = "'/task/completeTask/' + #completeParamDto.getTaskId()", info = "正在操作中,请勿重复操作")
public R completeTask(@RequestBody TaskParamDto completeParamDto) {
return taskService.completeTask(completeParamDto);
}
@@ -23,6 +23,7 @@ public enum HardTypeEnum {
OTHER("6", "其他支付方式(第三方接口对接)"),
MERCHANT_ACT("7", "商户活动"),
FARMER("8", "共富商城(助农)"),
YYX("10", "4G云音响"),
;
private final String type;
private final String description;
@@ -38,4 +38,7 @@ public class OfflineCar {
@Schema(description = "单商品总价(分)")
private Long totalPrice;
@Schema(description = "是否额外收费")
private Boolean isPriceIncreased;
}
@@ -73,6 +73,9 @@ public class HardCreateOrderDto {
@Schema(description = "钱包id")
private Long walletId;
@Schema(description = "额外价格(分)")
private Integer extraPrice;
@Schema(description = "离线支付是否成功")
private Boolean offlinePaySuccess;
}
@@ -24,4 +24,7 @@ public class HardCreateCarDto {
// TODO 超市特有
@Schema(description = "重量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20798")
private BigDecimal productWeight;
@Schema(description = "是否额外收费")
private Boolean isPriceIncreased;
}
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.hard.api.dto.order;
import com.bsd.pjcloud.admin.api.vo.base.BaseIsIncludeTreeInterface;
import com.bsd.pjcloud.common.core.constant.enums.DateRangeTypeEnum;
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
@@ -84,4 +85,9 @@ public class HardOrderDto extends HardOrderEntity implements BaseIsIncludeTreeIn
*/
private Boolean productDescIsNotEmpty = false;
/**
* 是否为优惠商品
*/
private String isDiscountsProduct;
}
@@ -83,6 +83,12 @@ public class HardCarEntity extends Model<HardCarEntity> {
@Schema(description = "重量")
private BigDecimal productWeight;
/**
* 是否为优惠商品
*/
@Schema(description = "是否为优惠商品")
private String isDiscountsProduct;
/**
* 总价(分)
*/
@@ -0,0 +1,22 @@
package com.bsd.pjcloud.hard.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "yyx")
@Data
public class YyxConf {
private String url;
/**
* 模板
* ${price}-价格
* ${phone-last}-手机尾号
* 浦江智慧后勤,收款${price}元
*/
private String template;
}
@@ -21,6 +21,7 @@ import com.bsd.pjcloud.hard.api.dto.order.app.HardAppCreateFarmerOrderDto;
import com.bsd.pjcloud.hard.api.dto.order.app.HardAppCreateOrderDto;
import com.bsd.pjcloud.hard.api.entity.order.HardCarEntity;
import com.bsd.pjcloud.hard.api.entity.order.HardOrderEntity;
import com.bsd.pjcloud.hard.api.vo.order.HardCarVo;
import com.bsd.pjcloud.hard.api.vo.order.HardCreateHardQrCodeScanOrderVo;
import com.bsd.pjcloud.hard.api.vo.order.HardOrderVo;
import com.bsd.pjcloud.thirdparty.dto.ks.KsRecognitionCallbackDto;
@@ -121,7 +122,7 @@ public interface HardOrderService extends IService<HardOrderEntity> {
Integer computeExtraPrice(Integer price, MchManageVo mch, SysUser sysUser);
Integer computeExtraPriceV2(Integer totalPrice, Long mchId, String hourType, Long userId, Long userDeptId);
Integer computeExtraPriceV2(List<HardCarVo> carList, Long mchId, String hourType, Long userId, Long userDeptId);
/**
* 核销二维码
@@ -0,0 +1,11 @@
package com.bsd.pjcloud.hard.service;
/**
* @author liusiyang_dxm
* @since 2026/4/28
*/
public interface YyxService {
void send(Long mchId, Integer payPrice, String phone);
}
@@ -42,21 +42,20 @@ import com.bsd.pjcloud.hard.api.vo.wechat.WechantRefunCallbackVo;
import com.bsd.pjcloud.hard.api.vo.wechat.WechatRefundVo;
import com.bsd.pjcloud.hard.mapper.order.HardAfterSaleLogMapper;
import com.bsd.pjcloud.hard.mapper.order.HardAfterSaleMapper;
import com.bsd.pjcloud.hard.mapper.order.HardCarMapper;
import com.bsd.pjcloud.hard.mapper.order.HardOrderMapper;
import com.bsd.pjcloud.hard.service.*;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundDto;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletRechargeService;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletSettingService;
import com.bsd.pjcloud.wallet.api.vo.WalletXfjDiscounts;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.parameters.P;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@@ -417,9 +416,11 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
throw new RuntimeException("退款总金额大于支付总金额");
}
// 查询设备信息
HardManagerVo hardDetail = hardManagerService.getByHardId(order.getHardId());
if (ObjectUtil.isNotEmpty(hardDetail)) {
orderVo.setHardType(hardDetail.getType());
if (ObjectUtil.isNotEmpty(order.getHardId())) {
HardManagerVo hardDetail = hardManagerService.getByHardId(order.getHardId());
if (ObjectUtil.isNotEmpty(hardDetail)) {
orderVo.setHardType(hardDetail.getType());
}
}
return orderVo;
}
@@ -573,9 +574,30 @@ public class HardAfterSaleServiceImpl extends ServiceImpl<HardAfterSaleMapper, H
if (ObjectUtil.equals(order.getHardType(), HardTypeEnum.CANTEEN.getType()) && ObjectUtil.isNotEmpty(order.getExtraPrice()) && order.getExtraPrice() > 0) {
int noRefundPrice = order.getPayPrice() - order.getExtraPrice() - refundPrice;
if (noRefundPrice >= 0) {
Integer noRefundExtraPrice = hardOrderService.computeExtraPriceV2(noRefundPrice, order.getMerchantId(), order.getHourType(), order.getUserId(), order.getUserDeptId());
if (noRefundExtraPrice == 0) {
refundPrice += order.getExtraPrice();
// 查询购物车
List<HardCarVo> carList = hardCarService.getByOrderId(order.getId());
List<HardCarVo> extraCarList = new ArrayList<>();
for (HardAfterSaleCarEntity product : dto.getCarList()) {
HardCarVo targetCarProduct = carList.stream().filter(item -> ObjectUtil.equals(item.getProductId(), product.getProductId())).findFirst().orElse(null);
if (ObjectUtil.isEmpty(targetCarProduct)) continue;
if (product.getProductCount() <= 0) continue;
HardCarVo entity = BeanUtils.toBean(targetCarProduct, HardCarVo.class);
entity.setProductCount(product.getProductCount());
if (ObjectUtil.isNotEmpty(entity.getProductCount()) && !ObjectUtil.equals(entity.getProductCount(), 0)) {
// 按数量
int itemTotalPrice = entity.getProductCount() * entity.getProductPrice();
entity.setTotalPrice((long) itemTotalPrice);
} else if (ObjectUtil.isNotEmpty(entity.getProductWeight()) && entity.getProductWeight().compareTo(BigDecimal.valueOf(0)) != 0) {
// 按重量
// 向上取整
int itemTotalPrice = entity.getProductWeight().multiply(BigDecimal.valueOf(entity.getProductPrice())).setScale(0, RoundingMode.CEILING).intValue();
entity.setTotalPrice((long) itemTotalPrice);
}
extraCarList.add(entity);
}
Integer noRefundExtraPrice = hardOrderService.computeExtraPriceV2(extraCarList, order.getMerchantId(), order.getHourType(), order.getUserId(), order.getUserDeptId());
if (noRefundExtraPrice > 0) {
refundPrice += noRefundExtraPrice;
}
}
}
@@ -10,6 +10,7 @@ import com.bsd.pjcloud.canteen.api.feign.RemoteCanteenSpuService;
import com.bsd.pjcloud.canteen.api.feign.RemoteOnlineCanteenSpuService;
import com.bsd.pjcloud.canteen.entity.CanteenSpuEntity;
import com.bsd.pjcloud.canteen.vo.app.CanteenOnlineSpuAppVO;
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
import com.bsd.pjcloud.common.core.util.BeanUtils;
import com.bsd.pjcloud.hard.api.constant.HardProductNatureEnum;
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
@@ -319,6 +320,8 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
HardCarEntity item = HardCarEntity.builder().parentCarId(parentCarId).build();
supList.stream().filter(i -> ObjectUtil.equals(i.getId(), v.getProductId())).findFirst().ifPresent(spu -> {
int totalPrice = v.getProductCount() * spu.getPrice();
String isPriceIncreased = ObjectUtil.isNotEmpty(v.getIsPriceIncreased()) && v.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
item.setProductId(spu.getId());
item.setProductName(spu.getName());
item.setProductPrice(spu.getPrice());
@@ -329,6 +332,7 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
}
item.setProductCount(v.getProductCount());
item.setProductWeight(new BigDecimal(0));
item.setIsDiscountsProduct(isPriceIncreased);
carObjectList.add(item);
});
}
@@ -340,6 +344,8 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
HardCarEntity item = HardCarEntity.builder().parentCarId(parentCarId).build();
supList.stream().filter(i -> ObjectUtil.equals(i.getId(), v.getProductId())).findFirst().ifPresent(spu -> {
int totalPrice = v.getProductCount() * spu.getPrice();
String isPriceIncreased = ObjectUtil.isNotEmpty(v.getIsPriceIncreased()) && v.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
item.setProductId(spu.getId());
item.setProductName(spu.getName());
item.setProductPrice(spu.getPrice());
@@ -350,6 +356,7 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
}
item.setProductCount(v.getProductCount());
item.setProductWeight(new BigDecimal(0));
item.setIsDiscountsProduct(isPriceIncreased);
carObjectList.add(item);
});
}
@@ -370,6 +377,8 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
Integer price = getSupermarket(sku);
int totalPrice = 0;
totalPrice = v.getProductCount() * price;
String isPriceIncreased = ObjectUtil.isNotEmpty(v.getIsPriceIncreased()) && v.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
// 找到spu
ShopSpuDetailAppVo spu = spuList.stream().filter(i -> ObjectUtil.equals(i.getId(), sku.getSpuId())).findFirst().orElse(null);
item.setProductId(sku.getId());
@@ -378,6 +387,7 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
item.setProductNature(HardProductNatureEnum.DEFAULT.getType());
item.setProductCount(v.getProductCount());
item.setProductWeight(BigDecimal.ZERO);
item.setIsDiscountsProduct(isPriceIncreased);
if (ObjectUtil.isNotEmpty(spu)) {
item.setProductName(spu.getProductName());
item.setProductPic(spu.getPicUrl());
@@ -401,6 +411,8 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
// 按重量
totalPrice = v.getProductWeight().multiply(BigDecimal.valueOf(price)).setScale(0, RoundingMode.CEILING).intValue();
}
String isPriceIncreased = ObjectUtil.isNotEmpty(v.getIsPriceIncreased()) && v.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
item.setProductId(spu.getId());
item.setProductName(spu.getProductName());
item.setProductPrice(price);
@@ -411,6 +423,7 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
}
item.setProductCount(v.getProductCount());
item.setProductWeight(v.getProductWeight());
item.setIsDiscountsProduct(isPriceIncreased);
carObjectList.add(item);
});
}
@@ -435,6 +448,8 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
// 按重量
totalPrice = v.getProductWeight().multiply(BigDecimal.valueOf(price)).setScale(0, RoundingMode.CEILING).intValue();
}
String isPriceIncreased = ObjectUtil.isNotEmpty(v.getIsPriceIncreased()) && v.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
item.setProductId(spu.getId());
item.setProductName(spu.getName());
item.setProductPrice(price);
@@ -445,6 +460,7 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
}
item.setProductCount(v.getProductCount());
item.setProductWeight(v.getProductWeight());
item.setIsDiscountsProduct(isPriceIncreased);
carObjectList.add(item);
});
}
@@ -478,6 +494,8 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
// 按重量
totalPrice = v.getProductWeight().multiply(BigDecimal.valueOf(price)).setScale(0, RoundingMode.CEILING).intValue();
}
String isPriceIncreased = ObjectUtil.isNotEmpty(v.getIsPriceIncreased()) && v.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
item.setProductId(sku.getId());
item.setProductName(spu.getTitle());
item.setProductPrice(price);
@@ -490,6 +508,7 @@ public class HardCarServiceImpl extends ServiceImpl<HardCarMapper, HardCarEntity
}
item.setProductCount(v.getProductCount());
item.setProductWeight(v.getProductWeight());
item.setIsDiscountsProduct(isPriceIncreased);
carObjectList.add(item);
}
@@ -161,6 +161,7 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
private final RemoteMallProductService remoteMallProductService;
private final HardManagerMapper hardManagerMapper;
private final YyxService yyxService;
private final StringRedisTemplate redisTemplate;
private final HardThirdMiOrderMapper hardThirdMiOrderMapper;
@@ -302,8 +303,8 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
}
int totalPrice = 0, extraPrice = 0, payPrice = 0;
totalPrice = hardCarService.computerCarTotalPrice(dto.getSysType(), dto.getOrderType(), dto.getList());
if (isExtraPrice) {
extraPrice = computeExtraPriceV2(totalPrice, mch.getId(), String.valueOf(dto.getHourType()), sysUser.getUserId(), sysUser.getDeptId());
if (isExtraPrice && ObjectUtil.isNotEmpty(dto.getExtraPrice())) {
extraPrice = dto.getExtraPrice();
}
payPrice = totalPrice + extraPrice;
if (isWalletPay) {
@@ -782,6 +783,9 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
getSelf().generateCode(orderObj);
// 扣减库存
// getSelf().fastenerInventory(orderObj, YesNoEnum.YES);
// 云音箱播报
CompletableFuture.runAsync(() -> yyxService.send(orderObj.getMerchantId(), orderObj.getPayPrice(), userDetail.getSysUser().getPhone()));
return orderObj.getId();
}
@@ -1137,14 +1141,34 @@ public class HardOrderServiceImpl extends ServiceImpl<HardOrderMapper, HardOrder
@Override
public Integer computeExtraPriceV2(Integer totalPrice, Long mchId, String hourType, Long userId, Long userDeptId) {
public Integer computeExtraPriceV2(List<HardCarVo> carList, Long mchId, String hourType, Long userId, Long userDeptId) {
if (ObjectUtil.isEmpty(carList)) {
return 0;
}
List<WalletXfjDiscounts> discountsList = walletSettingService.remoteGetDiscountsList(String.valueOf(mchId), userId, userDeptId).getData();
if (ObjectUtil.isEmpty(discountsList)) {
return 0;
}
return discountsList.stream()
.filter(item -> ObjectUtil.equals(item.getHourType(), String.valueOf(hourType)))
.map(item -> item.computedDiscountsExtraPrice(totalPrice))
.map(item -> {
boolean specialDishEnable = ObjectUtil.isNotEmpty(item.getSpecialDishEnable()) && ObjectUtil.equals(item.getSpecialDishEnable(), YesNoEnum.YES.getCode());
long totalPrice = 0L;
// 特色菜不额外收费
if (!specialDishEnable) {
totalPrice = carList.stream()
.filter(v -> ObjectUtil.isNotEmpty(v.getIsDiscountsProduct()) && ObjectUtil.equals(v.getIsDiscountsProduct(), YesNoEnum.YES.getCode()))
.map(HardCarEntity::getTotalPrice)
.reduce(Long::sum).orElse(0L);
}
// 特色菜额外收费
else if (specialDishEnable) {
totalPrice = carList.stream()
.map(HardCarEntity::getTotalPrice)
.reduce(Long::sum).orElse(0L);
}
return item.computedDiscountsExtraPrice((int) totalPrice);
})
.reduce(Integer::sum).orElse(0);
}
@@ -212,6 +212,8 @@ public class HardOrderSyncServiceImpl implements HardOrderSyncService {
}
public HardCarEntity parseCar(OfflineCar order) {
String isPriceIncreased = ObjectUtil.isNotEmpty(order.getIsPriceIncreased()) && order.getIsPriceIncreased() ? YesNoEnum.YES.getCode() : YesNoEnum.NO.getCode();
HardCarEntity res = new HardCarEntity();
res.setParentCarId(null);
res.setProductName(order.getProductName());
@@ -221,6 +223,7 @@ public class HardOrderSyncServiceImpl implements HardOrderSyncService {
res.setProductCount(order.getProductNumber());
res.setProductWeight(order.getProductWidget());
res.setTotalPrice(order.getTotalPrice());
res.setIsDiscountsProduct(isPriceIncreased);
return res;
}
@@ -0,0 +1,117 @@
package com.bsd.pjcloud.hard.service.impl.yyx;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.bsd.pjcloud.hard.api.constant.HardTypeEnum;
import com.bsd.pjcloud.hard.api.entity.manager.HardManagerEntity;
import com.bsd.pjcloud.hard.config.YyxConf;
import com.bsd.pjcloud.hard.mapper.manager.HardManagerMapper;
import com.bsd.pjcloud.hard.service.YyxService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
@Validated
@Slf4j
@RequiredArgsConstructor
@Service
public class YyxServiceImpl implements YyxService {
private final String TAG = "4G云音箱";
private final YyxConf yyxConf;
private final HardManagerMapper hardManagerMapper;
@Override
public void send(Long mchId, Integer payPrice, String phone) {
try {
if (!ObjectUtil.isAllNotEmpty(mchId, payPrice, phone)) {
return;
}
// 查询商户绑定的云音箱设备
List<HardManagerEntity> yyxList = hardManagerMapper.selectList(new LambdaQueryWrapper<HardManagerEntity>().eq(HardManagerEntity::getMerchantId, mchId)
.eq(HardManagerEntity::getType, HardTypeEnum.YYX.getType())
);
if (ObjectUtil.isEmpty(yyxList)) {
return;
}
String yuanPrice = fenToYuan(payPrice);
String lastPhone = phoneToLastPhone(phone);
String template = yyxConf.getTemplate();
// 把`template`中的`${price}`替换为`yuanPrice`
template = template.replace("${price}", yuanPrice);
// 把`template`中的`${phone}`替换为`lastPhone`
template = template.replace("${phone-last}", lastPhone);
String finalTemplate = template;
yyxList.forEach(v -> httpSend(v.getUniqueCode(), finalTemplate));
} catch (Exception e) {
log.error("|{}|发送失败,异常:{}", TAG, e.getMessage());
}
}
private boolean httpSend(String deviceId, String msg) {
String msgid = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) + RandomUtil.randomString(8);
try {
HashMap<String, Object> data = new HashMap<>();
data.put("cmd", "voice");
data.put("msg", msg);
data.put("msgid", msgid);
HashMap<String, Object> body = new HashMap<>();
body.put("sbx_id", deviceId);
body.put("agent_id", JSONUtil.toJsonStr(data));
String res = HttpUtil.post(yyxConf.getUrl(), JSONUtil.toJsonStr(body));
if (ObjectUtil.isEmpty(res)) {
log.error("|{}|发送失败,返回结果为空", TAG);
return false;
}
JSON resJson = JSONUtil.parse(res);
String requestId = resJson.getByPath("RequestId", String.class);
if (ObjectUtil.isEmpty(requestId)) {
log.error("|{}|发送失败,返回结果错误res:|{}|", res, TAG);
}
return true;
} catch (Exception e) {
e.printStackTrace();
log.error("|{}|发送失败,异常:{}", TAG, e.getMessage());
return false;
}
}
private String phoneToLastPhone(String phone) {
if (ObjectUtil.isEmpty(phone)) {
return "";
}
return phone.substring(phone.length() - 4);
}
private String fenToYuan(Integer payPrice) {
if (ObjectUtil.isEmpty(payPrice)) {
return "0";
}
BigDecimal fen = new BigDecimal(payPrice);
return fen.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP).toString();
}
}
@@ -2,23 +2,6 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.hard.mapper.order.HardCarMapper">
<resultMap id="hardCarMap" type="com.bsd.pjcloud.hard.api.entity.order.HardCarEntity">
<id property="id" column="id"/>
<result property="parentCarId" column="parent_car_id"/>
<result property="productId" column="product_id"/>
<result property="productName" column="product_name"/>
<result property="productPic" column="product_pic"/>
<result property="productPrice" column="product_price"/>
<result property="productCount" column="product_count"/>
<result property="productWeight" column="product_weight"/>
<result property="totalPrice" column="total_price"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
<sql id="selectA">
select a.id,
@@ -30,6 +13,7 @@
a.product_nature,
a.product_count,
a.product_weight,
a.is_discounts_product,
a.total_price,
a.create_by,
a.create_time,
@@ -41,6 +25,9 @@
<sql id="whereA">
a.del_flag = '0'
<if test="dto.isDiscountsProduct != null and dto.isDiscountsProduct != '' ">
and is_discounts_product = #{dto.isDiscountsProduct}
</if>
<if test="dto.createTimeStr != null and dto.createTimeStr != '' ">
<choose>
<when test="dto.createTimeType != null and dto.createTimeType != '' ">
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.thirdparty.dto;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class ThirdIsVerifyDeviceTo {
@NotEmpty(message = "用户手机号不能为空")
private String mobile;
/**
* 会校验`mobile`是否属于`userId`
*/
private Long userId;
/**
* 唯一值,小程序是openId
*/
private String uniqueCode;
/**
* 设备信息
*/
private String deviceJson;
}
@@ -1,5 +1,7 @@
package com.bsd.pjcloud.thirdparty.dto.oathmini.pay.wechat;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -14,14 +16,12 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor
public class ThirdMiniWechatRefundDto {
private Long orderId;
private String wechantOrderId;
private Long refundId;
private Integer orderPrice;
@NotEmpty
private String payNo;
/**
* 为null就全额退款
*/
private Integer refundPrice;
}
@@ -3,7 +3,13 @@ package com.bsd.pjcloud.thirdparty.dto.pay;
import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ThirdPayOrderDto extends ThirdPayOrderEntity {
private LocalDateTime[] payTimeList;
private LocalDateTime[] refundTimeList;
}
@@ -81,6 +81,18 @@ public class ThirdPayOrderEntity extends Model<ThirdPayOrderEntity> {
@Schema(description = "支付时间")
private LocalDateTime payTime;
/**
* 支付参数
*/
@Schema(description = "支付参数")
private String payRep;
/**
* 支付回调返回
*/
@Schema(description = "支付回调返回")
private String payCallbackRes;
/**
* 支付返回
*/
@@ -93,12 +105,24 @@ public class ThirdPayOrderEntity extends Model<ThirdPayOrderEntity> {
@Schema(description = "退款订单号")
private String refundNo;
/**
* 退款回调返回
*/
@Schema(description = "退款回调返回")
private String refundCallbackRes;
/**
* 退款时间
*/
@Schema(description = "退款时间")
private LocalDateTime refundTime;
/**
* 退款参数
*/
@Schema(description = "退款参数")
private String refundRep;
/**
* 退款返回
*/
@@ -122,4 +122,19 @@ public class ThirdMiniWechatQueryVo {
public boolean isPaySucess() {
return ObjectUtil.equals(ThirdMiniWechatQueryVo.SUCCESS, result_code);
}
public static String wechantError() {
return "<xml>" +
"<return_code><![CDATA[FAIL]]></return_code>" +
"<return_msg><![CDATA[FAIL]]></return_msg>" +
"</xml>";
}
public static String wechantSuccess() {
return "<xml>" +
" <return_code><![CDATA[SUCCESS]]></return_code>" +
" <return_msg><![CDATA[OK]]></return_msg>\n" +
"</xml>";
}
}
@@ -92,11 +92,25 @@ public class ThirdMiniWechatRefundQueryVo {
private Long refund_fee;
public boolean isHttpSucess() {
return ObjectUtil.equals(ThirdMiniWechatRefundQueryVo.SUCCESS, err_code);
return ObjectUtil.equals(ThirdMiniWechatRefundQueryVo.SUCCESS, err_code) || ObjectUtil.isEmpty(err_code);
}
public boolean isPaySucess() {
return ObjectUtil.equals(ThirdMiniWechatRefundQueryVo.SUCCESS, result_code);
}
public static String wechantError() {
return "<xml>" +
"<return_code><![CDATA[FAIL]]></return_code>" +
"<return_msg><![CDATA[FAIL]]></return_msg>" +
"</xml>";
}
public static String wechantSuccess() {
return "<xml>" +
" <return_code><![CDATA[SUCCESS]]></return_code>" +
" <return_msg><![CDATA[OK]]></return_msg>\n" +
"</xml>";
}
}
@@ -6,4 +6,8 @@ import lombok.Data;
@Data
public class ThirdPayOrderVo extends ThirdPayOrderEntity {
private String username;
private String userPhone;
}
@@ -1,6 +1,8 @@
package com.bsd.pjcloud.thirdparty.controller;
import cn.hutool.core.util.ObjectUtil;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
import com.bsd.pjcloud.admin.api.dto.UserTokenDTO;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.admin.api.entity.UserTrustedDeviceEntity;
@@ -13,10 +15,7 @@ import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.core.util.WebUtils;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.thirdparty.dto.DeviceVerifyDTO;
import com.bsd.pjcloud.thirdparty.dto.SHJNSSODTO;
import com.bsd.pjcloud.thirdparty.dto.UrlTokenDTO;
import com.bsd.pjcloud.thirdparty.dto.ZZDUserDTO;
import com.bsd.pjcloud.thirdparty.dto.*;
import com.bsd.pjcloud.thirdparty.service.SmsService;
import com.bsd.pjcloud.thirdparty.service.ThirdPartyOAuth2Service;
import com.bsd.pjcloud.thirdparty.service.ThirdPartyUserService;
@@ -35,6 +34,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DigestUtils;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
@@ -43,11 +43,14 @@ import java.util.concurrent.TimeUnit;
@RestController
@RequiredArgsConstructor
@RequestMapping("/thirdPartyOAth2" )
@Tag(description = "thirdPartyOAth2" , name = "第三方单点接口" )
@RequestMapping("/thirdPartyOAth2")
@Tag(description = "thirdPartyOAth2", name = "第三方单点接口")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class ThirdPartyOAth2Cotroller {
static final String MINI_MAP_APP_DEVICE_NAME = "微信小程序设备认证";
static final String SH_DEVICE_NAME = "诗画设备认证";
private final ThirdPartyOAuth2Service thirdPartyOAuth2Service;
private final RemoteUserService remoteUserService;
@@ -72,10 +75,10 @@ public class ThirdPartyOAth2Cotroller {
//
// }
@Operation(summary = "诗画token" )
@GetMapping("/sh/sso" )
public R getThirdPartyshsso(@RequestParam("accountId") String accountId,@RequestParam("sessionId") String sessionId) {
return R.ok(thirdPartyOAuth2Service.shjnsso(accountId,sessionId));
@Operation(summary = "诗画token")
@GetMapping("/sh/sso")
public R getThirdPartyshsso(@RequestParam("accountId") String accountId, @RequestParam("sessionId") String sessionId) {
return R.ok(thirdPartyOAuth2Service.shjnsso(accountId, sessionId));
}
@@ -200,7 +203,6 @@ public class ThirdPartyOAth2Cotroller {
}
/**
* 诗画sdk获取参数
*
@@ -213,31 +215,31 @@ public class ThirdPartyOAth2Cotroller {
@SysLog("诗画江南单点登录")
@Transactional
@GlobalTransactional
public R shjnsso(@RequestBody @Valid SHJNSSODTO shjnsso,HttpServletRequest httpServletRequest) {
public R shjnsso(@RequestBody @Valid SHJNSSODTO shjnsso, HttpServletRequest httpServletRequest) {
//检查设备指纹
ZZDUserDTO zzdUserDTO = thirdPartyOAuth2Service.shjnsso(shjnsso.getAccountId(), shjnsso.getSessionId());
if (null==zzdUserDTO){
ZZDUserDTO zzdUserDTO = thirdPartyOAuth2Service.shjnsso(shjnsso.getAccountId(), shjnsso.getSessionId());
if (null == zzdUserDTO) {
throw new RuntimeException("");
}
ZZDUserDTO.Ext ext = zzdUserDTO.getExt();
String mobile = zzdUserDTO.getMobile();
R<List<SysUser>> userByMobileOrEmployeeCode = remoteUserService.getUserByMobileOrEmployeeCode(null, mobile);
List<SysUser> sysUserList = userByMobileOrEmployeeCode.getData();
if(!CollectionUtils.isEmpty(sysUserList)){
List<SysUser> sysUserList = userByMobileOrEmployeeCode.getData();
if (!CollectionUtils.isEmpty(sysUserList)) {
SysUser sysUser = sysUserList.get(0);
//判断当前用户设备是否信任
String md5 = DeviceFingerprintUtil.generate(httpServletRequest);
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(sysUser.getPhone(), md5);
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(sysUser.getPhone(), md5, SH_DEVICE_NAME);
UserTrustedDeviceEntity userTrustedDevice = userTrustedDeviceR.getData();
if (userTrustedDevice == null || userTrustedDevice.getExpireTime().isBefore(LocalDateTime.now())){
return R.failed(-1,"请先信任设备");
if (userTrustedDevice == null || userTrustedDevice.getExpireTime().isBefore(LocalDateTime.now())) {
return R.failed(-1, "请先信任设备");
}
//登录成功生成token
UserTokenDTO userTokenDTO = new UserTokenDTO();
userTokenDTO.setSysUser(sysUser);
return remoteTokenService.generateToken(userTokenDTO);
}else {
} else {
//自动注册
// UserDTO userDTO = new UserDTO();
//// userDTO.setDeptId(1865990541445263361L);
@@ -259,6 +261,7 @@ public class ThirdPartyOAth2Cotroller {
}
}
@Inner(value = false)
@GetMapping("/getAuthToken/{token}")
public R info(@PathVariable(name = "token") String token) {
@@ -284,7 +287,7 @@ public class ThirdPartyOAth2Cotroller {
@PostMapping("/verify-device")
@SysLog("设备验证")
@Inner(value = false)
public R verifyDevice(@RequestBody DeviceVerifyDTO dto,HttpServletRequest httpServletRequest) {
public R verifyDevice(@RequestBody DeviceVerifyDTO dto, HttpServletRequest httpServletRequest) {
// 1. 校验验证码
String redisKey = "smsCode_" + dto.getMobilePhone();
String storedCode = (String) redisTemplate.opsForValue().get(redisKey);
@@ -292,53 +295,103 @@ public class ThirdPartyOAth2Cotroller {
return R.failed("验证码错误");
}
// 验证成功后移除验证码(可选)
redisTemplate.delete(redisKey);
// 验证成功后移除验证码(可选)
redisTemplate.delete(redisKey);
// 2. 记录信任设备
//查询之前的是否有过期的数据,有就更新
String generate = DeviceFingerprintUtil.generate(httpServletRequest);
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(dto.getMobilePhone(), generate);
if (userTrustedDeviceR.getData() != null){
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(dto.getMobilePhone(), generate, SH_DEVICE_NAME);
if (userTrustedDeviceR.getData() != null) {
userTrustedDeviceR.getData().setExpireTime(LocalDateTime.now().plusMonths(3));
remoteUserTrustedDeviceService.updateById(userTrustedDeviceR.getData());
}else {
} else {
UserTrustedDeviceEntity userTrustedDevice = new UserTrustedDeviceEntity();
userTrustedDevice.setMobilePhone(dto.getMobilePhone());
userTrustedDevice.setExpireTime(LocalDateTime.now().plusMonths(3));
userTrustedDevice.setDeviceName("诗画设备认证");
userTrustedDevice.setDeviceName(SH_DEVICE_NAME);
userTrustedDevice.setDeviceFingerprint(generate);
remoteUserTrustedDeviceService.save(userTrustedDevice);
}
return R.ok();
}
@PostMapping("/mini-app/verify-device")
@SysLog("小程序设备验证")
@Inner(value = false)
public R miniVerifyDevice(@RequestBody DeviceVerifyDTO dto, HttpServletRequest httpServletRequest) {
// 1. 校验验证码
String redisKey = "smsCode_" + dto.getMobilePhone();
String storedCode = (String) redisTemplate.opsForValue().get(redisKey);
if (!dto.getCode().equals(storedCode)) {
return R.failed("验证码错误");
}
// 验证成功后移除验证码(可选)
redisTemplate.delete(redisKey);
// 2. 记录信任设备
//查询之前的是否有过期的数据,有就更新
String generate = DeviceFingerprintUtil.generate(httpServletRequest);
R<UserTrustedDeviceEntity> userTrustedDeviceR = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(dto.getMobilePhone(), generate, MINI_MAP_APP_DEVICE_NAME);
if (userTrustedDeviceR.getData() != null) {
userTrustedDeviceR.getData().setExpireTime(LocalDateTime.now().plusMonths(3));
remoteUserTrustedDeviceService.updateById(userTrustedDeviceR.getData());
} else {
UserTrustedDeviceEntity userTrustedDevice = new UserTrustedDeviceEntity();
userTrustedDevice.setMobilePhone(dto.getMobilePhone());
userTrustedDevice.setExpireTime(LocalDateTime.now().plusMonths(3));
userTrustedDevice.setDeviceName(MINI_MAP_APP_DEVICE_NAME);
userTrustedDevice.setDeviceFingerprint(generate);
remoteUserTrustedDeviceService.save(userTrustedDevice);
}
return R.ok();
}
@PostMapping("/is-verify-device")
@SysLog("判断该设备是否被验证")
@Inner(value = false)
public R<Boolean> isVerifyDevice(@RequestBody ThirdIsVerifyDeviceTo body, HttpServletRequest httpServletRequest) {
if (ObjectUtil.isNotEmpty(body.getUserId())) {
UserInfo userInfo = remoteUserService.getInnerByUserId(body.getUserId()).getData();
if (ObjectUtil.isEmpty(userInfo)) {
throw new RuntimeException("用户不存在");
}
if (ObjectUtil.notEqual(userInfo.getSysUser().getPhone(), body.getMobile())) {
throw new RuntimeException("手机号和登录的账号不一致");
}
}
String generate = DeviceFingerprintUtil.generate(httpServletRequest);
UserTrustedDeviceEntity userTrustedDevice = remoteUserTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(body.getMobile(), generate, MINI_MAP_APP_DEVICE_NAME).getData();
return R.ok(ObjectUtil.isNotEmpty(userTrustedDevice));
}
/**
* 诗画浦江端
*
* @param mobilePhone
* @param httpServletRequest
* @param type 短信区分标识 if null 诗画登录短信 1 访客登录短信
* @param type 短信区分标识 if null 诗画登录短信 1 访客登录短信
* @return
*/
@PostMapping("/loginSms")
@Operation(summary = "发送登录短信")
@SysLog("发送登录短信")
@Inner(value = false)
public R loginSms(@RequestParam String mobilePhone,HttpServletRequest httpServletRequest,
@RequestParam(value = "type",required = false)Integer type) {
Map<String, String> stringHashMap = new LinkedHashMap<>();
public R loginSms(@RequestParam String mobilePhone, HttpServletRequest httpServletRequest,
@RequestParam(value = "type", required = false) Integer type) {
Map<String, String> stringHashMap = new LinkedHashMap<>();
String s = SmsCodeUtil.generateNumberCode(6);
stringHashMap.put("code",s);
stringHashMap.put("date","5");
smsService.sendSms(mobilePhone, "20657",stringHashMap, WebUtils.getIP());
stringHashMap.put("code", s);
stringHashMap.put("date", "5");
smsService.sendSms(mobilePhone, "20657", stringHashMap, WebUtils.getIP());
// null 适配之前诗画的,=1访客,先else
if (null==type){
redisTemplate.opsForValue().set(CacheConstants.SMS_CODE_KEY +mobilePhone,s, 5, TimeUnit.MINUTES);
}else {
stringRedisTemplate.opsForValue().set(CacheConstants.SMS_VISITOR_CODE_KEY +mobilePhone,s, 5, TimeUnit.MINUTES);
if (null == type) {
redisTemplate.opsForValue().set(CacheConstants.SMS_CODE_KEY + mobilePhone, s, 5, TimeUnit.MINUTES);
} else {
stringRedisTemplate.opsForValue().set(CacheConstants.SMS_VISITOR_CODE_KEY + mobilePhone, s, 5, TimeUnit.MINUTES);
}
// redisTemplate.opsForValue().set(CacheConstants.SMS_CODE_KEY +mobilePhone,s, 5, TimeUnit.MINUTES);
@@ -5,12 +5,13 @@ import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppPayDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.pay.wechat.ThirdMiniWechatRefundDto;
import com.bsd.pjcloud.thirdparty.service.ThirdMiniAppService;
import com.bsd.pjcloud.thirdparty.vo.oathmini.ThirdMinAppOAth2LoginVo;
import com.bsd.pjcloud.thirdparty.vo.oathmini.pay.ThirdMiniAppPayVo;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
@@ -28,6 +29,15 @@ public class ThirdMinAppPayCotroller {
private final ThirdMiniAppService thirdMiniAppService;
@Operation(summary = "小程序支付退款")
@PostMapping("/refund")
public R<ThirdMiniAppPayVo> miniRefund(@Valid @RequestBody ThirdMiniWechatRefundDto dto) {
return R.ok(thirdMiniAppService.miniRefund(dto));
}
/* ************************************* start app start ************************************* */
@Operation(summary = "小程序支付")
@PostMapping("/app/pay")
public R<ThirdMiniAppPayVo> miniPay(@Validated @RequestBody ThirdMiniAppPayDto dto) {
@@ -51,4 +61,6 @@ public class ThirdMinAppPayCotroller {
return R.ok(thirdMiniAppService.miniRefundCallback(body, headers));
}
/* ************************************* end app end ************************************* */
}
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.thirdparty.service;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppGetUniqueDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppOAth2LoginDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppPayDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.pay.wechat.ThirdMiniWechatRefundDto;
import com.bsd.pjcloud.thirdparty.vo.oathmini.ThirdMinAppOAth2LoginVo;
import com.bsd.pjcloud.thirdparty.vo.oathmini.pay.ThirdMiniAppPayVo;
@@ -25,6 +26,11 @@ public interface ThirdMiniAppService {
*/
ThirdMiniAppPayVo miniPay(Long userId, ThirdMiniAppPayDto dto);
/**
* 小程序退款
*/
ThirdMiniAppPayVo miniRefund(ThirdMiniWechatRefundDto dto);
/**
* 小程序支付回调
*/
@@ -3,6 +3,7 @@ package com.bsd.pjcloud.thirdparty.service;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.bsd.pjcloud.thirdparty.dto.oathmini.pay.wechat.*;
import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity;
import com.bsd.pjcloud.thirdparty.vo.oathmini.pay.wechat.*;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.RequestHeader;
@@ -39,16 +40,21 @@ public interface ThirdMiniAppWechatService {
/**
* 申请退款
*/
ThirdMiniWechatRefundVo refund(ThirdMiniWechatRefundDto dto);
ThirdMiniWechatRefundVo refund(ThirdPayOrderEntity thirdPayOrder, ThirdMiniWechatRefundDto dto);
/**
* 查询退款
*/
ThirdMiniWechatRefundQueryVo refundQuery(ThirdMiniWechatRefundQueryDto dto);
/**
* 支付的回调通知
*/
ThirdPayOrderEntity payCallback(String body, @RequestHeader Map<String, String> headers);
/**
* 退款回调
*/
String refundCallback(HttpEntity<String> httpEntity, @RequestHeader Map<String, String> headers);
ThirdPayOrderEntity refundCallback(String body, @RequestHeader Map<String, String> headers);
}
@@ -1,5 +1,6 @@
package com.bsd.pjcloud.thirdparty.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.common.data.datascope.DataScope;
@@ -11,4 +12,8 @@ public interface ThirdPayOrderService extends IService<ThirdPayOrderEntity> {
Page<ThirdPayOrderVo> scopePage(Page page, ThirdPayOrderDto dto, DataScope dataScope);
default ThirdPayOrderEntity getByPayNo(String payNo) {
return getOne(new LambdaQueryWrapper<ThirdPayOrderEntity>().eq(ThirdPayOrderEntity::getPayNo, payNo));
}
}
@@ -3,6 +3,8 @@ package com.bsd.pjcloud.thirdparty.service.impl.miniapp;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.XmlUtil;
import cn.hutool.json.JSONUtil;
import com.bsd.pjcloud.admin.api.dto.UserInfo;
import com.bsd.pjcloud.admin.api.dto.UserTokenDTO;
import com.bsd.pjcloud.admin.api.feign.RemoteTokenService;
@@ -14,18 +16,19 @@ import com.bsd.pjcloud.app.api.feign.RemoteAppUserService;
import com.bsd.pjcloud.thirdparty.constant.miniplatform.MiniPlatformLoginErrorEnum;
import com.bsd.pjcloud.thirdparty.constant.miniplatform.MiniPlatformTypeEnum;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayOrderBizTypeEnum;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayOrderStatusEnum;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppGetUniqueDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppOAth2LoginDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.ThirdMiniAppPayDto;
import com.bsd.pjcloud.thirdparty.dto.oathmini.pay.wechat.ThirdMiniJsapiPayDto;
import com.bsd.pjcloud.thirdparty.entity.api.ThirdPayLogEntity;
import com.bsd.pjcloud.thirdparty.service.ThirdMiniAppAliPayService;
import com.bsd.pjcloud.thirdparty.service.ThirdMiniAppService;
import com.bsd.pjcloud.thirdparty.service.ThirdMiniAppWechatService;
import com.bsd.pjcloud.thirdparty.service.ThirdPayLogService;
import com.bsd.pjcloud.thirdparty.dto.oathmini.pay.wechat.ThirdMiniWechatRefundDto;
import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity;
import com.bsd.pjcloud.thirdparty.service.*;
import com.bsd.pjcloud.thirdparty.vo.oathmini.ThirdMinAppOAth2LoginVo;
import com.bsd.pjcloud.thirdparty.vo.oathmini.pay.ThirdMiniAppPayVo;
import com.bsd.pjcloud.thirdparty.vo.oathmini.pay.wechat.ThirdMiniJsapiPayVo;
import com.bsd.pjcloud.thirdparty.vo.oathmini.pay.wechat.*;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePaySuccess;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundSuccess;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargePackageEntity;
import com.bsd.pjcloud.wallet.api.feign.RemoteWalletRechargePackageService;
import lombok.RequiredArgsConstructor;
@@ -36,7 +39,6 @@ import org.springframework.validation.annotation.Validated;
import javax.net.ssl.*;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.Map;
/**
@@ -48,9 +50,12 @@ import java.util.Map;
@RequiredArgsConstructor
public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
static private final String TAG = "小程序服务类";
private final ThirdMiniAppWechatService thirdMiniAppWechatService;
private final ThirdMiniAppAliPayService thirdMiniAppAliPayService;
private final ThirdPayLogService thirdPayLogService;
private final ThirdPayOrderService thirdPayOrderService;
private final RemoteUserService remoteUserService;
private final RemoteTokenService remoteTokenService;
@@ -62,11 +67,17 @@ public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
*/
@Override
public ThirdMinAppOAth2LoginVo adminLogin(ThirdMiniAppOAth2LoginDto dto) {
long startTime = System.currentTimeMillis();
ThirdMinAppOAth2LoginVo vo = new ThirdMinAppOAth2LoginVo();
// 微信小程序
if (ObjectUtil.equals(dto.getMiniPlatformType().getType(), MiniPlatformTypeEnum.WECHAT.getType())) {
// 获取手机号
long getPhoneStartTime = System.currentTimeMillis();
WxMaPhoneNumberInfo phoneInfo = thirdMiniAppWechatService.getPhone(dto.getPhoneCode());
long getPhoneEndTime = System.currentTimeMillis();
log.info("|{}|getPhone耗时: {} ms", TAG, (getPhoneEndTime - getPhoneStartTime));
if (ObjectUtil.isEmpty(phoneInfo)) {
vo.setErrorCode(MiniPlatformLoginErrorEnum.GET_PHONE_ERROR.getType());
return vo;
@@ -78,22 +89,28 @@ public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
return vo;
}
// 获取用户唯一编号
long getUnicodeStartTime = System.currentTimeMillis();
ThirdMiniAppGetUniqueDto getUniqueDto = new ThirdMiniAppGetUniqueDto();
getUniqueDto.setMiniPlatformType(MiniPlatformTypeEnum.WECHAT);
getUniqueDto.setCode(dto.getPhoneCode());
getUniqueDto.setCode(dto.getUserCode());
getUniqueDto.setUserId(userInfo.getSysUser().getUserId());
String uniqueCode = getUniqueCode(getUniqueDto);
long getUnicodeEndTime = System.currentTimeMillis();
log.info("|{}|getUniqueCode耗时: {} ms", TAG, (getUnicodeEndTime - getUnicodeStartTime));
// 同步到社交用户
AppAdminSyncDto appAdminSyncDto = new AppAdminSyncDto();
appAdminSyncDto.setUniqueCode(uniqueCode);
appAdminSyncDto.setUserTypeEnum(AppUserTypeEnum.MINI_WECHAT);
appAdminSyncDto.setAdminUserId(userInfo.getSysUser().getUserId());
appAdminSyncDto.setUsername(userInfo.getSysUser().getUsername());
appAdminSyncDto.setAvatarImage(userInfo.getSysUser().getAvatar());
AppUserInfo appUserInfo = remoteAppUserService.syncAdminAccountInfo(appAdminSyncDto).getData();
if (ObjectUtil.isEmpty(appUserInfo)) {
vo.setErrorCode(MiniPlatformLoginErrorEnum.LOGIN_ERROR.getType());
return vo;
if (ObjectUtil.isNotEmpty(uniqueCode)) {
AppAdminSyncDto appAdminSyncDto = new AppAdminSyncDto();
appAdminSyncDto.setUniqueCode(uniqueCode);
appAdminSyncDto.setUserTypeEnum(AppUserTypeEnum.MINI_WECHAT);
appAdminSyncDto.setAdminUserId(userInfo.getSysUser().getUserId());
appAdminSyncDto.setUsername(userInfo.getSysUser().getUsername());
appAdminSyncDto.setAvatarImage(userInfo.getSysUser().getAvatar());
AppUserInfo appUserInfo = remoteAppUserService.syncAdminAccountInfo(appAdminSyncDto).getData();
if (ObjectUtil.isEmpty(appUserInfo)) {
vo.setErrorCode(MiniPlatformLoginErrorEnum.LOGIN_ERROR.getType());
return vo;
}
}
// 执行登录
UserTokenDTO userTokenDTO = new UserTokenDTO();
@@ -110,6 +127,8 @@ public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
else if (ObjectUtil.equals(dto.getMiniPlatformType().getType(), MiniPlatformTypeEnum.ALI_PAY.getType())) {
}
long endTime = System.currentTimeMillis();
log.info("|{}|adminLogin耗时: {} ms", TAG, (startTime - endTime));
return vo;
}
@@ -146,7 +165,7 @@ public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
if (ObjectUtil.isEmpty(walletPackage)) {
throw new RuntimeException("错误的充值配置");
}
int price = walletPackage.getPayPrice() + walletPackage.getBonusPrice();
int price = walletPackage.getPayPrice();
if (price <= 0) {
throw new RuntimeException("错误的充值配置");
}
@@ -188,13 +207,56 @@ public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
return result;
}
/**
* 小程序退款
*/
@Override
public ThirdMiniAppPayVo miniRefund(ThirdMiniWechatRefundDto dto) {
// 查询详情
ThirdPayOrderEntity thirdPayOrder = thirdPayOrderService.getByPayNo(dto.getPayNo());
if (ObjectUtil.isEmpty(thirdPayOrder)) {
log.error("|{}|小程序退款错误,订单不存在dto:|{}|", TAG, JSONUtil.toJsonStr(dto));
throw new RuntimeException("订单不存在");
}
// 微信小程序
if (ObjectUtil.equals(thirdPayOrder.getPayPlatformType(), MiniPlatformTypeEnum.WECHAT.getType())) {
thirdMiniAppWechatService.refund(thirdPayOrder, dto);
}
// 支付宝
else if (ObjectUtil.equals(thirdPayOrder.getPayPlatformType(), MiniPlatformTypeEnum.ALI_PAY.getType())) {
}
// 不支持的支付类型
else {
throw new RuntimeException("不支持的支付类型");
}
return null;
}
/**
* 小程序支付回调
*/
@Override
public String miniPayCallback(String body, Map<String, String> headers) {
return "";
MiniPlatformTypeEnum platformType = checkMiniPlatformType(body);
// 微信小程序
if (ObjectUtil.equals(platformType, MiniPlatformTypeEnum.WECHAT)) {
ThirdPayOrderEntity thirdPayOrder = thirdMiniAppWechatService.payCallback(body, headers);
if (ObjectUtil.isEmpty(thirdPayOrder)) {
return ThirdMiniWechatQueryVo.wechantError();
}
// 根据订单bizType来处理
boolean isSuccess = disposePaySuccessAfter(thirdPayOrder);
return isSuccess ? ThirdMiniWechatQueryVo.wechantSuccess() : ThirdMiniWechatQueryVo.wechantError();
}
// 支付宝
else if (ObjectUtil.equals(platformType, MiniPlatformTypeEnum.ALI_PAY)) {
return null;
}
// 不支持的支付类型
else {
return null;
}
}
/**
@@ -202,7 +264,79 @@ public class ThirdMiniAppServiceImpl implements ThirdMiniAppService {
*/
@Override
public String miniRefundCallback(String body, Map<String, String> headers) {
return "";
MiniPlatformTypeEnum platformType = checkRefundMiniPlatformType(body);
// 微信小程序
if (ObjectUtil.equals(platformType, MiniPlatformTypeEnum.WECHAT)) {
ThirdPayOrderEntity thirdPayOrder = thirdMiniAppWechatService.refundCallback(body, headers);
if (ObjectUtil.isEmpty(thirdPayOrder)) {
return ThirdMiniWechantRefunCallbackVo.wechantError();
}
// 根据订单bizType来处理
boolean isSuccess = disposeRefundSuccessAfter(thirdPayOrder);
return isSuccess ? ThirdMiniWechatQueryVo.wechantSuccess() : ThirdMiniWechatQueryVo.wechantError();
}
// 支付宝
else if (ObjectUtil.equals(platformType, MiniPlatformTypeEnum.ALI_PAY)) {
return null;
}
// 不支持的支付类型
else {
return null;
}
}
private MiniPlatformTypeEnum checkMiniPlatformType(String body) {
ThirdMiniWechatRefundQueryVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(body)), ThirdMiniWechatRefundQueryVo.class);
// 查询订单
if (ObjectUtil.isNotEmpty(vo) && ObjectUtil.isNotEmpty(vo.getTransaction_id())) {
return MiniPlatformTypeEnum.WECHAT;
}
return null;
}
private MiniPlatformTypeEnum checkRefundMiniPlatformType(String body) {
ThirdMiniWechantRefunCallbackSoureVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(body)), ThirdMiniWechantRefunCallbackSoureVo.class);
// 查询订单
if (ObjectUtil.isNotEmpty(vo) && ObjectUtil.isNotEmpty(vo.getAppid()) && ObjectUtil.isNotEmpty(vo.getMch_id()) && ObjectUtil.isNotEmpty(vo.getNonce_str()) && ObjectUtil.isNotEmpty(vo.getReq_info())) {
return MiniPlatformTypeEnum.WECHAT;
}
return null;
}
public boolean disposePaySuccessAfter(ThirdPayOrderEntity thirdPayOrder) {
if (ObjectUtil.notEqual(thirdPayOrder.getStatus(), ThirdPayOrderStatusEnum.PAY_SUCCESS.getType())) {
return true;
}
// 已经支付
try {
WalletRechargePaySuccess dto = new WalletRechargePaySuccess();
dto.setPayNo(thirdPayOrder.getPayNo());
dto.setUserId(thirdPayOrder.getUserId());
dto.setBizId(thirdPayOrder.getBizId());
dto.setBizType(thirdPayOrder.getBizType());
return remoteWalletRechargePackageService.remotePaySuccess(dto).getData();
} catch (Exception e) {
log.error("处理小程序支付成功回调异常", e);
return false;
}
}
private boolean disposeRefundSuccessAfter(ThirdPayOrderEntity thirdPayOrder) {
if (ObjectUtil.notEqual(thirdPayOrder.getStatus(), ThirdPayOrderStatusEnum.REFUND_SUCCESS.getType())) {
return true;
}
// 已退款
try {
WalletRechargeRefundSuccess dto = new WalletRechargeRefundSuccess();
dto.setRefundNo(thirdPayOrder.getRefundNo());
dto.setUserId(thirdPayOrder.getUserId());
dto.setBizId(thirdPayOrder.getBizId());
dto.setBizType(thirdPayOrder.getBizType());
return remoteWalletRechargePackageService.remoteRefundSuccess(dto).getData();
} catch (Exception e) {
log.error("处理小程序退款成功回调异常", e);
return false;
}
}
}
@@ -68,6 +68,7 @@ public class ThirdMiniAppWechatServiceImpl implements ThirdMiniAppWechatService
payOrder.setUserCode(dto.getOpenid());
payOrder.setPrice(dto.getAmount());
payOrder.setStatus(ThirdPayOrderStatusEnum.PAY_UNPAID.getType());
payOrder.setPayNo(dto.getOutTradeNo());
try {
Map<String, Object> params = new HashMap<>();
// 公众账号ID
@@ -85,7 +86,7 @@ public class ThirdMiniAppWechatServiceImpl implements ThirdMiniAppWechatService
// 商户订单号
params.put("out_trade_no", dto.getOutTradeNo());
// 商户回调地址
params.put("notify_url", wechatConfig.getRefundCallbackUrl());
params.put("notify_url", wechatConfig.getPayCallbackUrl());
// 订单总金额,单位为分
params.put("total_fee", dto.getAmount());
// 终端IP
@@ -97,19 +98,24 @@ public class ThirdMiniAppWechatServiceImpl implements ThirdMiniAppWechatService
// 生成签名
params.put("sign", generateSign(params));
ThirdMiniWechantPayConstant.Api api = initApi(ThirdMiniWechantPayConstant.WECHAT_PAY);
String res = api.getHttpRequest().body(XmlUtil.mapToXmlStr(params)).execute().body();
String xmlStr = XmlUtil.mapToXmlStr(params);
payOrder.setPayRep(JSONUtil.toJsonStr(params));
String res = api.getHttpRequest().body(xmlStr).execute().body();
log.debug("JSAPI/小程序下单,json,xml|{}", res);
if (ObjectUtil.isEmpty(res)) {
log.error("JSAPI/小程序下单,res为空");
return null;
}
ThirdMiniJsapiPayVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(res)), ThirdMiniJsapiPayVo.class);
String jsonVo = JSONUtil.toJsonStr(XmlUtil.xmlToMap(res));
ThirdMiniJsapiPayVo vo = JSONUtil.toBean(jsonVo, ThirdMiniJsapiPayVo.class);
log.debug("|{}|JSAPI/小程序下单,json,res|{}", TAG, JSONUtil.toJsonStr(vo));
if (!vo.isHttpSucess()) {
return null;
}
payOrder.setPayTime(LocalDateTime.now());
payOrder.setPayRes(res);
payOrder.setPayRes(jsonVo);
// 生成小程序 paySign
String nonceStr = RandomUtil.randomString(32);
long timeStamp = System.currentTimeMillis() / 1000;
@@ -263,12 +269,30 @@ public class ThirdMiniAppWechatServiceImpl implements ThirdMiniAppWechatService
}
}
private String getTransactionIdByThirdPayOrder(ThirdPayOrderEntity thirdPayOrder) {
if (ObjectUtil.isEmpty(thirdPayOrder) || ObjectUtil.isEmpty(thirdPayOrder.getPayRes())) {
return null;
}
String payRes = thirdPayOrder.getPayCallbackRes();
ThirdMiniWechantRefunCallbackVo thirdMiniJsapiPay = JSONUtil.toBean(payRes, ThirdMiniWechantRefunCallbackVo.class);
return thirdMiniJsapiPay.getTransaction_id();
}
/**
* 申请退款
*/
@Override
public ThirdMiniWechatRefundVo refund(ThirdMiniWechatRefundDto dto) {
public ThirdMiniWechatRefundVo refund(ThirdPayOrderEntity thirdPayOrder, ThirdMiniWechatRefundDto dto) {
MiniAppConfig.WechatConfig wechatConfig = miniAppConfig.getWechatConfig();
ThirdPayOrderEntity updateObj = new ThirdPayOrderEntity();
updateObj.setId(thirdPayOrder.getId());
Integer refundFee = dto.getRefundPrice();
if (ObjectUtil.isEmpty(refundFee)) {
refundFee = thirdPayOrder.getPrice();
}
String refundNo = "refund_" + thirdPayOrder.getPayNo();
try {
Map<String, Object> params = new HashMap<>();
// 公众账号ID
@@ -282,36 +306,47 @@ public class ThirdMiniAppWechatServiceImpl implements ThirdMiniAppWechatService
// 签名类型
params.put("sign_type", "MD5");
// 微信支付订单号
params.put("transaction_id", dto.getWechantOrderId());
params.put("transaction_id", getTransactionIdByThirdPayOrder(thirdPayOrder));
// 商户订单号
params.put("out_trade_no", dto.getOrderId());
params.put("out_trade_no", thirdPayOrder.getPayNo());
// 商户退款单号
params.put("out_refund_no", dto.getRefundId());
params.put("out_refund_no", refundNo);
// 订单金额
params.put("total_fee", dto.getOrderPrice());
params.put("total_fee", thirdPayOrder.getPrice());
// 退款金额
params.put("refund_fee", dto.getRefundPrice());
params.put("refund_fee", refundFee);
// 退款结果通知url
params.put("notify_url", wechatConfig.getRefundCallbackUrl());
// 生成签名
params.put("sign", generateSign(params));
ThirdMiniWechantPayConstant.Api api = initApi(ThirdMiniWechantPayConstant.REFUND);
api.setSslSocketFactory(createSSLSocketFactory());
String res = api.getHttpRequest().body(XmlUtil.mapToXmlStr(params)).execute().body();
updateObj.setRefundRep(JSONUtil.toJsonStr(params));
String xmlStr = XmlUtil.mapToXmlStr(params);
String res = api.getHttpRequest().body(xmlStr).execute().body();
log.debug("|{}|申请退款,json,xml|{}", TAG, res);
if (ObjectUtil.isEmpty(res)) {
log.error("|{}|申请退款,res为空", TAG);
return null;
}
String jsonVo = JSONUtil.toJsonStr(XmlUtil.xmlToMap(res));
updateObj.setRefundRes(jsonVo);
ThirdMiniWechatRefundVo vo = JSONUtil.toBean(JSONUtil.toJsonStr(XmlUtil.xmlToMap(res)), ThirdMiniWechatRefundVo.class);
log.debug("|{}|申请退款,json,res|{}", TAG, JSONUtil.toJsonStr(vo));
if (!vo.isHttpSucess()) {
return null;
}
updateObj.setRefundNo(refundNo);
updateObj.setRefundTime(LocalDateTime.now());
updateObj.setStatus(ThirdPayOrderStatusEnum.REFUNDING.getType());
return vo;
} catch (Exception e) {
log.error("|{}|申请退款,异常", TAG, e);
return null;
} finally {
thirdPayOrderService.updateById(updateObj);
}
}
@@ -356,19 +391,65 @@ public class ThirdMiniAppWechatServiceImpl implements ThirdMiniAppWechatService
}
}
/**
* 支付的回调通知
*/
@Override
public ThirdPayOrderEntity payCallback(String body, Map<String, String> headers) {
String bodyJson = JSONUtil.toJsonStr(XmlUtil.xmlToMap(body));
ThirdMiniWechatRefundQueryVo vo = JSONUtil.toBean(bodyJson, ThirdMiniWechatRefundQueryVo.class);
if (ObjectUtil.isEmpty(vo) || !vo.isHttpSucess()) {
return null;
}
boolean paySucess = vo.isPaySucess();
// 查询订单
ThirdPayOrderEntity payOrderEntity = thirdPayOrderService.getByPayNo(vo.getOut_trade_no());
if (ObjectUtil.isEmpty(payOrderEntity)) {
log.error("|{}|payCallback,意外的错误,订单不存在,微信返回json|{}|", TAG, bodyJson);
return null;
}
// 重复的回调
if (ObjectUtil.notEqual(payOrderEntity.getStatus(), ThirdPayOrderStatusEnum.PAY_UNPAID.getType())) {
log.error("|{}|payCallback,意外的错误,重复的回调,微信返回json|{}|", TAG, bodyJson);
return null;
}
// 更新订单状态
ThirdPayOrderEntity updateObj = new ThirdPayOrderEntity();
updateObj.setId(payOrderEntity.getId());
updateObj.setPayCallbackRes(bodyJson);
updateObj.setStatus(paySucess ? ThirdPayOrderStatusEnum.PAY_SUCCESS.getType() : ThirdPayOrderStatusEnum.PAY_FAILED.getType());
thirdPayOrderService.updateById(updateObj);
return thirdPayOrderService.getById(payOrderEntity.getId());
}
/**
* 退款回调
*/
@Override
public String refundCallback(HttpEntity<String> httpEntity, Map<String, String> headers) {
String body = httpEntity.getBody();
ThirdMiniWechantRefunCallbackVo decrypt = decrypt(body);
if (ObjectUtil.isEmpty(decrypt)) {
// 告诉微信错误
return ThirdMiniWechantRefunCallbackVo.wechantError();
public ThirdPayOrderEntity refundCallback(String body, Map<String, String> headers) {
ThirdMiniWechantRefunCallbackVo vo = decrypt(body);
if (ObjectUtil.isEmpty(vo)) {
return null;
}
boolean isSuccess = true;
return isSuccess ? ThirdMiniWechantRefunCallbackVo.wechantSuccess() : ThirdMiniWechantRefunCallbackVo.wechantError();
// 查询订单
ThirdPayOrderEntity payOrderEntity = thirdPayOrderService.getByPayNo(vo.getOut_trade_no());
if (ObjectUtil.isEmpty(payOrderEntity)) {
log.error("|{}|refundCallback,意外的错误,订单不存在,微信返回json|{}|", TAG, JSONUtil.toJsonStr(vo));
return null;
}
// 重复的回调
if (ObjectUtil.notEqual(payOrderEntity.getStatus(), ThirdPayOrderStatusEnum.REFUNDING.getType())) {
log.error("|{}|refundCallback,意外的错误,重复的回调,微信返回json|{}|", TAG, JSONUtil.toJsonStr(vo));
return null;
}
// 更新订单状态
boolean refundSuccess = vo.isAllSuccess();
ThirdPayOrderEntity updateObj = new ThirdPayOrderEntity();
updateObj.setId(payOrderEntity.getId());
updateObj.setRefundCallbackRes(JSONUtil.toJsonStr(vo));
updateObj.setStatus(refundSuccess ? ThirdPayOrderStatusEnum.REFUND_SUCCESS.getType() : ThirdPayOrderStatusEnum.REFUND_FAILED.getType());
thirdPayOrderService.updateById(updateObj);
return thirdPayOrderService.getById(payOrderEntity.getId());
}
/**
@@ -1,24 +1,59 @@
package com.bsd.pjcloud.thirdparty.service.impl.pay;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.admin.api.feign.RemoteUserService;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.thirdparty.dto.pay.ThirdPayOrderDto;
import com.bsd.pjcloud.thirdparty.entity.pay.ThirdPayOrderEntity;
import com.bsd.pjcloud.thirdparty.mapper.pay.ThirdPayOrderMapper;
import com.bsd.pjcloud.thirdparty.service.ThirdPayOrderService;
import com.bsd.pjcloud.thirdparty.vo.pay.ThirdPayOrderVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Map;
/**
* 第三方支付订单
*/
@Service
@Validated
@Slf4j
@RequiredArgsConstructor
public class ThirdPayOrderServiceImpl extends ServiceImpl<ThirdPayOrderMapper, ThirdPayOrderEntity> implements ThirdPayOrderService {
private final RemoteUserService remoteUserService;
@Override
public Page<ThirdPayOrderVo> scopePage(Page page, ThirdPayOrderDto dto, DataScope dataScope) {
return baseMapper.scopePage(page, dto, dataScope);
Page<ThirdPayOrderVo> pageList = baseMapper.scopePage(page, dto, dataScope);
disposePageList(pageList.getRecords(), dto);
return pageList;
}
private void disposePageList(List<ThirdPayOrderVo> list, ThirdPayOrderDto dto) {
if (ObjectUtil.isEmpty(list)) return;
// 查询用户
List<Long> userIdList = list.stream().map(ThirdPayOrderEntity::getUserId).filter(ObjectUtil::isNotEmpty).distinct().toList();
if (ObjectUtil.isNotEmpty(userIdList)) {
List<SysUser> userList = remoteUserService.getInnerUserById(userIdList).getData();
if (ObjectUtil.isNotEmpty(userList)) {
Map<Long, SysUser> userMap = IterUtil.toMap(userList, SysUser::getUserId);
for (ThirdPayOrderVo user : list) {
SysUser targetUser = userMap.get(user.getUserId());
if (ObjectUtil.isEmpty(targetUser)) continue;
user.setUsername(targetUser.getName());
user.setUserPhone(targetUser.getPhone());
}
}
}
}
}
@@ -186,17 +186,17 @@ hikvision:
register-appointment: /api/visitor/v1/order/register # 新增登记接口
visitor-checkout: /api/visitor/v1/visitor/out # 新增签离接口
mini-platform:
mini-app:
wechat-config:
app-id: wx11771c7bf2913cb2
app-secret: a6b0d036b229117595687a7c1705d332
merchant-id: 1106210589
api-v2-key: qr0w9WkAu6GG3uuxPjDT7bcdIVoBr0rw
merchant-serial-path: /Users/dengzhangchen/Desktop/backups/pay/third_cert/apiclient_cert.p12
pay-callback-url: https://rj4jv5rwqs.by.takin.cc/third/third-mini-app-pay/app/pay-callback
refund-callback-url: https://rj4jv5rwqs.by.takin.cc/hard/hard-wechant-pay/app/refund-callback
spbill-create-ip: 59.202.170.152
base-url: https://api.mch.weixin.qq.com
ali-pay-config:
app-id:
#mini-platform:
# mini-app:
# wechat-config:
# app-id: wx11771c7bf2913cb2
# app-secret: a6b0d036b229117595687a7c1705d332
# merchant-id: 1106210589
# api-v2-key: qr0w9WkAu6GG3uuxPjDT7bcdIVoBr0rw
# merchant-serial-path: /Users/dengzhangchen/Desktop/backups/pay/third_cert/apiclient_cert.p12
# pay-callback-url: https://dzc-test-1.fy.takin.cc/third/third-mini-app-pay/app/pay-callback
# refund-callback-url: https://dzc-test-1.fy.takin.cc/third/third-mini-app-pay/app/refund-callback
# spbill-create-ip: 59.202.170.152
# base-url: https://api.mch.weixin.qq.com
# ali-pay-config:
# app-id:
@@ -2,7 +2,6 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.thirdparty.mapper.pay.ThirdPayOrderMapper">
<sql id="selectA">
select a.id,
a.pay_platform_type,
@@ -14,10 +13,14 @@
a.status,
a.pay_no,
a.pay_time,
a.pay_rep,
a.pay_res,
a.pay_callback_res,
a.refund_no,
a.refund_time,
a.refund_rep,
a.refund_res,
a.refund_callback_res,
a.expire_time,
a.create_time,
a.update_time,
@@ -29,8 +32,26 @@
<sql id="whereA">
a.del_flag = '0'
<if test="dto.payNo != null and dto.payNo != '' ">
and a.pay_no LIKE concat('%',#{dto.payNo},'%')
</if>
<if test="dto.id != null">
and a.id = #{dto.id}
</if>
<if test="dto.payPlatformType != null and dto.payPlatformType != '' ">
and a.pay_platform_type = #{dto.payPlatformType}
</if>
<if test="dto.bizType != null and dto.bizType != '' ">
and a.biz_type = #{dto.bizType}
</if>
<if test="dto.userId != null">
and a.user_id = #{dto.userId}
</if>
<if test="dto.payTimeList != null and dto.payTimeList.length == 2">
and a.pay_time between #{dto.payTimeList[0]} and #{dto.payTimeList[1]}
</if>
<if test="dto.refundTimeList != null and dto.refundTimeList.length == 2">
and a.refund_time between #{dto.refundTimeList[0]} and #{dto.refundTimeList[1]}
</if>
</sql>
@@ -42,5 +63,4 @@
</where>
order by a.create_time desc, a.id desc
</select>
</mapper>
@@ -48,8 +48,8 @@ public interface RemoteUserTrustedDeviceService {
@NoToken
@GetMapping("/userTrustedDevice/getUserTrustedDeviceByIdAndMD5" )
R<UserTrustedDeviceEntity> getUserTrustedDeviceByIdAndMD5(@RequestParam("mobilePhone")String mobilePhone,@RequestParam("md5") String md5);
@GetMapping("/userTrustedDevice/getUserTrustedDeviceByIdAndMD5")
R<UserTrustedDeviceEntity> getUserTrustedDeviceByIdAndMD5(@RequestParam("mobilePhone") String mobilePhone, @RequestParam("md5") String md5, @RequestParam("deviceName") String deviceName);
@PostMapping("/userTrustedDevice/save")
@@ -104,14 +104,23 @@ public class SysUserFaceController {
/* ************************************* start android start ************************************* */
@Operation(summary = "android查询人脸数据库", description = "android查询人脸数据库")
@GetMapping("/ad/find-face")
public R adFinFace(@ParameterObject Page page, @RequestParam(required = false) Long userId,
@RequestParam(required = false) Long merchantDeptId) {
public R adFinFace(@ParameterObject Page page, @RequestParam(required = false) Long userId, @RequestParam(required = false) Long merchantDeptId) {
SysUserFaceDto dto = new SysUserFaceDto();
dto.setUserId(userId);
dto.setMerchantDeptIdOrGlobal(merchantDeptId);
dto.setProcessStatus(TaskCheckStatusEnum.RESULT.getCode());
return R.ok(sysUserFaceService.pageJoin(page, dto).getRecords());
}
@Operation(summary = "android查询人脸数据库", description = "android查询人脸数据库")
@GetMapping("/ad/v2/find-face")
public R adV2FinFace(@ParameterObject Page page, @RequestParam(required = false) Long userId, @RequestParam(required = false) Long merchantDeptId) {
SysUserFaceDto dto = new SysUserFaceDto();
dto.setUserId(userId);
dto.setMerchantDeptIdOrGlobal(merchantDeptId);
dto.setProcessStatus(TaskCheckStatusEnum.RESULT.getCode());
return R.ok(sysUserFaceService.allPageJoin(page, dto).getRecords());
}
/* ************************************* start android start ************************************* */
@Operation(summary = "设置为全局人脸", description = "设置为全局人脸")
@@ -30,22 +30,23 @@ import java.util.List;
*/
@RestController
@RequiredArgsConstructor
@RequestMapping("/userTrustedDevice" )
@Tag(description = "userTrustedDevice" , name = "设备指纹管理" )
@RequestMapping("/userTrustedDevice")
@Tag(description = "userTrustedDevice", name = "设备指纹管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
public class UserTrustedDeviceController {
private final UserTrustedDeviceService userTrustedDeviceService;
private final UserTrustedDeviceService userTrustedDeviceService;
/**
* 分页查询
* @param page 分页对象
*
* @param page 分页对象
* @param userTrustedDevice 设备指纹
* @return
*/
@Operation(summary = "分页查询" , description = "分页查询" )
@GetMapping("/page" )
@PreAuthorize("@pms.hasPermission('admin_userTrustedDevice_view')" )
@Operation(summary = "分页查询", description = "分页查询")
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin_userTrustedDevice_view')")
public R getUserTrustedDevicePage(@ParameterObject Page page, @ParameterObject UserTrustedDeviceEntity userTrustedDevice) {
LambdaQueryWrapper<UserTrustedDeviceEntity> wrapper = Wrappers.lambdaQuery();
return R.ok(userTrustedDeviceService.page(page, wrapper));
@@ -54,22 +55,24 @@ public class UserTrustedDeviceController {
/**
* 通过id查询设备指纹
*
* @param id id
* @return R
*/
@Operation(summary = "通过id查询" , description = "通过id查询" )
@GetMapping("/{id}" )
@PreAuthorize("@pms.hasPermission('admin_userTrustedDevice_view')" )
public R getById(@PathVariable("id" ) Long id) {
@Operation(summary = "通过id查询", description = "通过id查询")
@GetMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin_userTrustedDevice_view')")
public R getById(@PathVariable("id") Long id) {
return R.ok(userTrustedDeviceService.getById(id));
}
/**
* 新增设备指纹
*
* @param userTrustedDevice 设备指纹
* @return R
*/
@Operation(summary = "新增设备指纹" , description = "新增设备指纹" )
@Operation(summary = "新增设备指纹", description = "新增设备指纹")
@PostMapping("/save")
@Inner
public R save(@RequestBody UserTrustedDeviceEntity userTrustedDevice) {
@@ -78,11 +81,12 @@ public class UserTrustedDeviceController {
/**
* 修改设备指纹
*
* @param userTrustedDevice 设备指纹
* @return R
*/
@Operation(summary = "修改设备指纹" , description = "修改设备指纹" )
@SysLog("修改设备指纹" )
@Operation(summary = "修改设备指纹", description = "修改设备指纹")
@SysLog("修改设备指纹")
@PutMapping("/update")
@Inner
public R updateById(@RequestBody UserTrustedDeviceEntity userTrustedDevice) {
@@ -91,23 +95,23 @@ public class UserTrustedDeviceController {
/**
* 通过id删除设备指纹
*
* @param ids id列表
* @return R
*/
@Operation(summary = "通过id删除设备指纹" , description = "通过id删除设备指纹" )
@SysLog("通过id删除设备指纹" )
@Operation(summary = "通过id删除设备指纹", description = "通过id删除设备指纹")
@SysLog("通过id删除设备指纹")
@DeleteMapping
@PreAuthorize("@pms.hasPermission('admin_userTrustedDevice_del')" )
@PreAuthorize("@pms.hasPermission('admin_userTrustedDevice_del')")
public R removeById(@RequestBody Long[] ids) {
return R.ok(userTrustedDeviceService.removeBatchByIds(CollUtil.toList(ids)));
}
@Operation(summary = "通过md5和用户查询设备" , description = "通过md5和用户查询设备" )
@GetMapping("/getUserTrustedDeviceByIdAndMD5" )
@Operation(summary = "通过md5和用户查询设备", description = "通过md5和用户查询设备")
@GetMapping("/getUserTrustedDeviceByIdAndMD5")
@Inner
public R<UserTrustedDeviceEntity> getUserTrustedDeviceByIdAndMD5(@RequestParam("mobilePhone")String mobilePhone,@RequestParam("md5") String md5) {
return R.ok(userTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(mobilePhone,md5));
public R<UserTrustedDeviceEntity> getUserTrustedDeviceByIdAndMD5(@RequestParam("mobilePhone") String mobilePhone, @RequestParam("md5") String md5, @RequestParam("deviceName") String deviceName) {
return R.ok(userTrustedDeviceService.getUserTrustedDeviceByIdAndMD5(mobilePhone, md5, deviceName));
}
}
@@ -69,4 +69,7 @@ public interface SysUserFaceMapper extends PjcloudBaseMapper<SysUserFaceEntity>
}
return selectJoinPage(page, SysUserFaceVo.class, wrapper);
}
Page<SysUserFaceVo> allPageJoin(Page page, @Param("pa") SysUserFaceDto pa);
}
@@ -23,6 +23,8 @@ public interface SysUserFaceService extends IService<SysUserFaceEntity> {
Page<SysUserFaceVo> scopePageList(Page page, SysUserFaceDto dto, DataScope dataScope);
Page<SysUserFaceVo> allPageJoin(Page page, SysUserFaceDto dto);
Page<SysUserFaceVo> pageJoin(Page page, SysUserFaceDto dto);
SysUserFaceVo join(Long id);
@@ -8,12 +8,12 @@ public interface UserTrustedDeviceService extends IService<UserTrustedDeviceEnti
/**
* 新增
*
* @param userTrustedDevice
* @return
*/
Object saveUserTruste(UserTrustedDeviceEntity userTrustedDevice);
UserTrustedDeviceEntity getUserTrustedDeviceByIdAndMD5(String mobilePhone, String md5);
UserTrustedDeviceEntity getUserTrustedDeviceByIdAndMD5(String mobilePhone, String md, String deviceName);
}
@@ -176,6 +176,18 @@ public class SysUserFaceServiceImpl extends ServiceImpl<SysUserFaceMapper, SysUs
return list;
}
@Override
public Page<SysUserFaceVo> allPageJoin(Page page, SysUserFaceDto dto) {
if (ObjectUtil.isNotEmpty(dto.getMerchantDeptIdOrGlobal())) {
List<SysDept> deptList = sysDeptService.listDescendant(dto.getMerchantDeptIdOrGlobal());
if (ObjectUtil.isNotEmpty(deptList)) {
List<Long> deptIds = deptList.stream().map(SysDept::getDeptId).toList();
dto.setDeptIds(deptIds);
}
}
return baseMapper.allPageJoin(page, dto);
}
@Override
public Page<SysUserFaceVo> pageJoin(Page page, SysUserFaceDto dto) {
if (ObjectUtil.isNotEmpty(dto.getMerchantDeptIdOrGlobal())) {
@@ -8,6 +8,7 @@ import com.bsd.pjcloud.admin.service.UserTrustedDeviceService;
import com.bsd.pjcloud.common.core.util.DeviceFingerprintUtil;
import com.bsd.pjcloud.common.core.util.WebUtils;
import org.springframework.stereotype.Service;
/**
* 设备指纹
*
@@ -22,10 +23,11 @@ public class UserTrustedDeviceServiceImpl extends ServiceImpl<UserTrustedDeviceM
}
@Override
public UserTrustedDeviceEntity getUserTrustedDeviceByIdAndMD5(String mobilePhone, String md5) {
public UserTrustedDeviceEntity getUserTrustedDeviceByIdAndMD5(String mobilePhone, String md5, String deviceName) {
LambdaQueryWrapper<UserTrustedDeviceEntity> userTrustedDeviceEntityLambdaQueryWrapper = new LambdaQueryWrapper<>();
userTrustedDeviceEntityLambdaQueryWrapper.eq(UserTrustedDeviceEntity::getMobilePhone, mobilePhone);
userTrustedDeviceEntityLambdaQueryWrapper.eq(UserTrustedDeviceEntity::getDeviceFingerprint, md5);
userTrustedDeviceEntityLambdaQueryWrapper.eq(UserTrustedDeviceEntity::getDeviceName, deviceName);
return baseMapper.selectOne(userTrustedDeviceEntityLambdaQueryWrapper);
}
@@ -2,17 +2,6 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.admin.mapper.SysUserFaceMapper">
<resultMap id="sysUserFaceMap" type="com.bsd.pjcloud.admin.api.entity.SysUserFaceEntity">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="faceImg" column="face_img"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
<sql id="selectA">
select a.id,
@@ -35,9 +24,18 @@
<if test="pa.userId != null">
and a.user_id = #{pa.userId}
</if>
<if test="pa.userIds != null and pa.userIds.size() != 0">
and a.user_id in
<foreach collection="pa.userIds" item="v" open="(" close=")" separator=",">
#{v}
</foreach>
</if>
<if test="pa.processStatus != null">
and a.process_status = #{pa.processStatus}
</if>
<if test="pa.isProcessIdNotNull != null and pa.isProcessIdNotNull == true ">
and a.process_id is not null
</if>
</sql>
<select id="scopePageList" resultType="com.bsd.pjcloud.admin.api.vo.SysUserFaceVo">
@@ -49,14 +47,14 @@
u.dept_id as deptId,
d.name as deptName
from sys_user_face a
left join sys_user u on u.user_id = a.user_id and u.del_flag = '0'
left join sys_dept d on d.dept_id = u.dept_id and d.del_flag = '0'
inner join sys_user u on u.user_id = a.user_id and u.del_flag = '0'
inner join sys_dept d on d.dept_id = u.dept_id and d.del_flag = '0'
<where>
<include refid="whereA"/>
<if test="pa.username != null">
<if test="pa.username != null and pa.username != '' ">
and u.name LIKE CONCAT('%', #{pa.username}, '%')
</if>
<if test="pa.phone != null">
<if test="pa.phone != null and pa.phone != '' ">
and u.phone LIKE CONCAT('%', #{pa.phone}, '%')
</if>
</where>
@@ -81,4 +79,51 @@
</where>
GROUP BY a.user_id
</select>
<select id="allPageJoin" resultType="com.bsd.pjcloud.admin.api.vo.SysUserFaceVo">
<include refid="selectA"/>
u.name as username,
u.phone as userMobile,
u.avatar as userAvatar,
d.dept_id as deptId,
d.name as deptName
from sys_user_face a
inner join sys_user u on u.user_id = a.user_id and u.del_flag = '0'
inner join sys_dept d on d.dept_id = u.dept_id and d.del_flag = '0'
<where>
<if test="pa.userId != null">
and a.user_id = #{pa.userId}
</if>
<if test="pa.userIds != null and pa.userIds.size() != 0">
and a.user_id in
<foreach collection="pa.userIds" item="v" open="(" close=")" separator=",">
#{v}
</foreach>
</if>
<if test="pa.processStatus != null">
and a.process_status = #{pa.processStatus}
</if>
<if test="pa.isProcessIdNotNull != null and pa.isProcessIdNotNull == true ">
and a.process_id is not null
</if>
<if test="pa.deptIds != null and pa.deptIds.size() != 0">
<choose>
<when test="pa.isInclude != null and pa.isInclude == true">
and d.dept_id IN
</when>
<when test="pa.isInclude != null and pa.isInclude == false">
and d.dept_id NOT IN
</when>
<otherwise>
and d.dept_id IN
</otherwise>
</choose>
<foreach collection="pa.deptIds" item="v" open="(" close=")" separator=",">
#{v}
</foreach>
</if>
</where>
order by a.create_time desc, a.id desc
</select>
</mapper>
+81 -1
View File
@@ -12,7 +12,11 @@
<artifactId>pjcloud-wallet-api</artifactId>
<dependencies>
<properties>
<kotlin.version>2.2.20</kotlin.version>
</properties>
<dependencies>
<!-- 连表查询注解 -->
<dependency>
<groupId>com.github.yulichang</groupId>
@@ -52,5 +56,81 @@
<groupId>com.bsd</groupId>
<artifactId>pjcloud-upms-api</artifactId>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib-jdk8</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<sourceDirs>
<sourceDir>src/main/java</sourceDir>
<sourceDir>target/generated-sources/annotations</sourceDir>
</sourceDirs>
</configuration>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
<configuration>
<jvmTarget>1.8</jvmTarget>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<executions>
<execution>
<id>default-compile</id>
<phase>none</phase>
</execution>
<execution>
<id>default-testCompile</id>
<phase>none</phase>
</execution>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>testCompile</id>
<phase>test-compile</phase>
<goals>
<goal>testCompile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -19,6 +19,11 @@ public enum WalletBizRechargeEnum {
private final String desc;
public static List<WalletBizRechargeEnum> reductionList = List.of(
REDUCTION,
RETURN_CANTEEN
);
public static List<String> reductionCodes() {
return Arrays.asList(REDUCTION.getCode(), RETURN_CANTEEN.getCode());
}
@@ -4,7 +4,6 @@ import com.bsd.pjcloud.admin.api.vo.base.BaseIsIncludeTreeInterface;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@@ -27,4 +26,9 @@ public class WalletManageDto extends WalletManageEntity implements BaseIsInclude
*/
private Boolean isInclude;
/**
* 钱包余额大于等于
*/
private Integer leBalance;
}
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.wallet.api.dto.recharge;
import lombok.Data;
@Data
public class WalletRechargePaySuccess {
/**
* 订单号
*/
private String payNo;
/**
* 用户id
*/
private Long userId;
/**
* 业务类型
*/
private String bizType;
/**
* 业务id
*/
private String bizId;
}
@@ -0,0 +1,28 @@
package com.bsd.pjcloud.wallet.api.dto.recharge;
import lombok.Data;
@Data
public class WalletRechargeRefundSuccess {
/**
* 订单号
*/
private String refundNo;
/**
* 用户id
*/
private Long userId;
/**
* 业务类型
*/
private String bizType;
/**
* 业务id
*/
private String bizId;
}
@@ -95,11 +95,19 @@ public class WalletManageEntity extends Model<WalletManageEntity> {
/**
* 饭卡绑定状态
*
* @see WalleCardStatusEnum
*/
@Schema(description = "饭卡绑定状态")
private String status;
/**
* 版本号
*/
@Version
@Schema(description = "版本号")
private Integer version = 0;
/**
* 创建人
*/
@@ -78,6 +78,13 @@ public class WalletTransactionEntity extends Model<WalletTransactionEntity> {
@Schema(description = "余额, 单位分")
private Integer balance;
/**
* 乐观锁
*/
@Version
@Schema(description = "乐观锁")
private Integer version = 0;
/**
* 创建人
*/
@@ -2,10 +2,15 @@ package com.bsd.pjcloud.wallet.api.feign;
import com.bsd.pjcloud.common.core.constant.ServiceNameConstants;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.feign.annotation.NoToken;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePaySuccess;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundSuccess;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargePackageEntity;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(contextId = "remoteWalletRechargePackageService", value = ServiceNameConstants.WALLET_SERVICE)
public interface RemoteWalletRechargePackageService {
@@ -13,4 +18,11 @@ public interface RemoteWalletRechargePackageService {
@GetMapping("/wallet-recharge-package/remote/{id}")
R<WalletRechargePackageEntity> remoteGetById(@PathVariable("id") Long id);
@NoToken
@PostMapping("/wallet-recharge-package/remote/pay-success")
R<Boolean> remotePaySuccess(@RequestBody WalletRechargePaySuccess dto);
@NoToken
@PostMapping("/wallet-recharge-package/remote/refund-success")
R<Boolean> remoteRefundSuccess(@RequestBody WalletRechargeRefundSuccess dto);
}
@@ -43,6 +43,11 @@ public class WalletXfjDiscounts {
*/
private Double extraPer;
/**
* 特色菜是否额外收费
*/
private String specialDishEnable;
public int computedDiscountsExtraPrice(int totalPrice) {
if (totalPrice <= basePrice) return 0;
double rate = ObjectUtil.isEmpty(extraPer) ? 1.0 : extraPer;
@@ -0,0 +1,52 @@
package com.bsd.pjcloud.wallet.api.vo.recharge;
import com.bsd.pjcloud.admin.api.vo.base.BaseUserInterface;
import lombok.Data;
import java.util.List;
@Data
public class WalletRechargeValidateErrorVo implements BaseUserInterface {
/**
* 用户相关
*/
private Long userId;
private String username;
private String userMobile;
private String userAvatar;
/**
* 饭卡类型id
*/
private Long walletTypeId;
/**
* 钱包余额
*/
private Integer walletBlance;
/**
* 操作类型
*/
private String bizType;
/**
* 操作金额
*/
private Integer price;
/**
* 操作后的金额
*/
private Integer afterPrice;
/**
* 错误信息
*/
private List<String> errorList;
}
@@ -13,6 +13,12 @@
<artifactId>pjcloud-wallet-biz</artifactId>
<dependencies>
<!-- 分布式锁 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>lock4j-redis-template-spring-boot-starter</artifactId>
<version>2.2.7</version>
</dependency>
<!--引入分布式事务-->
<dependency>
<groupId>com.bsd</groupId>
@@ -36,9 +36,6 @@ import java.util.List;
/**
* 钱包管理
*
* @author dzc
* @date 2024-12-30 09:06:35
*/
@RestController
@RequiredArgsConstructor
@@ -207,6 +204,14 @@ public class WalletManageController {
PjcloudUser user = SecurityUtils.getUser();
return R.ok(walletManageService.isUseDoor(user.getId()));
}
@GetMapping("/app/topup-wallet")
@Operation(summary = "创建充值钱包")
public R<String> createTopupWallet() {
PjcloudUser user = SecurityUtils.getUser();
walletManageService.createWallet(user.getId());
return R.ok();
}
/* ************************************* end app end ************************************* */
@Operation(summary = "分页查询", description = "分页查询")
@@ -37,9 +37,6 @@ import java.util.concurrent.TimeUnit;
/**
* 会员钱包流水表
*
* @author pjcloud
* @date 2024-12-30 09:17:49
*/
@RestController
@RequiredArgsConstructor
@@ -20,6 +20,7 @@ import com.bsd.pjcloud.wallet.api.dto.recharge.excel.WalletRechargeExcelDto;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargeApplyEntity;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeApplyCompareAceVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeImportVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeValidateErrorVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.excel.WalletRechargeByApplyExcelVo;
import com.bsd.pjcloud.wallet.service.WalletRechargeApplyService;
import com.bsd.pjcloud.wallet.service.WalletRechargeApplyComposeService;
@@ -55,6 +56,12 @@ public class WalletRechargeApplyController {
}
/* ************************************* start callback start ************************************* */
@GetMapping("/validate-error")
@Operation(summary = "校验申请的充值/扣款数据当前是否合法")
public R<List<WalletRechargeValidateErrorVo>> validateError(@RequestParam Long applyId) {
return R.ok(walletRechargeApplyService.validateError(applyId));
}
@PostMapping("/save-topup")
@Operation(summary = "新增充值")
@Idempotent(expireTime = 3, key = "'/wallet-recharge-apply/save-topup' + #dto.getInvoiceId()", info = "正在充值中,请勿重复操作")
@@ -79,7 +86,7 @@ public class WalletRechargeApplyController {
@SysLog("提交申请")
@GetMapping("/apply")
@PreAuthorize("@pms.hasPermission('wallet_walletRechargeApply_apply')")
@Idempotent(expireTime = 5, info = "正在申请中,请勿重复操作")
@Idempotent(expireTime = 5, key = "'/wallet-recharge-apply/apply/' + #id", info = "正在申请中,请勿重复操作")
public R apply(@RequestParam Long id) {
walletRechargeApplyService.apply(id);
return R.ok(true);
@@ -166,13 +173,13 @@ public class WalletRechargeApplyController {
}
@PostMapping("/read-excel")
@Idempotent(expireTime = 3, info = "正在上传中,请勿重复操作")
@Idempotent(expireTime = 3, key = "'/wallet-recharge-apply/read-excel'", info = "正在上传中,请勿重复操作")
public R readExcel(@RequestExcel List<WalletRechargeExcelDto> excelVOList, BindingResult bindingResult, @RequestParam(required = false) String bizType) {
return walletRechargeApplyService.readExcel(excelVOList, bindingResult, bizType);
}
@PostMapping("/read-topup-excel")
@Idempotent(expireTime = 3, info = "正在上传中,请勿重复操作")
@Idempotent(expireTime = 3, key = "'/wallet-recharge-apply/read-topup-excel'", info = "正在上传中,请勿重复操作")
public R readTopupExcel(@RequestExcel List<WalletRechargeExcelDto> excelVOList, BindingResult bindingResult) {
return walletRechargeApplyService.readTopupExcel(excelVOList, bindingResult);
}
@@ -1,21 +1,14 @@
package com.bsd.pjcloud.wallet.controller.recharge;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.admin.api.entity.SysUser;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.idempotent.annotation.Idempotent;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.common.security.service.PjcloudUser;
import com.bsd.pjcloud.common.security.util.SecurityUtils;
import com.bsd.pjcloud.flow.task.constant.TaskCheckStatusEnum;
import com.bsd.pjcloud.flow.task.dto.ProcessNodeRecordAssignUserParamDto;
import com.bsd.pjcloud.wallet.api.constant.WalletInvoiceStatusCheckEnum;
import com.bsd.pjcloud.wallet.api.dto.WalletManageBalanceDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeBindInvoiceDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeInvoiceApplyDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeInvoiceRefreshDto;
@@ -34,8 +27,6 @@ import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 充值/扣款申请发票记录
*/
@@ -59,7 +50,7 @@ public class WalletRechargeInvoiceController {
@Operation(summary = "重新生成发票", description = "重新生成发票")
@SysLog("重新生成发票")
@PostMapping("/refreshInvoice")
@Idempotent(expireTime = 10, info = "正在重新生成发票中,请勿重复操作")
@Idempotent(expireTime = 3, key = "'/walletRechargeInvoice/refreshInvoice/' + #dto.getId()", info = "正在重新生成发票中,请勿重复操作")
public R refreshInvoice(@Valid @RequestBody WalletRechargeInvoiceRefreshDto dto) {
walletRechargeInvoiceService.refreshInvoice(dto);
return R.ok(true);
@@ -68,7 +59,7 @@ public class WalletRechargeInvoiceController {
@Operation(summary = "提交申请", description = "提交申请")
@SysLog("提交申请")
@PostMapping("/apply")
@Idempotent(expireTime = 3, info = "正在申请中,请勿重复操作")
@Idempotent(expireTime = 3, key = "'/walletRechargeInvoice/apply/' + #body.getId()", info = "正在申请中,请勿重复操作")
public R apply(@Valid @RequestBody WalletRechargeInvoiceApplyDto body) {
walletRechargeInvoiceService.apply(body);
return R.ok(true);
@@ -83,6 +74,7 @@ public class WalletRechargeInvoiceController {
@GetMapping("/back")
@Operation(summary = "退回作废发票")
@Idempotent(expireTime = 3, key = "'/walletRechargeInvoice/back/' + #id", info = "正在退回中,请勿重复操作")
public R backInvoice(@RequestParam("id") Long id) {
walletRechargeInvoiceService.backInvoice(id);
return R.ok();
@@ -90,6 +82,7 @@ public class WalletRechargeInvoiceController {
@PostMapping("/bind-invoice")
@Operation(summary = "绑定发票")
@Idempotent(expireTime = 3, key = "'/walletRechargeInvoice/bind-invoice/' + #dto.getApplyId()", info = "正在申请中,请勿重复操作")
public R bindInvoice(@Valid @RequestBody WalletRechargeBindInvoiceDto dto) {
walletRechargeInvoiceService.bindInvoice(dto);
return R.ok();
@@ -7,7 +7,10 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.log.annotation.SysLog;
import com.bsd.pjcloud.common.security.annotation.Inner;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePackageDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePaySuccess;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundSuccess;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargePackageEntity;
import com.bsd.pjcloud.wallet.service.WalletRechargePackageService;
import org.springframework.security.access.prepost.PreAuthorize;
@@ -24,9 +27,6 @@ import java.util.List;
/**
* 充值套餐表
*
* @author dzc
* @date 2025-01-03 18:33:55
*/
@RestController
@RequiredArgsConstructor
@@ -43,13 +43,30 @@ public class WalletRechargePackageController {
public R<WalletRechargePackageEntity> remoteGetById(@PathVariable("id") Long id) {
return R.ok(walletRechargePackageService.getById(id));
}
@Inner
@Operation(summary = "支付成功", description = "支付成功")
@PostMapping("/remote/pay-success")
public R<Boolean> remotePaySuccess(@RequestBody WalletRechargePaySuccess dto) {
return R.ok(walletRechargePackageService.remotePaySuccess(dto));
}
@Inner
@Operation(summary = "退款成功", description = "退款成功")
@PostMapping("/remote/refund-success")
public R<Boolean> remoteRefundSuccess(@RequestBody WalletRechargeRefundSuccess dto) {
return R.ok(walletRechargePackageService.remoteRefundSuccess(dto));
}
/* ************************************* end remote end ************************************* */
/* ************************************* start app start ************************************* */
@Operation(summary = "查询全部", description = "查询全部")
@GetMapping("/app/list")
public R appList() {
return R.ok(walletRechargePackageService.list(new LambdaQueryWrapper<>()));
LambdaQueryWrapper<WalletRechargePackageEntity> wrapper = Wrappers.lambdaQuery();
wrapper.orderByAsc(WalletRechargePackageEntity::getPayPrice);
wrapper.orderByDesc(WalletRechargePackageEntity::getCreateTime);
return R.ok(walletRechargePackageService.list(wrapper));
}
/* ************************************* end app end ************************************* */
@@ -58,6 +75,7 @@ public class WalletRechargePackageController {
@PreAuthorize("@pms.hasPermission('wallet_walletRechargePackage_view')")
public R getWalletRechargePackagePage(@ParameterObject Page page, @ParameterObject WalletRechargePackageEntity walletRechargePackage) {
LambdaQueryWrapper<WalletRechargePackageEntity> wrapper = Wrappers.lambdaQuery();
wrapper.orderByAsc(WalletRechargePackageEntity::getPayPrice);
wrapper.orderByDesc(WalletRechargePackageEntity::getCreateTime);
return R.ok(walletRechargePackageService.page(page, wrapper));
}
@@ -40,9 +40,10 @@ public interface WalletManageMapper extends PjcloudBaseMapper<WalletManageEntity
ObjectUtil.isNotEmpty(dto.getCreateTimeList()) ? dto.getCreateTimeList()[0] : null,
ObjectUtil.isNotEmpty(dto.getCreateTimeList()) ? dto.getCreateTimeList()[1] : null
)
.orderByDesc(WalletManageEntity::getCreateTime)
.le(ObjectUtil.isNotEmpty(dto.getLeBalance()), WalletManageEntity::getBalance, dto.getLeBalance())
.orderByDesc(WalletManageEntity::getUserId)
.orderByDesc(WalletManageEntity::getWalletTypeId);
.orderByDesc(WalletManageEntity::getWalletTypeId)
.orderByDesc(WalletManageEntity::getCreateTime);
if (ObjectUtil.isNotEmpty(dto.getIsInclude())) {
if (dto.getIsInclude()) {
wrapper.in(ObjectUtil.isNotEmpty(dto.getDeptIds()), WalletManageEntity::getDeptId, dto.getDeptIds());
@@ -64,7 +65,7 @@ public interface WalletManageMapper extends PjcloudBaseMapper<WalletManageEntity
*/
default int updateWhenConsumption(Long id, Integer price, Boolean force) {
LambdaUpdateWrapper<WalletManageEntity> lambdaUpdateWrapper = new LambdaUpdateWrapper<WalletManageEntity>()
.setSql(" balance = balance - " + price + ", total_expense = total_expense + " + price)
.setSql(" balance = balance - " + price + ", total_expense = total_expense + " + price + ", version = version + 1")
.eq(WalletManageEntity::getId, id);
if (ObjectUtil.isEmpty(force) || !force) {
lambdaUpdateWrapper.ge(WalletManageEntity::getBalance, price);
@@ -80,7 +81,7 @@ public interface WalletManageMapper extends PjcloudBaseMapper<WalletManageEntity
*/
default int updateWhenConsumptionRefund(Long id, Integer price) {
LambdaUpdateWrapper<WalletManageEntity> lambdaUpdateWrapper = new LambdaUpdateWrapper<WalletManageEntity>()
.setSql(" balance = balance + " + price + ", total_expense = total_expense - " + price)
.setSql(" balance = balance + " + price + ", total_expense = total_expense - " + price + ", version = version + 1")
.eq(WalletManageEntity::getId, id);
return update(null, lambdaUpdateWrapper);
}
@@ -91,10 +92,11 @@ public interface WalletManageMapper extends PjcloudBaseMapper<WalletManageEntity
* @param id 钱包 id
* @param price 钱包金额
*/
default int updateWhenRecharge(Long id, Integer price) {
default int updateWhenRecharge(Long id, Integer price, Integer version) {
LambdaUpdateWrapper<WalletManageEntity> lambdaUpdateWrapper = new LambdaUpdateWrapper<WalletManageEntity>()
.setSql(" balance = balance + " + price + ", total_recharge = total_recharge + " + price)
.eq(WalletManageEntity::getId, id);
.setSql(" balance = balance + " + price + ", total_recharge = total_recharge + " + price + ", version = version + 1")
.eq(WalletManageEntity::getId, id)
.eq(WalletManageEntity::getVersion, version);
return update(null, lambdaUpdateWrapper);
}
@@ -4,21 +4,16 @@ import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.core.util.BeanUtils;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.data.datascope.PjcloudBaseMapper;
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionDto;
import com.bsd.pjcloud.wallet.api.dto.WalletTransactionSumAndCountDto;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletTypeEntity;
import com.bsd.pjcloud.wallet.api.vo.WalletExportCzTotalPerson;
import com.bsd.pjcloud.wallet.api.vo.WalletTransactionSumAndCountVo;
import com.bsd.pjcloud.wallet.api.vo.WalletTypeCountVo;
import com.bsd.pjcloud.wallet.api.vo.WalletTypeTotalVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeApplySelectCompareVo;
import com.bsd.pjcloud.wallet.api.vo.transaction.WalletTransactionVo;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -65,25 +60,16 @@ public interface WalletTransactionMapper extends PjcloudBaseMapper<WalletTransac
return wrapper;
}
default Page<WalletTransactionVo> scopePageList(Page page, WalletTransactionDto dto, DataScope dataScope) {
Page list = selectPageByScope(page, commonWrapperQuery(dto), dataScope);
list.setRecords(BeanUtils.toBean(list.getRecords(), WalletTransactionVo.class));
return list;
}
Page<WalletTransactionVo> scopePageList(Page page, @Param("dto") WalletTransactionDto dto, DataScope dataScope);
List<WalletTransactionVo> scopeList(@Param("dto") WalletTransactionDto dto, DataScope dataScope);
Long count(@Param("dto") WalletTransactionDto dto, DataScope dataScope);
WalletTypeCountVo walletTypetotal(@Param("dto") WalletTransactionDto dto, @Param("dataScope") DataScope dataScope);
List<WalletTypeCountVo> walletTypetotalByDept(@Param("dto") WalletTransactionDto dto, @Param("dataScope") DataScope dataScope);
default List<WalletTransactionVo> scopeList(WalletTransactionDto dto, DataScope dataScope) {
List<WalletTransactionEntity> list = selectListByScope(commonWrapperQuery(dto), dataScope);
return BeanUtils.toBean(list, WalletTransactionVo.class);
}
default Long count(WalletTransactionDto dto, DataScope dataScope) {
return selectCountByScope(commonWrapperQuery(dto), dataScope);
}
Page<WalletTransactionVo> scopeJzPageList(Page page, @Param("dto") WalletTransactionDto dto, DataScope dataScope);
Integer getTransactionPriceCount(@Param("month") String importTimeStr, @Param("walletId") Long walletId,
@@ -12,6 +12,7 @@ import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeSaveTopupDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.excel.WalletRechargeExcelDto;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargeApplyEntity;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeApplyVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeValidateErrorVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.excel.WalletRechargeByApplyExcelVo;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.validation.BindingResult;
@@ -72,4 +73,9 @@ public interface WalletRechargeApplyService extends IService<WalletRechargeApply
* 导出充值/扣款申请
*/
void export(HttpServletResponse response, WalletRechargeApplyDto dto);
/**
* 校验申请的充值/扣款数据当前是否合法
*/
List<WalletRechargeValidateErrorVo> validateError(Long applyId);
}
@@ -3,15 +3,28 @@ package com.bsd.pjcloud.wallet.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePackageDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePaySuccess;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundSuccess;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargePackageEntity;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargePackageVo;
public interface WalletRechargePackageService extends IService<WalletRechargePackageEntity> {
/**
* 支付成功
*/
Boolean remotePaySuccess(WalletRechargePaySuccess dto);
/**
* 退款成功
*/
Boolean remoteRefundSuccess(WalletRechargeRefundSuccess dto);
Page<WalletRechargePackageVo> pageJoin(Page page, WalletRechargePackageDto dto);
/**
* 校验是否有效
*
* @param packageId
* @return
*/
@@ -17,25 +17,16 @@ public interface WalletRechargeService extends IService<WalletRechargeEntity> {
/**
* app充值
*
* @param dto
* @return
*/
WalletRechargeEntity createWalletRecharge(WalletAppRechargeDto dto);
/**
* 远程调用-退款
*
* @param dto
* @return
*/
WalletRechargeVo refund(WalletRechargeRefundDto dto);
/**
* 根据退款id查询
*
* @return
*/
WalletRechargeEntity getByRefundId(Long refundId);
}
@@ -38,32 +38,16 @@ public interface WalletTransactionService extends IService<WalletTransactionEnti
/**
* 扣减钱包余额
*
* @param walletId 钱包 id
* @param price 扣减金额
* @param bizId 业务关联 id
* @param bizType 业务关联分类
* @return 钱包流水
*/
WalletTransactionEntity reduceWalletBalance(Long walletId, Integer price, String bizId, WalletBizEnum bizType, Boolean force);
/**
* 增加钱包余额
*
* @param walletId 钱包 id
* @param price 增加金额
* @param bizId 业务关联 id
* @param bizType 业务关联分类
* @return 钱包流水
*/
WalletTransactionEntity addWalletBalance(Long walletId, Integer price, String bizId, WalletBizEnum bizType);
/**
* 更新会员用户余额
*
* @param dto
* @return
*/
Boolean updateWalletBalance(WalletTransactionDto dto);
@@ -29,7 +29,6 @@ import com.bsd.pjcloud.common.core.util.CollectionUtils;
import com.bsd.pjcloud.common.core.util.R;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.common.data.resolver.ParamResolver;
import com.bsd.pjcloud.common.data.tenant.TenantContextHolder;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.dto.WalletManageBalanceDto;
import com.bsd.pjcloud.wallet.api.dto.WalletManageDto;
@@ -59,14 +58,9 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* 钱包管理
*
* @author dzc
* @date 2024-12-30 09:06:35
*/
@Validated
@Slf4j
@@ -95,6 +95,7 @@ public class WalletSettingServiceImpl extends ServiceImpl<WalletSettingMapper, W
entity.setType(WalletXfjDiscountsEnum.JZ_DISCOUNT.getCode());
entity.setExtraPer(item.getExtraPer());
entity.setBasePrice(item.getBasePrice());
entity.setSpecialDishEnable(item.getSpecialDishEnable());
return entity;
}).toList()
);
@@ -10,6 +10,8 @@ import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.fastjson.JSON;
import com.baomidou.lock.annotation.Lock4j;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.admin.api.entity.SysDept;
@@ -127,6 +129,7 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
insetObj.setBizId(String.valueOf(bizId));
insetObj.setBizType(bizType.getCode());
insetObj.setTitle(bizType.getDesc());
insetObj.setVersion(wallet.getVersion());
save(insetObj);
return insetObj;
}
@@ -134,6 +137,7 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
@Override
@Transactional(rollbackFor = Exception.class)
// @CacheEvict(value = CacheConstants.WALLET_DETAIL, key = "#walletId")
@Lock4j(keys = {"'add_wallet_balance'", "#walletId", "#bizId"}, acquireTimeout = 5000, expire = 60000)
public WalletTransactionEntity addWalletBalance(Long walletId, Integer price, String bizId, WalletBizEnum bizType) {
WalletManageEntity wallet = walletManageService.getById(walletId);
if (ObjectUtil.isEmpty(wallet)) {
@@ -143,7 +147,7 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
switch (bizType) {
case UPDATE:
case RECHARGE, BATCH, TRANSFER_ACCOUNTS: {
walletManageMapper.updateWhenRecharge(wallet.getId(), price);
walletManageMapper.updateWhenRecharge(wallet.getId(), price, wallet.getVersion());
break;
}
case REFUND, TRANSFER_ACCOUNTS_REFUND, RETURN_CANTEEN: {
@@ -164,6 +168,7 @@ public class WalletTransactionServiceImpl extends ServiceImpl<WalletTransactionM
insetObj.setBizId(String.valueOf(bizId));
insetObj.setBizType(bizType.getCode());
insetObj.setTitle(bizType.getDesc());
insetObj.setVersion(wallet.getVersion());
save(insetObj);
return insetObj;
}
@@ -57,6 +57,7 @@ import com.bsd.pjcloud.wallet.api.utils.WalletDataUtil;
import com.bsd.pjcloud.wallet.api.vo.WalletManageVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeApplyVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeImportVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargeValidateErrorVo;
import com.bsd.pjcloud.wallet.api.vo.recharge.excel.WalletRechargeByApplyExcelVo;
import com.bsd.pjcloud.wallet.mapper.WalletManageMapper;
import com.bsd.pjcloud.wallet.mapper.WalletTransactionMapper;
@@ -554,6 +555,66 @@ public class WalletRechargeApplyServiceImpl extends ServiceImpl<WalletRechargeAp
return result;
}
/**
* 校验申请的充值/扣款数据当前是否合法
*/
@Override
public List<WalletRechargeValidateErrorVo> validateError(Long applyId) {
List<WalletRechargeValidateErrorVo> resultList = new ArrayList<>();
WalletRechargeApplyEntity detail = baseMapper.selectById(applyId);
if (ObjectUtil.isEmpty(detail)) return null;
// 校验判断回不回出现扣减之后为负数的情况
List<String> reductionList = WalletBizRechargeEnum.reductionList.stream().map(WalletBizRechargeEnum::getCode).toList();
if (reductionList.contains(detail.getBizType())) {
List<WalletRechargeImportEntity> importList = rechargeImportService.list(new LambdaQueryWrapper<WalletRechargeImportEntity>().eq(WalletRechargeImportEntity::getApplyId, applyId));
if (ObjectUtil.isEmpty(importList)) {
return null;
}
List<Long> userIds = importList.stream().map(WalletRechargeImportEntity::getUserId).filter(ObjectUtil::isNotEmpty).distinct().toList();
// 查询现在这些用户的钱包余额
WalletManageDto walletQueryDto = new WalletManageDto();
walletQueryDto.setWalletTypeId(detail.getWalletTypeId());
walletQueryDto.setUserIds(userIds);
List<WalletManageEntity> walletList = walletManageMapper.list(walletQueryDto);
// 判断每一个人的每一个钱包余额是否满足扣减
for (WalletManageEntity wallet : walletList) {
List<WalletRechargeImportEntity> list = importList.stream().filter(v -> ObjectUtil.equals(v.getUserId(), wallet.getUserId())).toList();
if (ObjectUtil.isEmpty(list)) continue;
Integer reductionPrice = list.stream().map(WalletRechargeImportEntity::getPrice).reduce(Integer::sum).orElse(0);
int price = wallet.getBalance() - reductionPrice;
if (price < 0) {
WalletRechargeValidateErrorVo item = new WalletRechargeValidateErrorVo();
item.setUserId(wallet.getUserId());
item.setBizType(detail.getBizType());
item.setWalletTypeId(detail.getWalletTypeId());
item.setWalletBlance(wallet.getBalance());
item.setPrice(reductionPrice);
item.setAfterPrice(price);
item.setErrorList(new ArrayList<String>() {{
add("钱包余额不足以扣款,请尽快联系该用户");
}});
resultList.add(item);
}
}
}
// 处理`resultList`结果
if (ObjectUtil.isNotEmpty(resultList)) {
List<Long> userIds = resultList.stream().map(WalletRechargeValidateErrorVo::getUserId).filter(ObjectUtil::isNotEmpty).distinct().toList();
List<SysUser> userList = userService.getInnerUserById(userIds).getData();
if (ObjectUtil.isNotEmpty(userList)) {
DataUtil.convertUserList(resultList, CollectionUtils.convertMap(userList, SysUser::getUserId));
}
}
return resultList;
}
/**
* 导出充值/扣款申请
*/
@@ -1,25 +1,113 @@
package com.bsd.pjcloud.wallet.service.impl.recharge;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bsd.pjcloud.common.core.constant.enums.YesNoEnum;
import com.bsd.pjcloud.thirdparty.constant.pay.ThirdPayOrderBizTypeEnum;
import com.bsd.pjcloud.wallet.api.constant.WalletBizEnum;
import com.bsd.pjcloud.wallet.api.constant.WalletTypeEnum;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePackageDto;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargePaySuccess;
import com.bsd.pjcloud.wallet.api.dto.recharge.WalletRechargeRefundSuccess;
import com.bsd.pjcloud.wallet.api.entity.WalletManageEntity;
import com.bsd.pjcloud.wallet.api.entity.WalletTransactionEntity;
import com.bsd.pjcloud.wallet.api.entity.recharge.WalletRechargePackageEntity;
import com.bsd.pjcloud.wallet.api.vo.recharge.WalletRechargePackageVo;
import com.bsd.pjcloud.wallet.mapper.recharge.WalletRechargePackageMapper;
import com.bsd.pjcloud.wallet.service.WalletManageService;
import com.bsd.pjcloud.wallet.service.WalletRechargePackageService;
import com.bsd.pjcloud.wallet.service.WalletTransactionService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 充值套餐表
*
* @author dzc
* @date 2025-01-03 18:33:55
* 充值套餐
*/
@Slf4j
@Service
@AllArgsConstructor
public class WalletRechargePackageServiceImpl extends ServiceImpl<WalletRechargePackageMapper, WalletRechargePackageEntity> implements WalletRechargePackageService {
static private final String TAG = "充值套餐";
private final WalletManageService walletManageService;
private final WalletTransactionService walletTransactionService;
/**
* 支付成功的回调
*/
@Override
public Boolean remotePaySuccess(WalletRechargePaySuccess dto) {
// 小程序用户钱包充值
if (ObjectUtil.equals(dto.getBizType(), ThirdPayOrderBizTypeEnum.MINI_APP_WALLET_RECHARGE.getType())) {
// 查询充值套餐
WalletRechargePackageEntity rechargePackage = baseMapper.selectById(dto.getBizId());
if (ObjectUtil.isEmpty(rechargePackage)) {
log.error("{}支付成功的回调,充值套餐不存在,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
// 需要充值的金额
Integer totalPrice = rechargePackage.getPayPrice() + rechargePackage.getBonusPrice();
if (totalPrice <= 0) {
log.error("{}支付成功的回调,充值套餐金额<=0,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
// 查询这个用户的充值钱包
WalletManageEntity topupWallet = walletManageService.getWallet(dto.getUserId(), WalletTypeEnum.TOPUP);
if (ObjectUtil.isEmpty(topupWallet)) {
log.error("{}支付成功的回调,查询这个用户的充值钱包,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
// 进行充值
WalletTransactionEntity walletTransaction = walletTransactionService.addWalletBalance(topupWallet.getId(), totalPrice, dto.getPayNo(), WalletBizEnum.RECHARGE);
return ObjectUtil.isNotEmpty(walletTransaction);
}
// 不支持的类型
else {
log.error("{}支付成功的回调,不支持的类型,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
}
/**
* 退款成功的回调
*/
@Override
public Boolean remoteRefundSuccess(WalletRechargeRefundSuccess dto) {
// 小程序用户钱包充值
if (ObjectUtil.equals(dto.getBizType(), ThirdPayOrderBizTypeEnum.MINI_APP_WALLET_RECHARGE.getType())) {
// 查询充值套餐
WalletRechargePackageEntity rechargePackage = baseMapper.selectById(dto.getBizId());
if (ObjectUtil.isEmpty(rechargePackage)) {
log.error("{}退款成功的回调,充值套餐不存在,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
// 需要退款的金额
Integer totalPrice = rechargePackage.getPayPrice() + rechargePackage.getBonusPrice();
if (totalPrice <= 0) {
log.error("{}退款成功的回调,充值套餐金额<=0,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
// 查询这个用户的充值钱包
WalletManageEntity topupWallet = walletManageService.getWallet(dto.getUserId(), WalletTypeEnum.TOPUP);
if (ObjectUtil.isEmpty(topupWallet)) {
log.error("{}退款成功的回调,查询这个用户的充值钱包,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
// 进行扣款预约扣款为负数
WalletTransactionEntity walletTransaction = walletTransactionService.reduceWalletBalance(topupWallet.getId(), totalPrice, dto.getRefundNo(), WalletBizEnum.REFUND, true);
return ObjectUtil.isNotEmpty(walletTransaction);
}
// 不支持的类型
else {
log.error("{}退款成功的回调,不支持的类型,dto:{}", TAG, JSONUtil.toJsonStr(dto));
return false;
}
}
@Override
public Page<WalletRechargePackageVo> pageJoin(Page page, WalletRechargePackageDto dto) {
return baseMapper.pageJoin(page, dto);
@@ -2,25 +2,6 @@
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.wallet.mapper.WalletManageMapper">
<resultMap id="walletManageMap" type="com.bsd.pjcloud.wallet.api.entity.WalletManageEntity">
<id property="id" column="id"/>
<result property="userId" column="user_id"/>
<result property="walletTypeId" column="wallet_type_id"/>
<result property="cardNumber" column="card_number"/>
<result property="balance" column="balance"/>
<result property="totalExpense" column="total_expense"/>
<result property="totalRecharge" column="total_recharge"/>
<result property="freezePrice" column="freeze_price"/>
<result property="isFreeze" column="is_freeze"/>
<result property="activeBindTime" column="active_bind_time"/>
<result property="status" column="active_bind_status"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="delFlag" column="del_flag"/>
<result property="tenantId" column="tenant_id"/>
</resultMap>
<sql id="selectA">
select a.id,
@@ -35,6 +16,7 @@
a.is_freeze,
a.active_bind_time,
a.status,
a.version,
a.create_by,
a.create_time,
a.update_by,
@@ -22,12 +22,13 @@
a.title,
a.price,
a.balance,
a.version,
a.create_by,
a.create_time,
a.update_by,
a.update_time,
a.del_flag,
a.tenant_id,
a.tenant_id
</sql>
<sql id="whereA">
@@ -102,7 +103,7 @@
</sql>
<select id="scopeJzPageList" resultType="com.bsd.pjcloud.wallet.api.vo.transaction.WalletTransactionVo">
<include refid="selectA"/>
<include refid="selectA"/>,
y.process_id as processId
from wallet_transaction a
inner join wallet_recharge_import i on i.id = a.biz_id and i.del_flag = '0'
@@ -282,4 +283,44 @@
and t.dept_id not in (1875013738914156546, 1910218102049226753, 1910218128624336898, 1942046316044144642)
order by v.mchId, v.deptId
</select>
<select id="scopePageList" resultType="com.bsd.pjcloud.wallet.api.vo.transaction.WalletTransactionVo">
<include refid="selectA"/>
from wallet_transaction a
left join wallet_manage m on m.id = a.wallet_id and m.del_flag = '0'
<where>
<include refid="whereA"/>
<if test="dto.walletTypeId != null">
and m.wallet_type_id = #{dto.walletTypeId}
</if>
</where>
order by a.create_time desc, a.user_id desc
</select>
<select id="scopeList" resultType="com.bsd.pjcloud.wallet.api.vo.transaction.WalletTransactionVo">
<include refid="selectA"/>
from wallet_transaction a
left join wallet_manage m on m.id = a.wallet_id and m.del_flag = '0'
<where>
<include refid="whereA"/>
<if test="dto.walletTypeId != null">
and m.wallet_type_id = #{dto.walletTypeId}
</if>
</where>
order by a.create_time desc, a.user_id desc
</select>
<select id="count" resultType="java.lang.Long">
select count(*)
from wallet_transaction a
left join wallet_manage m on m.id = a.wallet_id and m.del_flag = '0'
<where>
<include refid="whereA"/>
<if test="dto.walletTypeId != null">
and m.wallet_type_id = #{dto.walletTypeId}
</if>
</where>
order by a.create_time desc, a.user_id desc
</select>
</mapper>
@@ -0,0 +1,29 @@
package com.bsd.pjcloud;
import com.bsd.pjcloud.wallet.WalletApp;
import com.bsd.pjcloud.wallet.api.constant.WalletBizEnum;
import com.bsd.pjcloud.wallet.service.WalletTransactionService;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest(classes = WalletApp.class)
public class WalletTransactionTest {
@Resource
private WalletTransactionService walletTransactionService;
@Test
public void eruptAdd() {
// 4.24
Long walletId = 1877309001545244673L;
int price = 1;
String bizId = "-1";
for (int i = 0; i < 5; i++) {
walletTransactionService.addWalletBalance(walletId, price, bizId, WalletBizEnum.RECHARGE);
}
}
}