Browse Source

优化扣减积分接口

master
glx 1 month ago
parent
commit
e906feb31d
  1. 86
      src/main/java/com/youlai/boot/admin/service/impl/PointManageServiceImpl.java
  2. 18
      src/main/java/com/youlai/boot/mini/controller/MiniUserController.java
  3. 13
      src/main/java/com/youlai/boot/mini/controller/PointController.java
  4. 21
      src/main/java/com/youlai/boot/mini/model/form/MiniDeductPointForm.java
  5. 3
      src/main/java/com/youlai/boot/mini/model/form/MiniUserUpdateForm.java
  6. 32
      src/main/java/com/youlai/boot/mini/service/MiniPointRecordService.java
  7. 5
      src/main/java/com/youlai/boot/mini/service/MiniUserService.java
  8. 122
      src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java
  9. 2
      src/main/java/com/youlai/boot/mini/service/impl/MiniSignServiceImpl.java
  10. 53
      src/main/java/com/youlai/boot/mini/service/impl/MiniUserServiceImpl.java

86
src/main/java/com/youlai/boot/admin/service/impl/PointManageServiceImpl.java

@ -3,6 +3,7 @@ package com.youlai.boot.admin.service.impl;
import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@ -28,6 +29,7 @@ import com.youlai.boot.mini.service.MiniPointAccountService;
import jodd.util.StringUtil; import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -138,50 +140,80 @@ public class PointManageServiceImpl extends ServiceImpl<MiniPointRuleMapper, Min
throw new MsgException("积分调整值不能为0"); throw new MsgException("积分调整值不能为0");
} }
// 2.根据用户主键userId查询积分账户 int changeAmount = form.getChangeAmount(); // 调整积分值
MiniPointAccount account = miniPointAccountService.getOne(new LambdaQueryWrapper<MiniPointAccount>() Long operateUserId = form.getUserId(); // 操作用户ID
.eq(MiniPointAccount::getUserId, form.getUserId()) boolean isDeduct = changeAmount < 0; // 是否为扣减
int updateCount; // 更新数量
MiniPointAccount account;
if (isDeduct) {
// 扣减场景:原子更新+积分校验,解决并发超扣
int deductAmount = -changeAmount; // 要扣的正数值
updateCount = miniPointAccountMapper.update(null, new LambdaUpdateWrapper<MiniPointAccount>()
.eq(MiniPointAccount::getUserId, operateUserId)
.eq(MiniPointAccount::getDeleted, false)
// 核心:当前积分 >= 扣减金额,保证扣完不会为负,原子操作靠数据库行锁隔离并发
.ge(MiniPointAccount::getPoints, deductAmount) // 当前积分 >= 扣减金额
.setSql("points = points + " + changeAmount)
.set(MiniPointAccount::getUpdateBy, SecurityUtils.getUserId())
.set(MiniPointAccount::getUpdateTime, new Date())
.set(MiniPointAccount::getUpdateTimestamp, System.currentTimeMillis())
);
if (updateCount == 0) {
// 更新行数为0:要么账户不存在(用户从来没获得过积分=0,扣减肯定失败),要么积分不足
throw new MsgException("积分不足,扣减后余额不能为负数");
}
// 查询更新后的最新账户信息,用于流水记录
account = miniPointAccountService.getOne(new LambdaQueryWrapper<MiniPointAccount>()
.eq(MiniPointAccount::getUserId, operateUserId)
.eq(MiniPointAccount::getDeleted, false)); .eq(MiniPointAccount::getDeleted, false));
// 账户不存在自动创建,新用户第一次获得积分的时候自动初始化账户 } else {
if (account == null) { // 增加积分场景:不需要校验余额,直接原子更新,避免并发少加
updateCount = miniPointAccountMapper.update(null, new LambdaUpdateWrapper<MiniPointAccount>()
.eq(MiniPointAccount::getUserId, operateUserId)
.eq(MiniPointAccount::getDeleted, false)
.setSql("points = points + " + changeAmount)
.set(MiniPointAccount::getUpdateBy, operateUserId)
.set(MiniPointAccount::getUpdateTime, new Date())
.set(MiniPointAccount::getUpdateTimestamp, System.currentTimeMillis())
);
if (updateCount == 0) {
// 账户不存在,自动创建(兼容原有新用户首次获得积分自动开户逻辑)
account = new MiniPointAccount(); account = new MiniPointAccount();
account.setUuid(IdUtil.fastSimpleUUID()); account.setUuid(IdUtil.fastSimpleUUID());
account.setUserId(form.getUserId()); account.setUserId(operateUserId);
account.setPoints(0); account.setPoints(changeAmount); // 新账户初始积分就是本次增加的
account.setCreateBy(form.getUserId()); account.setCreateBy(operateUserId);
account.setCreateTime(new Date()); account.setCreateTime(new Date());
account.setCreateTimestamp(System.currentTimeMillis()); account.setCreateTimestamp(System.currentTimeMillis());
miniPointAccountService.save(account); miniPointAccountService.save(account);
} else {
// 更新成功,查询最新账户信息
account = miniPointAccountService.getOne(new LambdaQueryWrapper<MiniPointAccount>()
.eq(MiniPointAccount::getUserId, operateUserId)
.eq(MiniPointAccount::getDeleted, false));
} }
// 4. 计算变更后余额,校验不能为负数
Integer afterBalance = account.getPoints() + form.getChangeAmount();
if (afterBalance < 0) {
throw new MsgException("积分不足,扣减后余额不能为负数");
} }
// 5. 更新用户积分账户余额 // 6. 插入积分流水记录(用最新查询的账户余额,保证流水和实际完全一致)
MiniPointAccount updateAccount = new MiniPointAccount();
updateAccount.setId(account.getId());
updateAccount.setPoints(afterBalance);
updateAccount.setUpdateBy(SecurityUtils.getUserId());
updateAccount.setUpdateTime(new Date());
updateAccount.setUpdateTimestamp(System.currentTimeMillis());
miniPointAccountService.updateById(updateAccount);
// 6. 插入积分流水记录
MiniPointRecord record = new MiniPointRecord(); MiniPointRecord record = new MiniPointRecord();
record.setUuid(IdUtil.fastSimpleUUID()); record.setUuid(IdUtil.fastSimpleUUID());
record.setUserId(account.getUserId()); record.setUserId(account.getUserId());
record.setChangeAmount(form.getChangeAmount()); record.setChangeAmount(changeAmount);
record.setBalanceAfter(afterBalance); record.setBalanceAfter(account.getPoints()); // 直接用最新的账户积分,不用自己计算,避免不一致
record.setBizType(form.getBizType()); record.setBizType(form.getBizType());
// 优先用传进来的业务唯一ID(比如AI任务ID),没有的话自动生成,保证幂等 // 优先用传进来的业务唯一ID,没有的话自动生成,保证幂等
record.setBizId(StrUtil.isNotBlank(form.getBizId()) ? form.getBizId() : UUID.randomUUID().toString().replace("-", "")); record.setBizId(StrUtil.isNotBlank(form.getBizId()) ? form.getBizId() : UUID.randomUUID().toString().replace("-", ""));
record.setCreateBy(SecurityUtils.getUserId()); record.setCreateBy(operateUserId);
record.setCreateTime(new Date()); record.setCreateTime(new Date());
record.setCreateTimestamp(System.currentTimeMillis()); record.setCreateTimestamp(System.currentTimeMillis());
try {
miniPointRecordMapper.insert(record); miniPointRecordMapper.insert(record);
} catch (DuplicateKeyException e) {
// bizId 唯一索引冲突,说明是重复请求
log.warn("重复请求,bizId: {}", record.getBizId());
throw new MsgException("请勿重复提交");
}
} }
@Override @Override

