pom.xml
@@ -27,7 +27,7 @@ <pagehelper.boot.version>1.4.6</pagehelper.boot.version> <druid.version>1.2.16</druid.version> <dynamic-ds.version>3.5.2</dynamic-ds.version> <commons.io.version>2.11.0</commons.io.version> <commons.io.version>2.15.1</commons.io.version> <velocity.version>2.3</velocity.version> <fastjson.version>2.0.25</fastjson.version> <jjwt.version>0.9.1</jjwt.version> ruoyi-api/ruoyi-api-account/src/main/java/com/ruoyi/account/api/model/TAppUser.java
@@ -141,6 +141,10 @@ @TableField(exist = false) private Long orderCount; @ApiModelProperty("兑换码余额") @TableField(exist = false) private BigDecimal exchangeCodeBalance; @ApiModelProperty("剩余天数") @TableField(exist = false) private Long lastDays; ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/dto/PartnerListDTO.java
@@ -4,6 +4,8 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; /** * @author zhibing.pu * @Date 2024/8/7 14:03 @@ -15,9 +17,6 @@ private Integer id; @ApiModelProperty(value = "合作商名称") private String name; private Integer roleId; @ApiModelProperty(value = "角色名") private String roleName; @ApiModelProperty(value = "合作商电话1") private String phoneOne; @ApiModelProperty(value = "注册地址") @@ -26,4 +25,8 @@ private String account; @ApiModelProperty(value = "备注") private String remark; @ApiModelProperty(value = "电费分成比例") private BigDecimal electricityShareRatio; @ApiModelProperty(value = "服务费分成比例") private BigDecimal serviceShareRatio; } ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/model/Partner.java
@@ -63,6 +63,24 @@ @ApiModelProperty(value = "费率") private BigDecimal rate; /** * 工行合作商ID */ @TableField("icbc_partner_id") @ApiModelProperty(value = "工行合作商ID") private String icbcPartnerId; /** * 电费分成比例 */ @TableField("electricity_share_ratio") @ApiModelProperty(value = "电费分成比例") private BigDecimal electricityShareRatio; /** * 服务费分成比例 */ @TableField("service_share_ratio") @ApiModelProperty(value = "服务费分成比例") private BigDecimal serviceShareRatio; /** * 注册地址 */ @TableField("address") ruoyi-api/ruoyi-api-chargingPile/src/main/java/com/ruoyi/chargingPile/api/query/GetPartnerList.java
@@ -14,6 +14,4 @@ public class GetPartnerList extends BasePage { @ApiModelProperty(value = "合作商名称") private String name; @ApiModelProperty(value = "角色id") private Integer roleId; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TExchangeCodeDetail.java
New file @@ -0,0 +1,76 @@ package com.ruoyi.other.api.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.core.web.domain.BasePojo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; import java.time.LocalDateTime; @Data @EqualsAndHashCode(callSuper = false) @TableName("t_exchange_code_detail") @ApiModel(value = "TExchangeCodeDetail对象", description = "兑换码明细表") public class TExchangeCodeDetail extends BasePojo { @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty("主键") private Long id; @TableField("package_id") @ApiModelProperty("卡包ID") private Long packageId; @TableField("package_spec_id") @ApiModelProperty("规格ID") private Long packageSpecId; @TableField("code") @ApiModelProperty("兑换码") private String code; @TableField("password") @ApiModelProperty("密码") private String password; @TableField("status") @ApiModelProperty("状态 1未绑定 2已绑定 3已失效") private Integer status; @TableField("used_amount") @ApiModelProperty("已使用金额") private BigDecimal usedAmount; @TableField("bind_user_id") @ApiModelProperty("绑定用户ID") private Long bindUserId; @TableField("bind_phone") @ApiModelProperty("绑定手机号") private String bindPhone; @TableField("bind_time") @ApiModelProperty("绑定时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime bindTime; @TableField("invalid_time") @ApiModelProperty("失效时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime invalidTime; @TableField("invalid_reason") @ApiModelProperty("失效原因") private String invalidReason; @TableField("batch_no") @ApiModelProperty("批次号") private String batchNo; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TExchangeCodePackage.java
New file @@ -0,0 +1,91 @@ package com.ruoyi.other.api.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.fasterxml.jackson.annotation.JsonFormat; import com.ruoyi.common.core.web.domain.BasePojo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; import java.time.LocalDateTime; @Data @EqualsAndHashCode(callSuper = false) @TableName("t_exchange_code_package") @ApiModel(value = "TExchangeCodePackage对象", description = "兑换码卡包主表") public class TExchangeCodePackage extends BasePojo { @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty("主键") private Long id; @TableField("package_name") @ApiModelProperty("卡包名称") private String packageName; @TableField("cover_picture") @ApiModelProperty("封面图") private String coverPicture; @TableField("details_picture") @ApiModelProperty("详情图,逗号分隔") private String detailsPicture; @TableField("exchange_description") @ApiModelProperty("兑换说明") private String exchangeDescription; @TableField("remark") @ApiModelProperty("备注") private String remark; @TableField("inventory") @ApiModelProperty("库存总数") private Integer inventory; @TableField("bound_count") @ApiModelProperty("已绑定数量") private Integer boundCount; @TableField("invalid_count") @ApiModelProperty("已失效数量") private Integer invalidCount; @TableField("unlimited_receive_limit") @ApiModelProperty("是否不限领取 0否 1是") private Integer unlimitedReceiveLimit; @TableField("receive_limit") @ApiModelProperty("单手机号领取限制次数") private Integer receiveLimit; @TableField("finance_received_amount") @ApiModelProperty("财务实收") private BigDecimal financeReceivedAmount; @TableField("gift_amount") @ApiModelProperty("赠送金额") private BigDecimal giftAmount; @TableField("total_amount") @ApiModelProperty("总金额") private BigDecimal totalAmount; @TableField("expire_time") @ApiModelProperty("失效时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime expireTime; @TableField("status") @ApiModelProperty("状态 1启用 2禁用") private Integer status; @TableField(exist = false) @ApiModelProperty("未绑定数量") private Integer unboundCount; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TExchangeCodePackageSpec.java
New file @@ -0,0 +1,48 @@ package com.ruoyi.other.api.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.ruoyi.common.core.web.domain.BasePojo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; @Data @EqualsAndHashCode(callSuper = false) @TableName("t_exchange_code_package_spec") @ApiModel(value = "TExchangeCodePackageSpec对象", description = "兑换码卡包规格表") public class TExchangeCodePackageSpec extends BasePojo { @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty("主键") private Long id; @TableField("package_id") @ApiModelProperty("卡包ID") private Long packageId; @TableField("amount") @ApiModelProperty("规格金额") private BigDecimal amount; @TableField("quantity") @ApiModelProperty("规格数量") private Integer quantity; @TableField("subtotal_amount") @ApiModelProperty("规格总金额") private BigDecimal subtotalAmount; @TableField("discount_amount") @ApiModelProperty("规格分摊优惠金额") private BigDecimal discountAmount; @TableField("sort") @ApiModelProperty("排序") private Integer sort; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/domain/TExchangeCodeUseRecord.java
New file @@ -0,0 +1,40 @@ package com.ruoyi.other.api.domain; import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import com.ruoyi.common.core.web.domain.BasePojo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.math.BigDecimal; @Data @EqualsAndHashCode(callSuper = false) @TableName("t_exchange_code_use_record") @ApiModel(value = "TExchangeCodeUseRecord对象", description = "兑换码使用流水表") public class TExchangeCodeUseRecord extends BasePojo { @TableId(value = "id", type = IdType.AUTO) @ApiModelProperty("主键") private Long id; @TableField("detail_id") @ApiModelProperty("兑换码明细ID") private Long detailId; @TableField("order_id") @ApiModelProperty("订单ID") private Long orderId; @TableField("user_id") @ApiModelProperty("用户ID") private Long userId; @TableField("used_amount") @ApiModelProperty("本次使用金额") private BigDecimal usedAmount; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/ExchangeCodeBindDTO.java
New file @@ -0,0 +1,19 @@ package com.ruoyi.other.api.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel("兑换码绑定DTO") public class ExchangeCodeBindDTO { @ApiModelProperty("兑换码") private String code; @ApiModelProperty("密码") private String password; @ApiModelProperty("手机号,默认取登录用户手机号") private String phone; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/ExchangeCodeConsumeDTO.java
New file @@ -0,0 +1,21 @@ package com.ruoyi.other.api.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; @Data @ApiModel("兑换码余额扣减DTO") public class ExchangeCodeConsumeDTO { @ApiModelProperty("用户ID") private Long userId; @ApiModelProperty("订单ID") private Long orderId; @ApiModelProperty("扣减金额") private BigDecimal amount; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/ExchangeCodePackageSaveDTO.java
New file @@ -0,0 +1,61 @@ package com.ruoyi.other.api.dto; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @Data @ApiModel("兑换码卡包新增编辑DTO") public class ExchangeCodePackageSaveDTO { @ApiModelProperty("卡包ID,编辑时必填") private Long id; @ApiModelProperty("卡包名称") private String packageName; @ApiModelProperty("封面图") private String coverPicture; @ApiModelProperty("详情图,逗号分隔") private String detailsPicture; @ApiModelProperty("兑换说明") private String exchangeDescription; @ApiModelProperty("备注") private String remark; @ApiModelProperty("库存") private Integer inventory; @ApiModelProperty("是否不限领 0否 1是") private Integer unlimitedReceiveLimit; @ApiModelProperty("领取限制次数") private Integer receiveLimit; @ApiModelProperty("财务实收") private BigDecimal financeReceivedAmount; @ApiModelProperty("赠送金额") private BigDecimal giftAmount; @ApiModelProperty("总金额") private BigDecimal totalAmount; @ApiModelProperty("失效时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime expireTime; @ApiModelProperty("状态 1启用 2禁用") private Integer status; @ApiModelProperty("规格列表") private List<ExchangeCodePackageSpecDTO> specList; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/dto/ExchangeCodePackageSpecDTO.java
New file @@ -0,0 +1,30 @@ package com.ruoyi.other.api.dto; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; @Data @ApiModel("兑换码卡包规格DTO") public class ExchangeCodePackageSpecDTO { @ApiModelProperty("规格ID") private Long id; @ApiModelProperty("金额") private BigDecimal amount; @ApiModelProperty("数量") private Integer quantity; @ApiModelProperty("规格总金额") private BigDecimal subtotalAmount; @ApiModelProperty("优惠金额") private BigDecimal discountAmount; @ApiModelProperty("排序") private Integer sort; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/factory/OtherFallbackFactory.java
@@ -3,10 +3,14 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.ruoyi.common.core.domain.R; import com.ruoyi.other.api.domain.*; import com.ruoyi.other.api.dto.ExchangeCodeBindDTO; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.dto.TagListQueryDto; import com.ruoyi.other.api.dto.UnitListQueryDto; import com.ruoyi.other.api.feignClient.OtherClient; import com.ruoyi.other.api.vo.CouponListVOVO; import com.ruoyi.other.api.vo.ExchangeCodeBindResultVO; import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.openfeign.FallbackFactory; @@ -141,6 +145,21 @@ public R reset(String electricity) { return R.fail("远程调用获取配置失败:"+throwable.getMessage()); } @Override public R<ExchangeCodeBindResultVO> bindExchangeCode(ExchangeCodeBindDTO dto, Long userId, String phone) { return R.fail("兑换码绑定失败:" + throwable.getMessage()); } @Override public R<Map<Long, BigDecimal>> getExchangeCodeBalanceMap(List<Long> userIds) { return R.fail("查询兑换码余额失败:" + throwable.getMessage()); } @Override public R<ExchangeCodeConsumeResultVO> consumeExchangeCodeBalance(ExchangeCodeConsumeDTO dto) { return R.fail("扣减兑换码余额失败:" + throwable.getMessage()); } }; } } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/feignClient/OtherClient.java
@@ -4,10 +4,14 @@ import com.ruoyi.common.core.constant.ServiceNameConstants; import com.ruoyi.common.core.domain.R; import com.ruoyi.other.api.domain.*; import com.ruoyi.other.api.dto.ExchangeCodeBindDTO; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.dto.TagListQueryDto; import com.ruoyi.other.api.dto.UnitListQueryDto; import com.ruoyi.other.api.factory.OtherFallbackFactory; import com.ruoyi.other.api.vo.CouponListVOVO; import com.ruoyi.other.api.vo.ExchangeCodeBindResultVO; import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.*; @@ -96,4 +100,15 @@ @GetMapping(value = "/region/reset") R reset(@RequestParam("electricity") String electricity); @PostMapping(value = "/exchange-code-package/bind") R<ExchangeCodeBindResultVO> bindExchangeCode(@RequestBody ExchangeCodeBindDTO dto, @RequestParam("userId") Long userId, @RequestParam("phone") String phone); @PostMapping(value = "/exchange-code-package/balanceMap") R<Map<Long, BigDecimal>> getExchangeCodeBalanceMap(@RequestBody List<Long> userIds); @PostMapping(value = "/exchange-code-package/consumeBalance") R<ExchangeCodeConsumeResultVO> consumeExchangeCodeBalance(@RequestBody ExchangeCodeConsumeDTO dto); } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/vo/ExchangeCodeBindResultVO.java
New file @@ -0,0 +1,22 @@ package com.ruoyi.other.api.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel("兑换码绑定结果VO") public class ExchangeCodeBindResultVO { @ApiModelProperty("明细ID") private Long detailId; @ApiModelProperty("卡包ID") private Long packageId; @ApiModelProperty("卡包名称") private String packageName; @ApiModelProperty("绑定手机号") private String bindPhone; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/vo/ExchangeCodeConsumeResultVO.java
New file @@ -0,0 +1,15 @@ package com.ruoyi.other.api.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; @Data @ApiModel("兑换码余额扣减结果VO") public class ExchangeCodeConsumeResultVO { @ApiModelProperty("实际扣减金额") private BigDecimal deductedAmount; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/vo/ExchangeCodePackageDetailVO.java
New file @@ -0,0 +1,18 @@ package com.ruoyi.other.api.vo; import com.ruoyi.other.api.domain.TExchangeCodePackage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import lombok.EqualsAndHashCode; import java.util.List; @Data @EqualsAndHashCode(callSuper = true) @ApiModel("兑换码卡包详情VO") public class ExchangeCodePackageDetailVO extends TExchangeCodePackage { @ApiModelProperty("规格列表") private List<ExchangeCodePackageSpecVO> specList; } ruoyi-api/ruoyi-api-other/src/main/java/com/ruoyi/other/api/vo/ExchangeCodePackageSpecVO.java
New file @@ -0,0 +1,36 @@ package com.ruoyi.other.api.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.math.BigDecimal; @Data @ApiModel("兑换码卡包规格VO") public class ExchangeCodePackageSpecVO { @ApiModelProperty("规格ID") private Long id; @ApiModelProperty("金额") private BigDecimal amount; @ApiModelProperty("数量") private Integer quantity; @ApiModelProperty("已绑定数量") private Integer boundCount; @ApiModelProperty("剩余数量") private Integer unboundCount; @ApiModelProperty("小计金额") private BigDecimal subtotalAmount; @ApiModelProperty("优惠金额") private BigDecimal discountAmount; @ApiModelProperty("排序") private Integer sort; } ruoyi-service/ruoyi-account/pom.xml
@@ -176,6 +176,9 @@ <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includeSystemScope>true</includeSystemScope> </configuration> <executions> <execution> <goals> @@ -188,13 +191,6 @@ <resources> <resource> <directory>src/main/resources</directory> </resource> <resource> <directory>lib</directory> <targetPath>/BOOT-INF/lib/</targetPath> <includes> <include>**/*.jar</include> </includes> </resource> <resource> <directory>src/main/java</directory> ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppCouponController.java
@@ -35,7 +35,9 @@ import com.ruoyi.common.security.service.TokenService; import com.ruoyi.order.api.feignClient.ChargingOrderClient; import com.ruoyi.other.api.domain.TCoupon; import com.ruoyi.other.api.dto.ExchangeCodeBindDTO; import com.ruoyi.other.api.feignClient.OtherClient; import com.ruoyi.other.api.vo.ExchangeCodeBindResultVO; import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiOperation; import org.redisson.api.RLock; @@ -249,6 +251,15 @@ return R.ok(count); } @ApiOperation(value = "兑换码绑定", tags = {"小程序-兑换码"}) @PostMapping(value = "/exchangeCode/bind") public AjaxResult<ExchangeCodeBindResultVO> bindExchangeCode(@RequestBody ExchangeCodeBindDTO dto) { Long userId = tokenService.getLoginUserApplet().getUserId(); TAppUser user = appUserService.getById(userId); ExchangeCodeBindResultVO data = otherClient.bindExchangeCode(dto, userId, user == null ? "" : user.getPhone()).getData(); return AjaxResult.ok(data); } /** @@ -351,4 +362,3 @@ return R.ok(); } } ruoyi-service/ruoyi-account/src/main/java/com/ruoyi/account/controller/TAppUserController.java
@@ -368,12 +368,18 @@ if (page.getRecords().isEmpty()){ return R.ok(page); } List<Long> pageUserIds = page.getRecords().stream().map(TAppUser::getId).collect(Collectors.toList()); Map<Long, BigDecimal> exchangeCodeBalanceMap = otherClient.getExchangeCodeBalanceMap(pageUserIds).getData(); if (exchangeCodeBalanceMap == null) { exchangeCodeBalanceMap = new HashMap<>(); } List<Integer> vipIds = new ArrayList<>(); // vipIds = page.getRecords().stream().map(TAppUser::getVipId).collect(Collectors.toList()); //获取会员map R<Map<Integer, String>> vipMap = otherClient.getVipMap(vipIds); //循环处理 for (TAppUser appUser : page.getRecords()) { appUser.setExchangeCodeBalance(exchangeCodeBalanceMap.getOrDefault(appUser.getId(), BigDecimal.ZERO)); //拿到最新的tagId TAppUserTag one = appUserTagService.lambdaQuery().eq(TAppUserTag::getAppUserId, appUser.getId()).orderByDesc(TAppUserTag::getCreateTime).last("limit 1").one(); if (one!=null) { @@ -1475,4 +1481,3 @@ } } ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/PartnerServiceImpl.java
@@ -17,12 +17,9 @@ import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.other.api.feignClient.RoleSiteClient; import com.ruoyi.other.api.feignClient.UserSiteClient; import com.ruoyi.system.api.domain.SysRole; import com.ruoyi.system.api.domain.SysUser; import com.ruoyi.system.api.feignClient.SysRoleClient; import com.ruoyi.system.api.feignClient.SysUserClient; import com.ruoyi.system.api.feignClient.SysUserRoleClient; import com.ruoyi.system.api.model.GetSysRoleByIds; import com.ruoyi.system.api.model.SysUserRoleVo; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,8 +35,10 @@ @Service public class PartnerServiceImpl extends ServiceImpl<PartnerMapper, Partner> implements IPartnerService { @Resource private SysRoleClient sysRoleClient; /** * TODO 确认合作商默认角色ID,当前先使用通用角色占位。 */ private static final Long PARTNER_DEFAULT_ROLE_ID = 2L; @Resource private SysUserClient sysUserClient; @@ -76,25 +75,6 @@ public PageInfo<PartnerListDTO> getPartnerList(GetPartnerList partnerList) { PageInfo<PartnerListDTO> pageInfo = new PageInfo(partnerList.getPageCurr(), partnerList.getPageSize()); List<PartnerListDTO> list = this.baseMapper.getPartnerList(pageInfo, partnerList); List<Integer> roleIds = list.stream().map(PartnerListDTO::getRoleId).collect(Collectors.toList()); GetSysRoleByIds ids = new GetSysRoleByIds(); ids.setIds(roleIds); //获取相关的角色id R<List<SysRole>> r = sysRoleClient.getSysRoleByIds(ids); if(r.getCode() != 200){ throw new RuntimeException(r.getMsg()); } List<SysRole> data = r.getData(); for (PartnerListDTO partnerListDTO : list) { Integer roleId = partnerListDTO.getRoleId(); if(null == roleId){ continue; } SysRole sysRole = data.stream().filter(s -> roleId.compareTo(s.getRoleId().intValue()) == 0).findFirst().get(); if(null != sysRole){ partnerListDTO.setRoleName(sysRole.getRoleName()); } } pageInfo.setRecords(list); return pageInfo; } @@ -112,6 +92,7 @@ if(ajaxResult.isError()){ return ajaxResult; } partner.setRoleId(PARTNER_DEFAULT_ROLE_ID.intValue()); Partner one = this.getOne(new LambdaQueryWrapper<Partner>() .eq(Partner::getCode, partner.getCode()) .eq(Partner::getDelFlag, 0) @@ -135,7 +116,7 @@ user.setPassword(partner.getPassword()); user.setStatus("0"); user.setDelFlag("0"); user.setRoleId(partner.getRoleId().longValue()); user.setRoleId(PARTNER_DEFAULT_ROLE_ID); user.setRoleType(2); user.setPassWordUpdate(new Date()); R<Long> r = sysUserClient.addSysUser(user); @@ -163,9 +144,6 @@ if(StringUtils.isEmpty(partner.getName())){ return AjaxResult.error("合作商名称不能为空"); } if(null == partner.getRoleId()){ return AjaxResult.error("合作商角色不能为空"); } if(StringUtils.isEmpty(partner.getPhoneOne())){ return AjaxResult.error("合作商电话1不能为空"); } @@ -186,22 +164,7 @@ */ @Override public Partner getPartner(Integer id) { Partner partner = this.getById(id); if(null == partner){ return partner; } GetSysRoleByIds ids = new GetSysRoleByIds(); ids.setIds(Arrays.asList(partner.getRoleId())); //获取相关的角色id R<List<SysRole>> r = sysRoleClient.getSysRoleByIds(ids); if(r.getCode() != 200){ throw new RuntimeException(r.getMsg()); } List<SysRole> data = r.getData(); if(data.size() > 0){ partner.setRoleName(data.get(0).getRoleName()); } return partner; return this.getById(id); } @@ -217,6 +180,7 @@ if(ajaxResult.isError()){ return ajaxResult; } partner.setRoleId(PARTNER_DEFAULT_ROLE_ID.intValue()); Partner one = this.getOne(new LambdaQueryWrapper<Partner>() .eq(Partner::getCode, partner.getCode()) .eq(Partner::getDelFlag, 0) @@ -233,6 +197,9 @@ } Partner oldPartner = this.getById(partner.getId()); SysUser user = sysUserClient.queryUserByUserName(oldPartner.getAccount()).getData(); if(StringUtils.isEmpty(partner.getPassword())){ partner.setPassword(oldPartner.getPassword()); } //修改登录账户 if(null == user){ user = new SysUser(); @@ -243,7 +210,7 @@ user.setNickName(partner.getName()); user.setAvatar(partner.getLogoUrl()); user.setPassword(partner.getPassword()); user.setRoleId(partner.getRoleId().longValue()); user.setRoleId(PARTNER_DEFAULT_ROLE_ID); user.setRoleType(2); user.setObjectId(partner.getId()); user.setPassWordUpdate(new Date()); @@ -256,9 +223,11 @@ user.setUserName(partner.getAccount()); user.setNickName(partner.getName()); user.setAvatar(partner.getLogoUrl()); if(StringUtils.isNotEmpty(partner.getPassword())){ user.setPassword(partner.getPassword()); user.setRoleId(partner.getRoleId().longValue()); user.setPassWordUpdate(new Date()); } user.setRoleId(PARTNER_DEFAULT_ROLE_ID); R r = sysUserClient.updateSysUser(user); if(200 != r.getCode()){ throw new RuntimeException(r.getMsg()); ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/PartnerMapper.xml
@@ -10,6 +10,9 @@ <result column="role_id" property="roleId" /> <result column="logo_url" property="logoUrl" /> <result column="rate" property="rate" /> <result column="icbc_partner_id" property="icbcPartnerId" /> <result column="electricity_share_ratio" property="electricityShareRatio" /> <result column="service_share_ratio" property="serviceShareRatio" /> <result column="address" property="address" /> <result column="phone_one" property="phoneOne" /> <result column="phone_two" property="phoneTwo" /> @@ -26,17 +29,15 @@ select id, name, role_id as roleId, phone_one as phoneOne, address, account, remark remark, electricity_share_ratio as electricityShareRatio, service_share_ratio as serviceShareRatio from t_partner where del_flag = 0 <if test="null != item.name and '' != item.name"> and `name` like CONCAT('%', #{item.name}, '%') </if> <if test="null != item.roleId and '' != item.name"> and role_id = #{item.roleId} </if> order by create_time desc </select> ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TVipOrderController.java
@@ -30,7 +30,6 @@ import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.poi.ss.usermodel.Workbook; import org.omg.CORBA.PRIVATE_MEMBER; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.StringUtils; @@ -210,4 +209,3 @@ } ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -63,7 +63,9 @@ import com.ruoyi.other.api.domain.*; import com.ruoyi.order.vo.ChargingOrderListInfoVO; import com.ruoyi.other.api.domain.TVip; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.feignClient.*; import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; import com.ruoyi.payment.api.feignClient.AliPaymentClient; import com.ruoyi.payment.api.feignClient.H5AliPaymentClient; import com.ruoyi.payment.api.feignClient.WxPaymentClient; @@ -201,6 +203,8 @@ @Resource private VipClient vipClient; @Resource private OtherClient otherClient; @Resource private UserTagClient userTagClient; @@ -2672,6 +2676,22 @@ } chargingOrder.setCouponDiscountAmount(couponDiscount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setServiceCharge(serviceCharge_total.setScale(2, RoundingMode.HALF_EVEN)); BigDecimal exchangeCodeDeductAmount = BigDecimal.ZERO; if (chargingOrder.getAppUserId() != null && payAmount.compareTo(BigDecimal.ZERO) > 0) { ExchangeCodeConsumeDTO exchangeCodeConsumeDTO = new ExchangeCodeConsumeDTO(); exchangeCodeConsumeDTO.setUserId(chargingOrder.getAppUserId()); exchangeCodeConsumeDTO.setOrderId(chargingOrder.getId()); exchangeCodeConsumeDTO.setAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); R<ExchangeCodeConsumeResultVO> consumeResult = otherClient.consumeExchangeCodeBalance(exchangeCodeConsumeDTO); if (consumeResult != null && consumeResult.getData() != null && consumeResult.getData().getDeductedAmount() != null) { exchangeCodeDeductAmount = consumeResult.getData().getDeductedAmount(); } } payAmount = payAmount.subtract(exchangeCodeDeductAmount); if (payAmount.compareTo(BigDecimal.ZERO) < 0) { payAmount = BigDecimal.ZERO; } chargingOrder.setPaymentAmount(payAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundAmount(refundAmount.setScale(2, RoundingMode.HALF_EVEN)); chargingOrder.setRefundStatus(1); ruoyi-service/ruoyi-other/pom.xml
@@ -132,9 +132,19 @@ <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel-core</artifactId> <version>RELEASE</version> <version>3.3.4</version> <scope>compile</scope> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.3.0</version> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.3.0</version> </dependency> </dependencies> <build> ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/controller/TExchangeCodePackageController.java
New file @@ -0,0 +1,190 @@ package com.ruoyi.other.controller; import cn.afterturn.easypoi.excel.ExcelExportUtil; import cn.afterturn.easypoi.excel.entity.ExportParams; import com.ruoyi.common.core.utils.WebUtils; import com.ruoyi.common.core.domain.R; import com.ruoyi.other.util.QRCodeUtils; import com.ruoyi.common.core.web.domain.AjaxResult; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.common.log.annotation.Log; import com.ruoyi.common.log.enums.BusinessType; import com.ruoyi.common.log.enums.OperatorType; import com.ruoyi.common.security.annotation.Logical; import com.ruoyi.common.security.annotation.RequiresPermissions; import com.ruoyi.other.api.dto.ExchangeCodeBindDTO; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.dto.ExchangeCodePackageSaveDTO; import com.ruoyi.other.api.vo.ExchangeCodeBindResultVO; import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; import com.ruoyi.other.api.vo.ExchangeCodePackageDetailVO; import com.ruoyi.other.query.ExchangeCodeBatchInvalidDTO; import com.ruoyi.other.query.ExchangeCodeDetailQuery; import com.ruoyi.other.query.ExchangeCodePackageQuery; import com.ruoyi.other.service.TExchangeCodePackageService; import com.ruoyi.other.vo.ExchangeCodeBatchInvalidResultVO; import com.ruoyi.other.vo.ExchangeCodeDetailExportVO; import com.ruoyi.other.vo.ExchangeCodeDetailVO; import io.swagger.annotations.ApiOperation; import org.springframework.beans.BeanUtils; import org.apache.poi.ss.usermodel.Workbook; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.net.URLEncoder; import java.util.List; import java.math.BigDecimal; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; @RestController @RequestMapping("/exchange-code-package") public class TExchangeCodePackageController { @Resource private TExchangeCodePackageService exchangeCodePackageService; @RequiresPermissions(value = {"/exchangeCode"}, logical = Logical.OR) @PostMapping("/pageList") @ApiOperation(value = "兑换码卡包分页", tags = {"管理后台-兑换码管理"}) public AjaxResult<PageInfo<?>> pageList(@RequestBody ExchangeCodePackageQuery query) { return AjaxResult.ok(exchangeCodePackageService.pageList(query)); } @RequiresPermissions(value = {"/exchangeCode/add"}, logical = Logical.OR) @PostMapping("/save") @ApiOperation(value = "新增兑换码卡包", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】新增卡包", businessType = BusinessType.INSERT, operatorType = OperatorType.MANAGE) public AjaxResult<Void> save(@RequestBody ExchangeCodePackageSaveDTO dto) { exchangeCodePackageService.savePackage(dto); return AjaxResult.success(); } @RequiresPermissions(value = {"/exchangeCode/update"}, logical = Logical.OR) @PostMapping("/update") @ApiOperation(value = "编辑兑换码卡包", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】编辑卡包", businessType = BusinessType.UPDATE, operatorType = OperatorType.MANAGE) public AjaxResult<Void> update(@RequestBody ExchangeCodePackageSaveDTO dto) { exchangeCodePackageService.updatePackage(dto); return AjaxResult.success(); } @RequiresPermissions(value = {"/exchangeCode/del"}, logical = Logical.OR) @GetMapping("/delete") @ApiOperation(value = "删除兑换码卡包", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】删除卡包", businessType = BusinessType.DELETE, operatorType = OperatorType.MANAGE) public AjaxResult<Void> delete(String ids) { exchangeCodePackageService.deletePackage(ids); return AjaxResult.success(); } @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR) @GetMapping("/detail") @ApiOperation(value = "兑换码卡包详情", tags = {"管理后台-兑换码管理"}) public AjaxResult<ExchangeCodePackageDetailVO> detail(@RequestParam("id") Long id) { return AjaxResult.ok(exchangeCodePackageService.getDetail(id)); } @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR) @PostMapping("/codePage") @ApiOperation(value = "兑换码明细分页", tags = {"管理后台-兑换码管理"}) public AjaxResult<PageInfo<?>> codePage(@RequestBody ExchangeCodeDetailQuery query) { return AjaxResult.ok(exchangeCodePackageService.codePage(query)); } @RequiresPermissions(value = {"/exchangeCode/del"}, logical = Logical.OR) @PostMapping("/batchInvalidate") @ApiOperation(value = "批量失效兑换码", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】批量失效", businessType = BusinessType.UPDATE, operatorType = OperatorType.MANAGE) public AjaxResult<ExchangeCodeBatchInvalidResultVO> batchInvalidate(@RequestBody ExchangeCodeBatchInvalidDTO dto) { ExchangeCodeBatchInvalidResultVO result = exchangeCodePackageService.batchInvalidate(dto); return AjaxResult.ok(result).put("msg", "已失效" + result.getSuccessCount() + "条,跳过" + result.getSkipCount() + "条"); } @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR) @PutMapping("/export") @ApiOperation(value = "导出兑换码明细", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】导出明细", businessType = BusinessType.EXPORT, operatorType = OperatorType.MANAGE) public void export(@RequestBody ExchangeCodeDetailQuery query) throws IOException { List<ExchangeCodeDetailVO> list = exchangeCodePackageService.exportCodes(query); List<ExchangeCodeDetailExportVO> exportList = new java.util.ArrayList<>(); for (ExchangeCodeDetailVO detailVO : list) { ExchangeCodeDetailExportVO exportVO = new ExchangeCodeDetailExportVO(); BeanUtils.copyProperties(detailVO, exportVO); exportList.add(exportVO); } Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(), ExchangeCodeDetailExportVO.class, exportList); HttpServletResponse response = WebUtils.response(); response.setContentType("application/vnd.ms-excel;charset=UTF-8"); response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("兑换码明细.xls", "utf-8"); response.setHeader("Content-Disposition", "attachment;filename=" + fileName); ServletOutputStream outputStream = response.getOutputStream(); workbook.write(outputStream); outputStream.flush(); outputStream.close(); } @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR) @GetMapping("/downloadQRCode/{id}") @ApiOperation(value = "下载单个兑换码二维码", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】下载二维码", businessType = BusinessType.OTHER, operatorType = OperatorType.MANAGE) public void downloadQRCode(@PathVariable("id") Long id) throws Exception { ExchangeCodeDetailVO detail = exchangeCodePackageService.getCodeDetail(id); HttpServletResponse response = WebUtils.response(); response.setContentType("image/jpeg"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(detail.getCode() + ".jpg", "UTF-8")); QRCodeUtils.encode(buildQrPayload(detail), response.getOutputStream()); } @RequiresPermissions(value = {"/exchangeCode/select"}, logical = Logical.OR) @PutMapping("/downloadUnboundQRCodes") @ApiOperation(value = "批量下载未绑定二维码", tags = {"管理后台-兑换码管理"}) @Log(title = "【兑换码管理】批量下载未绑定二维码", businessType = BusinessType.EXPORT, operatorType = OperatorType.MANAGE) public void downloadUnboundQRCodes(@RequestBody ExchangeCodeDetailQuery query) throws Exception { List<ExchangeCodeDetailVO> list = exchangeCodePackageService.listDownloadableCodes(query.getPackageId(), true); HttpServletResponse response = WebUtils.response(); response.setContentType("application/zip"); response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("未绑定二维码.zip", "UTF-8")); ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream()); for (ExchangeCodeDetailVO detail : list) { zipOutputStream.putNextEntry(new ZipEntry(detail.getCode() + ".jpg")); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); QRCodeUtils.encode(buildQrPayload(detail), outputStream); zipOutputStream.write(outputStream.toByteArray()); zipOutputStream.closeEntry(); } zipOutputStream.finish(); zipOutputStream.flush(); zipOutputStream.close(); } @PostMapping("/bind") @ApiOperation(value = "小程序兑换码绑定", tags = {"小程序-兑换码"}) public R<ExchangeCodeBindResultVO> bind(@RequestBody ExchangeCodeBindDTO dto, @RequestParam("userId") Long userId, @RequestParam("phone") String phone) { return R.ok(exchangeCodePackageService.bindExchangeCode(dto, userId, phone)); } @PostMapping("/balanceMap") @ApiOperation(value = "查询用户兑换码余额Map", tags = {"内部-兑换码"}) public R<Map<Long, BigDecimal>> balanceMap(@RequestBody List<Long> userIds) { return R.ok(exchangeCodePackageService.getUserExchangeCodeBalanceMap(userIds)); } @PostMapping("/consumeBalance") @ApiOperation(value = "扣减用户兑换码余额", tags = {"内部-兑换码"}) public R<ExchangeCodeConsumeResultVO> consumeBalance(@RequestBody ExchangeCodeConsumeDTO dto) { return R.ok(exchangeCodePackageService.consumeBalance(dto)); } private String buildQrPayload(ExchangeCodeDetailVO detail) { return "code=" + detail.getCode() + "&password=" + detail.getPassword(); } } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/TExchangeCodeDetailMapper.java
New file @@ -0,0 +1,18 @@ package com.ruoyi.other.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.other.api.domain.TExchangeCodeDetail; import com.ruoyi.other.vo.ExchangeCodeDetailVO; import com.ruoyi.other.query.ExchangeCodeDetailQuery; import org.apache.ibatis.annotations.Param; import java.util.List; public interface TExchangeCodeDetailMapper extends BaseMapper<TExchangeCodeDetail> { List<ExchangeCodeDetailVO> pageList(@Param("pageInfo") PageInfo<ExchangeCodeDetailVO> pageInfo, @Param("req") ExchangeCodeDetailQuery query); List<ExchangeCodeDetailVO> exportList(@Param("req") ExchangeCodeDetailQuery query); } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/TExchangeCodePackageMapper.java
New file @@ -0,0 +1,15 @@ package com.ruoyi.other.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.other.api.domain.TExchangeCodePackage; import com.ruoyi.other.query.ExchangeCodePackageQuery; import org.apache.ibatis.annotations.Param; import java.util.List; public interface TExchangeCodePackageMapper extends BaseMapper<TExchangeCodePackage> { List<TExchangeCodePackage> pageList(@Param("pageInfo") PageInfo<TExchangeCodePackage> pageInfo, @Param("req") ExchangeCodePackageQuery query); } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/TExchangeCodePackageSpecMapper.java
New file @@ -0,0 +1,7 @@ package com.ruoyi.other.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.other.api.domain.TExchangeCodePackageSpec; public interface TExchangeCodePackageSpecMapper extends BaseMapper<TExchangeCodePackageSpec> { } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/mapper/TExchangeCodeUseRecordMapper.java
New file @@ -0,0 +1,7 @@ package com.ruoyi.other.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.ruoyi.other.api.domain.TExchangeCodeUseRecord; public interface TExchangeCodeUseRecordMapper extends BaseMapper<TExchangeCodeUseRecord> { } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/ExchangeCodeBatchInvalidDTO.java
New file @@ -0,0 +1,18 @@ package com.ruoyi.other.query; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.util.List; @Data @ApiModel("兑换码批量失效DTO") public class ExchangeCodeBatchInvalidDTO { @ApiModelProperty("卡包ID") private Long packageId; @ApiModelProperty("明细ID列表") private List<Long> detailIds; } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/ExchangeCodeDetailQuery.java
New file @@ -0,0 +1,23 @@ package com.ruoyi.other.query; import com.ruoyi.common.core.web.page.BasePage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel("兑换码明细分页查询DTO") public class ExchangeCodeDetailQuery extends BasePage { @ApiModelProperty("卡包ID") private Long packageId; @ApiModelProperty("兑换码/卡号") private String code; @ApiModelProperty("绑定手机号") private String bindPhone; @ApiModelProperty("状态 1未绑定 2已绑定 3已失效") private Integer status; } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/query/ExchangeCodePackageQuery.java
New file @@ -0,0 +1,17 @@ package com.ruoyi.other.query; import com.ruoyi.common.core.web.page.BasePage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @Data @ApiModel("兑换码卡包分页查询DTO") public class ExchangeCodePackageQuery extends BasePage { @ApiModelProperty("卡包名称") private String packageName; @ApiModelProperty("状态 1启用 2禁用") private Integer status; } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/TExchangeCodePackageService.java
New file @@ -0,0 +1,48 @@ package com.ruoyi.other.service; import com.baomidou.mybatisplus.extension.service.IService; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.other.api.domain.TExchangeCodePackage; import com.ruoyi.other.api.dto.ExchangeCodeBindDTO; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.dto.ExchangeCodePackageSaveDTO; import com.ruoyi.other.api.vo.ExchangeCodeBindResultVO; import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; import com.ruoyi.other.api.vo.ExchangeCodePackageDetailVO; import com.ruoyi.other.query.ExchangeCodeBatchInvalidDTO; import com.ruoyi.other.query.ExchangeCodeDetailQuery; import com.ruoyi.other.query.ExchangeCodePackageQuery; import com.ruoyi.other.vo.ExchangeCodeBatchInvalidResultVO; import com.ruoyi.other.vo.ExchangeCodeDetailVO; import java.util.List; import java.util.Map; public interface TExchangeCodePackageService extends IService<TExchangeCodePackage> { PageInfo<TExchangeCodePackage> pageList(ExchangeCodePackageQuery query); void savePackage(ExchangeCodePackageSaveDTO dto); void updatePackage(ExchangeCodePackageSaveDTO dto); void deletePackage(String ids); ExchangeCodePackageDetailVO getDetail(Long id); PageInfo<ExchangeCodeDetailVO> codePage(ExchangeCodeDetailQuery query); List<ExchangeCodeDetailVO> exportCodes(ExchangeCodeDetailQuery query); ExchangeCodeBatchInvalidResultVO batchInvalidate(ExchangeCodeBatchInvalidDTO dto); List<ExchangeCodeDetailVO> listDownloadableCodes(Long packageId, boolean onlyUnbound); ExchangeCodeDetailVO getCodeDetail(Long id); ExchangeCodeBindResultVO bindExchangeCode(ExchangeCodeBindDTO dto, Long userId, String phone); Map<Long, java.math.BigDecimal> getUserExchangeCodeBalanceMap(List<Long> userIds); ExchangeCodeConsumeResultVO consumeBalance(ExchangeCodeConsumeDTO dto); } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/service/impl/TExchangeCodePackageServiceImpl.java
New file @@ -0,0 +1,537 @@ package com.ruoyi.other.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.core.exception.ServiceException; import com.ruoyi.common.core.utils.StringUtils; import com.ruoyi.common.core.web.page.PageInfo; import com.ruoyi.other.api.domain.TExchangeCodeDetail; import com.ruoyi.other.api.domain.TExchangeCodePackage; import com.ruoyi.other.api.domain.TExchangeCodePackageSpec; import com.ruoyi.other.api.domain.TExchangeCodeUseRecord; import com.ruoyi.other.api.dto.ExchangeCodeBindDTO; import com.ruoyi.other.api.dto.ExchangeCodeConsumeDTO; import com.ruoyi.other.api.dto.ExchangeCodePackageSaveDTO; import com.ruoyi.other.api.dto.ExchangeCodePackageSpecDTO; import com.ruoyi.other.api.vo.ExchangeCodeBindResultVO; import com.ruoyi.other.api.vo.ExchangeCodeConsumeResultVO; import com.ruoyi.other.api.vo.ExchangeCodePackageDetailVO; import com.ruoyi.other.api.vo.ExchangeCodePackageSpecVO; import com.ruoyi.other.mapper.TExchangeCodeDetailMapper; import com.ruoyi.other.mapper.TExchangeCodePackageMapper; import com.ruoyi.other.mapper.TExchangeCodePackageSpecMapper; import com.ruoyi.other.mapper.TExchangeCodeUseRecordMapper; import com.ruoyi.other.query.ExchangeCodeBatchInvalidDTO; import com.ruoyi.other.query.ExchangeCodeDetailQuery; import com.ruoyi.other.query.ExchangeCodePackageQuery; import com.ruoyi.other.service.TExchangeCodePackageService; import com.ruoyi.other.vo.ExchangeCodeBatchInvalidResultVO; import com.ruoyi.other.vo.ExchangeCodeDetailVO; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.annotation.Resource; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.stream.Collectors; @Service public class TExchangeCodePackageServiceImpl extends ServiceImpl<TExchangeCodePackageMapper, TExchangeCodePackage> implements TExchangeCodePackageService { private static final Integer STATUS_ENABLE = 1; private static final Integer STATUS_DISABLE = 2; private static final Integer CODE_STATUS_UNBOUND = 1; private static final Integer CODE_STATUS_BOUND = 2; private static final Integer CODE_STATUS_INVALID = 3; private static final DateTimeFormatter BATCH_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss"); @Resource private TExchangeCodePackageSpecMapper packageSpecMapper; @Resource private TExchangeCodeDetailMapper codeDetailMapper; @Resource private TExchangeCodeUseRecordMapper useRecordMapper; @Override public PageInfo<TExchangeCodePackage> pageList(ExchangeCodePackageQuery query) { PageInfo<TExchangeCodePackage> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize()); List<TExchangeCodePackage> records = this.baseMapper.pageList(pageInfo, query); pageInfo.setRecords(records); return pageInfo; } @Override @Transactional(rollbackFor = Exception.class) public void savePackage(ExchangeCodePackageSaveDTO dto) { validateSaveDTO(dto, false); TExchangeCodePackage entity = buildPackageEntity(dto, new TExchangeCodePackage()); this.save(entity); saveSpecsAndCodes(entity.getId(), dto.getSpecList(), dto.getInventory(), null); refreshPackageStats(entity.getId()); } @Override @Transactional(rollbackFor = Exception.class) public void updatePackage(ExchangeCodePackageSaveDTO dto) { if (dto.getId() == null) { throw new ServiceException("卡包ID不能为空"); } validateSaveDTO(dto, true); TExchangeCodePackage old = this.getById(dto.getId()); if (old == null) { throw new ServiceException("卡包不存在"); } if (dto.getInventory() < old.getInventory()) { throw new ServiceException("编辑时库存只能增加"); } TExchangeCodePackage entity = buildPackageEntity(dto, old); this.updateById(entity); packageSpecMapper.delete(new QueryWrapper<TExchangeCodePackageSpec>().eq("package_id", dto.getId())); saveSpecsAndCodes(dto.getId(), dto.getSpecList(), dto.getInventory(), old.getInventory()); refreshPackageStats(dto.getId()); } @Override @Transactional(rollbackFor = Exception.class) public void deletePackage(String ids) { if (!StringUtils.hasText(ids)) { throw new ServiceException("卡包ID不能为空"); } List<Long> idList = Arrays.stream(ids.split(",")) .filter(StringUtils::hasText) .map(Long::valueOf) .collect(Collectors.toList()); if (idList.isEmpty()) { throw new ServiceException("卡包ID不能为空"); } Long boundCount = codeDetailMapper.selectCount(new QueryWrapper<TExchangeCodeDetail>() .in("package_id", idList) .eq("status", CODE_STATUS_BOUND) .eq("del_flag", 0)); if (boundCount != null && boundCount > 0) { throw new ServiceException("存在已绑定兑换码,不允许删除"); } this.removeByIds(idList); packageSpecMapper.delete(new QueryWrapper<TExchangeCodePackageSpec>().in("package_id", idList)); codeDetailMapper.delete(new QueryWrapper<TExchangeCodeDetail>().in("package_id", idList)); } @Override public ExchangeCodePackageDetailVO getDetail(Long id) { TExchangeCodePackage entity = this.getById(id); if (entity == null) { throw new ServiceException("卡包不存在"); } refreshPackageStats(id); entity = this.getById(id); ExchangeCodePackageDetailVO detailVO = new ExchangeCodePackageDetailVO(); BeanUtils.copyProperties(entity, detailVO); List<TExchangeCodePackageSpec> specs = packageSpecMapper.selectList(new QueryWrapper<TExchangeCodePackageSpec>() .eq("package_id", id) .orderByAsc("sort", "id")); List<ExchangeCodePackageSpecVO> specVOList = new ArrayList<>(); for (TExchangeCodePackageSpec spec : specs) { ExchangeCodePackageSpecVO vo = new ExchangeCodePackageSpecVO(); BeanUtils.copyProperties(spec, vo); Long boundCount = codeDetailMapper.selectCount(new QueryWrapper<TExchangeCodeDetail>() .eq("package_spec_id", spec.getId()) .eq("status", CODE_STATUS_BOUND) .eq("del_flag", 0)); Long unboundCount = codeDetailMapper.selectCount(new QueryWrapper<TExchangeCodeDetail>() .eq("package_spec_id", spec.getId()) .eq("status", CODE_STATUS_UNBOUND) .eq("del_flag", 0)); vo.setBoundCount(boundCount == null ? 0 : boundCount.intValue()); vo.setUnboundCount(unboundCount == null ? 0 : unboundCount.intValue()); specVOList.add(vo); } detailVO.setSpecList(specVOList); return detailVO; } @Override public PageInfo<ExchangeCodeDetailVO> codePage(ExchangeCodeDetailQuery query) { PageInfo<ExchangeCodeDetailVO> pageInfo = new PageInfo<>(query.getPageCurr(), query.getPageSize()); List<ExchangeCodeDetailVO> records = codeDetailMapper.pageList(pageInfo, query); pageInfo.setRecords(records); return pageInfo; } @Override public List<ExchangeCodeDetailVO> exportCodes(ExchangeCodeDetailQuery query) { return codeDetailMapper.exportList(query); } @Override @Transactional(rollbackFor = Exception.class) public ExchangeCodeBatchInvalidResultVO batchInvalidate(ExchangeCodeBatchInvalidDTO dto) { if (dto.getPackageId() == null || dto.getDetailIds() == null || dto.getDetailIds().isEmpty()) { throw new ServiceException("请选择需要失效的兑换码"); } int success = 0; int skip = 0; List<TExchangeCodeDetail> details = codeDetailMapper.selectBatchIds(dto.getDetailIds()); for (TExchangeCodeDetail detail : details) { if (!Objects.equals(detail.getPackageId(), dto.getPackageId())) { continue; } if (Objects.equals(detail.getStatus(), CODE_STATUS_UNBOUND)) { detail.setStatus(CODE_STATUS_INVALID); detail.setInvalidTime(LocalDateTime.now()); detail.setInvalidReason("后台批量失效"); codeDetailMapper.updateById(detail); success++; } else { skip++; } } refreshPackageStats(dto.getPackageId()); return new ExchangeCodeBatchInvalidResultVO(success, skip); } @Override public List<ExchangeCodeDetailVO> listDownloadableCodes(Long packageId, boolean onlyUnbound) { ExchangeCodeDetailQuery query = new ExchangeCodeDetailQuery(); query.setPackageId(packageId); if (onlyUnbound) { query.setStatus(CODE_STATUS_UNBOUND); } return exportCodes(query); } @Override public ExchangeCodeDetailVO getCodeDetail(Long id) { TExchangeCodeDetail detail = codeDetailMapper.selectById(id); if (detail == null || Boolean.TRUE.equals(detail.getDelFlag())) { throw new ServiceException("兑换码不存在"); } ExchangeCodeDetailVO vo = new ExchangeCodeDetailVO(); BeanUtils.copyProperties(detail, vo); if (detail.getPackageSpecId() != null) { TExchangeCodePackageSpec spec = packageSpecMapper.selectById(detail.getPackageSpecId()); if (spec != null) { vo.setAmount(spec.getAmount()); vo.setQuantity(spec.getQuantity()); } } return vo; } @Override @Transactional(rollbackFor = Exception.class) public ExchangeCodeBindResultVO bindExchangeCode(ExchangeCodeBindDTO dto, Long userId, String phone) { if (!StringUtils.hasText(dto.getCode()) || !StringUtils.hasText(dto.getPassword())) { throw new ServiceException("兑换码或密码不能为空"); } TExchangeCodeDetail detail = codeDetailMapper.selectOne(new QueryWrapper<TExchangeCodeDetail>() .eq("code", dto.getCode()) .eq("password", dto.getPassword()) .last("limit 1")); if (detail == null) { throw new ServiceException("兑换码或密码错误"); } TExchangeCodePackage exchangePackage = this.getById(detail.getPackageId()); if (exchangePackage == null || !Objects.equals(exchangePackage.getStatus(), STATUS_ENABLE)) { throw new ServiceException("卡包已下架"); } if (exchangePackage.getExpireTime() != null && exchangePackage.getExpireTime().isBefore(LocalDateTime.now())) { throw new ServiceException("已失效"); } if (Objects.equals(detail.getStatus(), CODE_STATUS_INVALID)) { throw new ServiceException("卡号失效"); } if (Objects.equals(detail.getStatus(), CODE_STATUS_BOUND)) { throw new ServiceException("兑换码已绑定"); } String bindPhone = StringUtils.hasText(dto.getPhone()) ? dto.getPhone() : phone; if (!StringUtils.hasText(bindPhone)) { throw new ServiceException("绑定手机号不能为空"); } if (Objects.equals(exchangePackage.getUnlimitedReceiveLimit(), 0) && exchangePackage.getReceiveLimit() != null) { Long bindCount = codeDetailMapper.selectCount(new QueryWrapper<TExchangeCodeDetail>() .eq("package_id", exchangePackage.getId()) .eq("bind_phone", bindPhone) .eq("status", CODE_STATUS_BOUND) .eq("del_flag", 0)); if (bindCount >= exchangePackage.getReceiveLimit()) { throw new ServiceException("该手机号领取次数已达上限"); } } detail.setStatus(CODE_STATUS_BOUND); detail.setBindUserId(userId); detail.setBindPhone(bindPhone); detail.setBindTime(LocalDateTime.now()); codeDetailMapper.updateById(detail); refreshPackageStats(exchangePackage.getId()); ExchangeCodeBindResultVO resultVO = new ExchangeCodeBindResultVO(); resultVO.setDetailId(detail.getId()); resultVO.setPackageId(exchangePackage.getId()); resultVO.setPackageName(exchangePackage.getPackageName()); resultVO.setBindPhone(bindPhone); return resultVO; } @Override public Map<Long, BigDecimal> getUserExchangeCodeBalanceMap(List<Long> userIds) { Map<Long, BigDecimal> result = new HashMap<>(); if (userIds == null || userIds.isEmpty()) { return result; } List<TExchangeCodeDetail> details = codeDetailMapper.selectList(new QueryWrapper<TExchangeCodeDetail>() .in("bind_user_id", userIds) .eq("status", CODE_STATUS_BOUND) .eq("del_flag", 0)); for (TExchangeCodeDetail detail : details) { BigDecimal remainAmount = getDetailRemainAmount(detail); if (remainAmount.compareTo(BigDecimal.ZERO) > 0) { result.merge(detail.getBindUserId(), remainAmount, BigDecimal::add); } } return result; } @Override @Transactional(rollbackFor = Exception.class) public ExchangeCodeConsumeResultVO consumeBalance(ExchangeCodeConsumeDTO dto) { if (dto == null || dto.getUserId() == null || dto.getOrderId() == null || dto.getAmount() == null) { throw new ServiceException("扣减参数不能为空"); } ExchangeCodeConsumeResultVO resultVO = new ExchangeCodeConsumeResultVO(); BigDecimal requestedAmount = dto.getAmount().setScale(2, RoundingMode.HALF_UP); if (requestedAmount.compareTo(BigDecimal.ZERO) <= 0) { resultVO.setDeductedAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)); return resultVO; } List<TExchangeCodeUseRecord> existingRecords = useRecordMapper.selectList(new QueryWrapper<TExchangeCodeUseRecord>() .eq("order_id", dto.getOrderId()) .eq("del_flag", 0)); if (!existingRecords.isEmpty()) { BigDecimal deducted = existingRecords.stream() .map(TExchangeCodeUseRecord::getUsedAmount) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add) .setScale(2, RoundingMode.HALF_UP); resultVO.setDeductedAmount(deducted); return resultVO; } BigDecimal remainingNeed = requestedAmount; BigDecimal deductedAmount = BigDecimal.ZERO; List<TExchangeCodeDetail> details = codeDetailMapper.selectList(new QueryWrapper<TExchangeCodeDetail>() .eq("bind_user_id", dto.getUserId()) .eq("status", CODE_STATUS_BOUND) .eq("del_flag", 0) .orderByAsc("bind_time", "id")); for (TExchangeCodeDetail detail : details) { if (remainingNeed.compareTo(BigDecimal.ZERO) <= 0) { break; } BigDecimal remainAmount = getDetailRemainAmount(detail); if (remainAmount.compareTo(BigDecimal.ZERO) <= 0) { continue; } BigDecimal useAmount = remainAmount.min(remainingNeed).setScale(2, RoundingMode.HALF_UP); detail.setUsedAmount(defaultMoney(detail.getUsedAmount()).add(useAmount).setScale(2, RoundingMode.HALF_UP)); codeDetailMapper.updateById(detail); TExchangeCodeUseRecord record = new TExchangeCodeUseRecord(); record.setDetailId(detail.getId()); record.setOrderId(dto.getOrderId()); record.setUserId(dto.getUserId()); record.setUsedAmount(useAmount); useRecordMapper.insert(record); remainingNeed = remainingNeed.subtract(useAmount); deductedAmount = deductedAmount.add(useAmount); } resultVO.setDeductedAmount(deductedAmount.setScale(2, RoundingMode.HALF_UP)); return resultVO; } private BigDecimal getDetailRemainAmount(TExchangeCodeDetail detail) { if (detail == null || detail.getPackageSpecId() == null) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } TExchangeCodePackageSpec spec = packageSpecMapper.selectById(detail.getPackageSpecId()); if (spec == null || spec.getAmount() == null) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } BigDecimal remainAmount = spec.getAmount().subtract(defaultMoney(detail.getUsedAmount())); return remainAmount.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : remainAmount.setScale(2, RoundingMode.HALF_UP); } private TExchangeCodePackage buildPackageEntity(ExchangeCodePackageSaveDTO dto, TExchangeCodePackage entity) { entity.setPackageName(dto.getPackageName()); entity.setCoverPicture(dto.getCoverPicture()); entity.setDetailsPicture(dto.getDetailsPicture()); entity.setExchangeDescription(dto.getExchangeDescription()); entity.setRemark(dto.getRemark()); entity.setInventory(dto.getInventory()); entity.setUnlimitedReceiveLimit(dto.getUnlimitedReceiveLimit()); entity.setReceiveLimit(Objects.equals(dto.getUnlimitedReceiveLimit(), 1) ? null : dto.getReceiveLimit()); entity.setFinanceReceivedAmount(defaultMoney(dto.getFinanceReceivedAmount())); entity.setGiftAmount(defaultMoney(dto.getGiftAmount())); entity.setTotalAmount(defaultMoney(dto.getTotalAmount())); entity.setExpireTime(dto.getExpireTime()); entity.setStatus(dto.getStatus()); return entity; } private void validateSaveDTO(ExchangeCodePackageSaveDTO dto, boolean editing) { if (!StringUtils.hasText(dto.getPackageName())) { throw new ServiceException("请输入卡包名称"); } if (dto.getInventory() == null || dto.getInventory() <= 0) { throw new ServiceException("库存必须大于0"); } if (dto.getExpireTime() != null && dto.getExpireTime().isBefore(LocalDateTime.now())) { throw new ServiceException("截止领取时间不能早于当前时间"); } if (!Objects.equals(dto.getUnlimitedReceiveLimit(), 1) && (dto.getReceiveLimit() == null || dto.getReceiveLimit() <= 0)) { throw new ServiceException("请输入领取限制次数"); } if (dto.getSpecList() == null || dto.getSpecList().isEmpty()) { throw new ServiceException("请至少添加一个规格"); } if (dto.getStatus() == null) { dto.setStatus(STATUS_DISABLE); } BigDecimal finance = defaultMoney(dto.getFinanceReceivedAmount()); BigDecimal gift = defaultMoney(dto.getGiftAmount()); BigDecimal total = defaultMoney(dto.getTotalAmount()); if (finance.add(gift).compareTo(total) != 0) { throw new ServiceException("财务实收 + 赠送金额 必须等于规格总金额"); } BigDecimal specTotal = BigDecimal.ZERO; int quantityTotal = 0; for (ExchangeCodePackageSpecDTO spec : dto.getSpecList()) { if (spec.getAmount() == null || spec.getAmount().compareTo(BigDecimal.ZERO) < 0) { throw new ServiceException("规格金额不正确"); } if (spec.getQuantity() == null || spec.getQuantity() <= 0) { throw new ServiceException("规格数量必须大于0"); } BigDecimal subtotal = spec.getAmount().multiply(BigDecimal.valueOf(spec.getQuantity())) .setScale(2, RoundingMode.HALF_UP); spec.setSubtotalAmount(subtotal); specTotal = specTotal.add(subtotal); quantityTotal += spec.getQuantity(); } if (specTotal.compareTo(total) != 0) { throw new ServiceException("总额必须等于所有规格金额乘数量之和"); } if (quantityTotal != dto.getInventory()) { throw new ServiceException(editing ? "编辑后的库存必须等于规格数量总和" : "库存必须等于规格数量总和"); } allocateDiscount(dto.getSpecList(), gift, total); } private void allocateDiscount(List<ExchangeCodePackageSpecDTO> specList, BigDecimal giftAmount, BigDecimal totalAmount) { if (giftAmount.compareTo(BigDecimal.ZERO) == 0 || totalAmount.compareTo(BigDecimal.ZERO) == 0) { for (ExchangeCodePackageSpecDTO spec : specList) { spec.setDiscountAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)); } return; } BigDecimal allocated = BigDecimal.ZERO; for (int i = 0; i < specList.size(); i++) { ExchangeCodePackageSpecDTO spec = specList.get(i); BigDecimal discount; if (i == specList.size() - 1) { discount = giftAmount.subtract(allocated).setScale(2, RoundingMode.HALF_UP); } else { discount = spec.getSubtotalAmount().multiply(giftAmount) .divide(totalAmount, 2, RoundingMode.HALF_UP); allocated = allocated.add(discount); } spec.setDiscountAmount(discount); } } private void saveSpecsAndCodes(Long packageId, List<ExchangeCodePackageSpecDTO> specList, Integer inventory, Integer oldInventory) { List<TExchangeCodePackageSpec> specEntities = new ArrayList<>(); for (int i = 0; i < specList.size(); i++) { ExchangeCodePackageSpecDTO spec = specList.get(i); TExchangeCodePackageSpec entity = new TExchangeCodePackageSpec(); entity.setPackageId(packageId); entity.setAmount(spec.getAmount()); entity.setQuantity(spec.getQuantity()); entity.setSubtotalAmount(spec.getSubtotalAmount()); entity.setDiscountAmount(spec.getDiscountAmount()); entity.setSort(spec.getSort() == null ? i + 1 : spec.getSort()); packageSpecMapper.insert(entity); specEntities.add(entity); } int needCreate = oldInventory == null ? inventory : inventory - oldInventory; if (needCreate <= 0) { return; } String batchNo = "PKG" + LocalDateTime.now().format(BATCH_FORMATTER); List<TExchangeCodePackageSpec> expandList = new ArrayList<>(); for (TExchangeCodePackageSpec spec : specEntities) { for (int i = 0; i < spec.getQuantity(); i++) { expandList.add(spec); } } if (expandList.isEmpty()) { expandList = Collections.nCopies(needCreate, null); } for (int i = 0; i < needCreate; i++) { TExchangeCodePackageSpec spec = expandList.get(i % expandList.size()); TExchangeCodeDetail detail = new TExchangeCodeDetail(); detail.setPackageId(packageId); detail.setPackageSpecId(spec == null ? null : spec.getId()); detail.setCode(generateCode(packageId, i)); detail.setPassword(generatePassword()); detail.setStatus(CODE_STATUS_UNBOUND); detail.setBatchNo(batchNo); codeDetailMapper.insert(detail); } } private void refreshPackageStats(Long packageId) { TExchangeCodePackage entity = this.getById(packageId); if (entity == null) { return; } Long boundCount = codeDetailMapper.selectCount(new QueryWrapper<TExchangeCodeDetail>() .eq("package_id", packageId) .eq("status", CODE_STATUS_BOUND) .eq("del_flag", 0)); Long invalidCount = codeDetailMapper.selectCount(new QueryWrapper<TExchangeCodeDetail>() .eq("package_id", packageId) .eq("status", CODE_STATUS_INVALID) .eq("del_flag", 0)); entity.setBoundCount(boundCount.intValue()); entity.setInvalidCount(invalidCount.intValue()); entity.setUnboundCount(entity.getInventory() - entity.getBoundCount() - entity.getInvalidCount()); this.baseMapper.updateById(entity); } private String generateCode(Long packageId, int seq) { return "CD" + packageId + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyMMddHHmmss")) + String.format("%03d", seq); } private String generatePassword() { return UUID.randomUUID().toString().replace("-", "").substring(0, 8).toUpperCase(); } private BigDecimal defaultMoney(BigDecimal amount) { return amount == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : amount.setScale(2, RoundingMode.HALF_UP); } } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/util/QRCodeUtils.java
New file @@ -0,0 +1,43 @@ package com.ruoyi.other.util; import com.google.zxing.BarcodeFormat; import com.google.zxing.EncodeHintType; import com.google.zxing.MultiFormatWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.OutputStream; import java.util.Hashtable; public class QRCodeUtils { private static final String CHARSET = "utf-8"; private static final String FORMAT_NAME = "JPG"; private static final int QRCODE_SIZE = 300; private QRCodeUtils() { } public static void encode(String content, OutputStream output) throws Exception { Hashtable<EncodeHintType, Object> hints = new Hashtable<>(); hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.H); hints.put(EncodeHintType.CHARACTER_SET, CHARSET); hints.put(EncodeHintType.MARGIN, 1); BitMatrix bitMatrix = new MultiFormatWriter().encode( content, BarcodeFormat.QR_CODE, QRCODE_SIZE, QRCODE_SIZE, hints ); BufferedImage image = new BufferedImage(QRCODE_SIZE, QRCODE_SIZE, BufferedImage.TYPE_INT_RGB); for (int x = 0; x < QRCODE_SIZE; x++) { for (int y = 0; y < QRCODE_SIZE; y++) { image.setRGB(x, y, bitMatrix.get(x, y) ? 0xFF000000 : 0xFFFFFFFF); } } ImageIO.write(image, FORMAT_NAME, output); } } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ExchangeCodeBatchInvalidResultVO.java
New file @@ -0,0 +1,20 @@ package com.ruoyi.other.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @Data @NoArgsConstructor @AllArgsConstructor @ApiModel("兑换码批量失效结果VO") public class ExchangeCodeBatchInvalidResultVO { @ApiModelProperty("成功失效数量") private Integer successCount; @ApiModelProperty("跳过数量") private Integer skipCount; } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ExchangeCodeDetailExportVO.java
New file @@ -0,0 +1,41 @@ package com.ruoyi.other.vo; import cn.afterturn.easypoi.excel.annotation.Excel; import io.swagger.annotations.ApiModel; import lombok.Data; import java.io.Serializable; import java.math.BigDecimal; import java.time.LocalDateTime; @Data @ApiModel("兑换码导出VO") public class ExchangeCodeDetailExportVO implements Serializable { @Excel(name = "兑换码", width = 30) private String code; @Excel(name = "密码", width = 20) private String password; @Excel(name = "金额", width = 20) private BigDecimal amount; @Excel(name = "数量", width = 15) private Integer quantity; @Excel(name = "状态", replace = {"未绑定_1", "已绑定_2", "已失效_3"}, width = 20) private Integer status; @Excel(name = "绑定手机号", width = 20) private String bindPhone; @Excel(name = "绑定时间", exportFormat = "yyyy-MM-dd HH:mm:ss", width = 25) private LocalDateTime bindTime; @Excel(name = "失效时间", exportFormat = "yyyy-MM-dd HH:mm:ss", width = 25) private LocalDateTime invalidTime; @Excel(name = "失效原因", width = 25) private String invalidReason; } ruoyi-service/ruoyi-other/src/main/java/com/ruoyi/other/vo/ExchangeCodeDetailVO.java
New file @@ -0,0 +1,48 @@ package com.ruoyi.other.vo; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import java.time.LocalDateTime; @Data @ApiModel("兑换码明细VO") public class ExchangeCodeDetailVO { @ApiModelProperty("明细ID") private Long id; @ApiModelProperty("卡包ID") private Long packageId; @ApiModelProperty("规格ID") private Long packageSpecId; @ApiModelProperty("金额") private java.math.BigDecimal amount; @ApiModelProperty("数量") private Integer quantity; @ApiModelProperty("兑换码") private String code; @ApiModelProperty("密码") private String password; @ApiModelProperty("状态 1未绑定 2已绑定 3已失效") private Integer status; @ApiModelProperty("绑定手机号") private String bindPhone; @ApiModelProperty("绑定时间") private LocalDateTime bindTime; @ApiModelProperty("失效时间") private LocalDateTime invalidTime; @ApiModelProperty("失效原因") private String invalidReason; } ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TExchangeCodeDetailMapper.xml
New file @@ -0,0 +1,60 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.other.mapper.TExchangeCodeDetailMapper"> <sql id="BaseQuery"> from t_exchange_code_detail d left join t_exchange_code_package_spec s on s.id = d.package_spec_id and s.del_flag = 0 <where> and d.del_flag = 0 <if test="req.packageId != null"> and d.package_id = #{req.packageId} </if> <if test="req.code != null and req.code != ''"> and d.code like concat('%', #{req.code}, '%') </if> <if test="req.bindPhone != null and req.bindPhone != ''"> and d.bind_phone like concat('%', #{req.bindPhone}, '%') </if> <if test="req.status != null"> and d.status = #{req.status} </if> </where> </sql> <select id="pageList" resultType="com.ruoyi.other.vo.ExchangeCodeDetailVO"> select d.id, d.package_id, d.package_spec_id, s.amount, s.quantity, d.code, d.password, d.status, d.bind_phone, d.bind_time, d.invalid_time, d.invalid_reason <include refid="BaseQuery"/> order by d.create_time desc, d.id desc </select> <select id="exportList" resultType="com.ruoyi.other.vo.ExchangeCodeDetailVO"> select d.id, d.package_id, d.package_spec_id, s.amount, s.quantity, d.code, d.password, d.status, d.bind_phone, d.bind_time, d.invalid_time, d.invalid_reason <include refid="BaseQuery"/> order by d.create_time desc, d.id desc </select> </mapper> ruoyi-service/ruoyi-other/src/main/resources/mapper/other/TExchangeCodePackageMapper.xml
New file @@ -0,0 +1,24 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.ruoyi.other.mapper.TExchangeCodePackageMapper"> <select id="pageList" resultType="com.ruoyi.other.api.domain.TExchangeCodePackage"> select p.*, ( select count(1) from t_exchange_code_detail d where d.package_id = p.id and d.status = 1 and d.del_flag = 0 ) as unbound_count from t_exchange_code_package p <where> <if test="req.packageName != null and req.packageName != ''"> and p.package_name like concat('%', #{req.packageName}, '%') </if> <if test="req.status != null"> and p.status = #{req.status} </if> and p.del_flag = 0 </where> order by p.create_time desc </select> </mapper>