18
src/main/java/com/youlai/boot/mini/controller/MiniUserController.java

@ -1,6 +1,7 @@
package com.youlai.boot.mini.controller; package com.youlai.boot.mini.controller;
import com.youlai.boot.common.annotation.Log; import com.youlai.boot.common.annotation.Log;
import com.youlai.boot.common.annotation.RepeatSubmit;
import com.youlai.boot.common.enums.ActionTypeEnum; import com.youlai.boot.common.enums.ActionTypeEnum;
import com.youlai.boot.common.enums.LogModuleEnum; import com.youlai.boot.common.enums.LogModuleEnum;
import com.youlai.boot.common.result.Result; import com.youlai.boot.common.result.Result;
@ -14,6 +15,9 @@ import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/** /**
* C端用户信息接口 * C端用户信息接口
@ -40,7 +44,7 @@ public class MiniUserController {
} }
@Operation(summary = "修改当前登录用户基本信息") @Operation(summary = "修改当前登录用户基本信息")
@PostMapping(value = "/updateInfo", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) @PostMapping(value = "/updateInfo")
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.UPDATE) @Log(module = LogModuleEnum.USER, value = ActionTypeEnum.UPDATE)
public Result<Void> updateCurrentUserInfo(@Valid MiniUserUpdateForm form) { public Result<Void> updateCurrentUserInfo(@Valid MiniUserUpdateForm form) {
Long userId = SecurityUtils.getUserId(); Long userId = SecurityUtils.getUserId();
@ -48,4 +52,16 @@ public class MiniUserController {
return Result.success(); return Result.success();
} }
@Operation(summary = "上传用户头像")
@PostMapping(value = "save/avatar", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
@RepeatSubmit
@Log(module = LogModuleEnum.USER, value = ActionTypeEnum.UPDATE)
public Result<?> saveFile(
@RequestPart(name = "image", required = true) MultipartFile image
) {
Long userId = SecurityUtils.getUserId();
miniUserService.saveFile(userId, image);
return Result.success();
}
} }

13
src/main/java/com/youlai/boot/mini/controller/PointController.java

@ -8,6 +8,7 @@ import com.youlai.boot.common.result.PageResult;
import com.youlai.boot.common.result.Result; import com.youlai.boot.common.result.Result;
import com.youlai.boot.framework.security.util.SecurityUtils; import com.youlai.boot.framework.security.util.SecurityUtils;
import com.youlai.boot.mini.model.query.MyPointRecordQuery; import com.youlai.boot.mini.model.query.MyPointRecordQuery;
import com.youlai.boot.mini.model.form.MiniDeductPointForm;
import com.youlai.boot.mini.model.vo.MyPointRecordVO; import com.youlai.boot.mini.model.vo.MyPointRecordVO;
import com.youlai.boot.mini.model.vo.MyPointVO; import com.youlai.boot.mini.model.vo.MyPointVO;
import com.youlai.boot.mini.model.vo.SignResultVO; import com.youlai.boot.mini.model.vo.SignResultVO;
@ -18,6 +19,7 @@ import com.youlai.boot.mini.service.MiniSignService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject; import org.springdoc.core.annotations.ParameterObject;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -83,13 +85,12 @@ public class PointController {
return Result.success(point); return Result.success(point);
} }
@Operation(summary = "AI生成扣除积分") @Operation(summary = "通用扣减积分接口")
@PostMapping("/ai-generate/deduct") @PostMapping("/deduct")
@Log(module = LogModuleEnum.OTHER, value = ActionTypeEnum.OTHER) @Log(module = LogModuleEnum.POINT_RECORD, value = ActionTypeEnum.UPDATE, title = "通用扣减积分")
public Result<Integer> aiGenerateImageDeduct( public Result<Integer> deductPoint(@Valid @RequestBody MiniDeductPointForm form) {
@Parameter(description = "AI生成任务唯一ID(用于幂等)", required = true) @RequestParam String taskId) {
Long userId = SecurityUtils.getUserId(); Long userId = SecurityUtils.getUserId();
Integer deductPoint = pointRecordService.deductAiGenerateImagePoint(userId, taskId); Integer deductPoint = pointRecordService.deductPoint(userId, form.getRuleCode(), form.getBizId());
return Result.success(deductPoint); return Result.success(deductPoint);
} }

21
src/main/java/com/youlai/boot/mini/model/form/MiniDeductPointForm.java

@ -0,0 +1,21 @@
package com.youlai.boot.mini.model.form;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Data
@Schema(description = "通用扣积分请求")
public class MiniDeductPointForm {
@Schema(description = "积分规则编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "AI_GENERATE_IMAGE")
@NotBlank(message = "规则编码不能为空")
@Size(max = 50, message = "规则编码长度不能超过50字符")
private String ruleCode;
@Schema(description = "业务唯一ID(幂等用,确保同一次业务请求唯一)", requiredMode = Schema.RequiredMode.REQUIRED, example = "task_123456")
@NotBlank(message = "业务ID不能为空")
@Size(max = 100, message = "业务ID长度不能超过100字符")
private String bizId;
}

3
src/main/java/com/youlai/boot/mini/model/form/MiniUserUpdateForm.java

@ -14,9 +14,6 @@ public class MiniUserUpdateForm {
@Size(max = 20, message = "昵称长度不能超过20个字符") @Size(max = 20, message = "昵称长度不能超过20个字符")
private String nickname; private String nickname;
@Schema(description = "头像文件")
private MultipartFile avatar;
@Schema(description = "性别:0-未知 1-男 2-女") @Schema(description = "性别:0-未知 1-男 2-女")
private Integer gender; private Integer gender;
} }

32
src/main/java/com/youlai/boot/mini/service/MiniPointRecordService.java

@ -8,42 +8,12 @@ import com.youlai.boot.mini.model.vo.MyPointRecordVO;
public interface MiniPointRecordService extends IService<MiniPointRecord> { public interface MiniPointRecordService extends IService<MiniPointRecord> {
/**
* 后台手动调整用户积分
* @param form 调整参数
*/
// void adjustPoint(AdjustUserPointForm form);
/**
* 分页查询当前用户的积分流水用户端专属
* @param userId 当前登录用户ID内部传参避免越权
* @param query 用户端查询条件
* @return 用户端积分流水分页
*/
IPage<MyPointRecordVO> pageMyPointRecord(Long userId, MyPointRecordQuery query); IPage<MyPointRecordVO> pageMyPointRecord(Long userId, MyPointRecordQuery query);
/**
* 发放分享奖励
* @param userId 用户ID
* @return 获得的积分
*/
Integer giveShareReward(Long userId); Integer giveShareReward(Long userId);
/**
* 登记流浪动物领取积分
* @param userId 用户ID
* @param animalId 登记的动物ID幂等用防止同一动物重复领积分
* @return 获得的积分0表示没有获得次数超限/已经领过
*/
Integer giveRegisterAnimalReward(Long userId, Long animalId); Integer giveRegisterAnimalReward(Long userId, Long animalId);
/** Integer deductPoint(Long userId, String ruleCode, String bizId);
* AI生成图片扣除用户积分
* @param userId 用户ID
* @param taskId AI生成任务唯一ID幂等用防止重复扣费
* @return 扣除的积分值负数大于等于0表示扣除失败积分不足/已经扣过
*/
Integer deductAiGenerateImagePoint(Long userId, String taskId);
} }

5
src/main/java/com/youlai/boot/mini/service/MiniUserService.java

@ -2,10 +2,15 @@ package com.youlai.boot.mini.service;
import com.youlai.boot.mini.model.form.MiniUserUpdateForm; import com.youlai.boot.mini.model.form.MiniUserUpdateForm;
import com.youlai.boot.mini.model.vo.MiniUserInfoVO; import com.youlai.boot.mini.model.vo.MiniUserInfoVO;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
public interface MiniUserService { public interface MiniUserService {
MiniUserInfoVO getCurrentUserInfo(Long userId); MiniUserInfoVO getCurrentUserInfo(Long userId);
void updateCurrentUserInfo(Long userId, MiniUserUpdateForm form); void updateCurrentUserInfo(Long userId, MiniUserUpdateForm form);
void saveFile(Long userId, MultipartFile image);
} }

122
src/main/java/com/youlai/boot/mini/service/impl/MiniPointRecordServiceImpl.java

@ -19,6 +19,7 @@ import com.youlai.boot.mini.service.MiniPointAccountService;
import com.youlai.boot.mini.service.MiniPointRecordService; import com.youlai.boot.mini.service.MiniPointRecordService;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -50,6 +51,7 @@ public class MiniPointRecordServiceImpl extends ServiceImpl<MiniPointRecordMappe
/** /**
* 简单封装通用周期校验 * 简单封装通用周期校验
*
* @param ruleCode 规则编码 * @param ruleCode 规则编码
* @param userId 用户ID * @param userId 用户ID
* @param bizPrefix 业务前缀避免不同业务Key冲突 * @param bizPrefix 业务前缀避免不同业务Key冲突
@ -233,82 +235,6 @@ public class MiniPointRecordServiceImpl extends ServiceImpl<MiniPointRecordMappe
return rewardPoint; return rewardPoint;
} }
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deductAiGenerateImagePoint(Long userId, String taskId) {
try {
// 1. 幂等校验:同一个任务ID只能扣一次费,防止重试重复扣费
String idempotentKey = String.format("reward:ai_image:idempotent:%s", taskId);
boolean alreadyDeducted = false;
try {
alreadyDeducted = Boolean.TRUE.equals(redisTemplate.hasKey(idempotentKey));
} catch (Exception e) {
// Redis异常查数据库兜底:查bizId等于taskId的流水
long count = count(new LambdaQueryWrapper<MiniPointRecord>()
.eq(MiniPointRecord::getBizType, "AI_GENERATE_IMAGE")
.eq(MiniPointRecord::getBizId, taskId)
.eq(MiniPointRecord::getDeleted, 0));
alreadyDeducted = count > 0;
}
if (alreadyDeducted) {
throw new MsgException("任务已处理");
}
// 2. 调用通用周期校验,规则编码AI_GENERATE_IMAGE,业务前缀ai_generate_image
Object[] checkResult = checkPeriodLimit("AI_GENERATE_IMAGE", userId, "ai_generate_image");
boolean allow = (boolean) checkResult[0];
int deductPoint = (int) checkResult[1]; // 规则配置的扣除积分数,应该是负数
String countKey = (String) checkResult[2];
long expireDays = (long) checkResult[3];
String limitPeriod = (String) checkResult[4];
boolean redisNormal = (boolean) checkResult[6];
// 校验不通过(次数超限/规则未配置)或者扣除的积分>=0(规则配置错误),返回0
if (!allow || deductPoint >= 0) {
throw new MsgException("积分规则配置错误");
}
// 3. 检查用户积分是否足够
LambdaQueryWrapper<MiniPointAccount> pointAccountQuery = new LambdaQueryWrapper<>();
pointAccountQuery.eq(MiniPointAccount::getUserId, userId);
pointAccountQuery.last("LIMIT 1");
MiniPointAccount account = miniPointAccountMapper.selectOne(pointAccountQuery);
// 账户不存在积分就是0,扣除后为负说明积分不足
if (account == null || account.getPoints() + deductPoint < 0) {
throw new MsgException("用户积分不足");
}
// 4. 执行扣费
AdjustUserPointForm adjustForm = new AdjustUserPointForm();
adjustForm.setUserId(userId);
adjustForm.setBizType("AI_GENERATE_IMAGE");
adjustForm.setChangeAmount(deductPoint);
adjustForm.setBizId(taskId);
// adjustForm.setRemark("AI生成图片扣费,任务ID:" + taskId);
pointManageService.adjustPoint(adjustForm);
// 5. 更新周期计数缓存
if (countKey != null && expireDays > 0 && redisNormal) {
redisTemplate.opsForValue().increment(countKey, 1);
if ("DAY".equals(limitPeriod)) {
redisTemplate.expire(countKey, 25, TimeUnit.HOURS);
} else {
redisTemplate.expire(countKey, expireDays, TimeUnit.DAYS);
}
}
// 6. 写入幂等标记,存1年足够
if (redisNormal) {
redisTemplate.opsForValue().set(idempotentKey, "1", 365, TimeUnit.DAYS);
}
return deductPoint; // 返回扣除的积分数,负数表示扣除成功
} catch (Exception e) {
log.error("用户{}AI生成图片任务{}扣费失败", userId, taskId, e);
return 0;
}
}
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public Integer giveRegisterAnimalReward(Long userId, Long animalId) { public Integer giveRegisterAnimalReward(Long userId, Long animalId) {
@ -352,4 +278,48 @@ public class MiniPointRecordServiceImpl extends ServiceImpl<MiniPointRecordMappe
} }
} }
@Override
@Transactional(rollbackFor = Exception.class)
public Integer deductPoint(Long userId, String ruleCode, String bizId) {
try {
// 1. 幂等前置校验:查询是否已经处理过
long existCount = count(new LambdaQueryWrapper<MiniPointRecord>()
.eq(MiniPointRecord::getBizType, ruleCode)
.eq(MiniPointRecord::getBizId, bizId)
.eq(MiniPointRecord::getDeleted, 0));
if (existCount > 0) {
throw new MsgException("业务已处理,请勿重复请求");
}
// 2. 查询积分规则并校验:必须存在、启用、且是扣费项目(points < 0)
MiniPointRule rule = pointManageService.getOne(new LambdaQueryWrapper<MiniPointRule>()
.eq(MiniPointRule::getRuleCode, ruleCode)
.eq(MiniPointRule::getStatus, false) // 启用状态
.last("LIMIT 1"));
if (rule == null) {
throw new MsgException("积分规则不存在");
}
if (rule.getPoints() >= 0) {
throw new MsgException("当前规则不是扣减类型,无法调用此接口");
}
// 3. 执行扣减:uk_biz_id唯一索引兜底幂等,adjustPoint内置原子积分校验,不会超扣
AdjustUserPointForm adjustForm = new AdjustUserPointForm();
adjustForm.setUserId(userId);
adjustForm.setBizType(ruleCode.toUpperCase());
adjustForm.setChangeAmount(rule.getPoints());
adjustForm.setBizId(bizId);
pointManageService.adjustPoint(adjustForm);
return rule.getPoints();
} catch (MsgException e) {
throw e;
} catch (DuplicateKeyException e) {
throw new MsgException("重复请求");
} catch (Exception e) {
log.error("用户{}扣积分失败,规则:{},业务ID:{}", userId, ruleCode, bizId, e);
throw new MsgException("积分扣除失败,请重试");
}
}
} }

2
src/main/java/com/youlai/boot/mini/service/impl/MiniSignServiceImpl.java

@ -91,7 +91,7 @@ public class MiniSignServiceImpl extends ServiceImpl<MiniSignRecordMapper, MiniS
if (totalPoint > 0) { if (totalPoint > 0) {
AdjustUserPointForm adjustForm = new AdjustUserPointForm(); AdjustUserPointForm adjustForm = new AdjustUserPointForm();
adjustForm.setUserId(userId); adjustForm.setUserId(userId);
adjustForm.setBizType("SIGN_IN"); adjustForm.setBizType("SIGN_IN_BASE");
adjustForm.setChangeAmount(totalPoint); adjustForm.setChangeAmount(totalPoint);
pointManageService.adjustPoint(adjustForm); pointManageService.adjustPoint(adjustForm);
} }

53
src/main/java/com/youlai/boot/mini/service/impl/MiniUserServiceImpl.java

@ -26,6 +26,7 @@ import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.UUID; import java.util.UUID;
/** /**
@ -71,6 +72,36 @@ public class MiniUserServiceImpl implements MiniUserService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateCurrentUserInfo(Long userId, MiniUserUpdateForm form) { public void updateCurrentUserInfo(Long userId, MiniUserUpdateForm form) {
if (userId == null) {
throw new MsgException("用户不存在");
}
SysUser sysUser = sysUserService.getById(userId);
if (sysUser == null) {
throw new MsgException("用户不存在");
}
LambdaUpdateWrapper<SysUser> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SysUser::getId, userId);
if (StringUtils.hasText(form.getNickname())) {
updateWrapper.set(SysUser::getNickname, form.getNickname());
}
if (form.getGender() != null) {
updateWrapper.set(SysUser::getGender, form.getGender());
}
boolean success = sysUserService.update(updateWrapper);
if (!success) {
throw new MsgException("用户信息更新失败,请重试");
}
}
@Override
public void saveFile(Long userId, MultipartFile image) {
if (userId == null) {
throw new MsgException("用户不存在");
}
SysUser sysUser = sysUserService.getById(userId); SysUser sysUser = sysUserService.getById(userId);
if (sysUser == null) { if (sysUser == null) {
throw new MsgException("用户不存在"); throw new MsgException("用户不存在");
@ -79,16 +110,14 @@ public class MiniUserServiceImpl implements MiniUserService {
LambdaUpdateWrapper<SysUser> updateWrapper = new LambdaUpdateWrapper<>(); LambdaUpdateWrapper<SysUser> updateWrapper = new LambdaUpdateWrapper<>();
updateWrapper.eq(SysUser::getId, userId); updateWrapper.eq(SysUser::getId, userId);
// 处理头像上传
MultipartFile avatar = form.getAvatar();
long currentTimestamp = System.currentTimeMillis(); long currentTimestamp = System.currentTimeMillis();
if (avatar != null && !avatar.isEmpty()) { if (image != null && !image.isEmpty()) {
try { try {
String objectName = "user_avatar/image/" String objectName = "user_avatar/image/"
+ currentTimestamp + RandomNumberUtils.createRandomLowerLetterAndNumber(8) + currentTimestamp + RandomNumberUtils.createRandomLowerLetterAndNumber(8)
+ "." + "."
+ FilenameUtils.getExtension(avatar.getOriginalFilename()); + FilenameUtils.getExtension(image.getOriginalFilename());
String newAvatarUrl = aliyunFileService.uploadFile(objectName, avatar.getInputStream()); String newAvatarUrl = aliyunFileService.uploadFile(objectName, image.getInputStream());
//更新数据库头像字段 //更新数据库头像字段
updateWrapper.set(SysUser::getAvatar, newAvatarUrl); updateWrapper.set(SysUser::getAvatar, newAvatarUrl);
@ -108,19 +137,7 @@ public class MiniUserServiceImpl implements MiniUserService {
throw new MsgException("头像上传失败"); throw new MsgException("头像上传失败");
} }
} }
// 处理其他字段
if (StringUtils.hasText(form.getNickname())) {
updateWrapper.set(SysUser::getNickname, form.getNickname());
}
if (form.getGender() != null) {
updateWrapper.set(SysUser::getGender, form.getGender());
} }
// 执行更新
boolean success = sysUserService.update(updateWrapper);
if (!success) {
throw new MsgException("用户信息更新失败,请重试");
}
}
} }

Loading…
Cancel
Save