feat: 积分发放模块

This commit is contained in:
2026-07-27 11:03:19 +08:00
parent dd7c35eb50
commit 784350bb42
6 changed files with 504 additions and 0 deletions
@@ -0,0 +1,68 @@
package com.budwk.app.zhgh.pointsmall.pointsgrant.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.pointsmall.pointsgrant.models.PointsMallUserPointsGrantPlan;
import com.budwk.app.zhgh.pointsmall.pointsgrant.param.PointsMallUserPointsGrantPlanPageParam;
import com.budwk.app.zhgh.pointsmall.pointsgrant.service.PointsMallUserPointsGrantPlanService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/pointsgrant")
public class PointsMallUserPointsGrantPlanController {
@Inject
private PointsMallUserPointsGrantPlanService grantPlanService;
@At("")
@Ok("beetl:/platform/zhgh/points-mall/pointsgrant/index.html")
@SaCheckPermission("points.mall.pointsgrant")
public void index() {
}
@At
@SaCheckPermission("points.mall.pointsgrant")
public Result pageData(PointsMallUserPointsGrantPlanPageParam param) {
return Result.success(grantPlanService.page(param));
}
@At
@SLog(tag = "积分发放", msg = "创建发放计划")
@SaCheckPermission("points.mall.pointsgrant")
public Result doAdd(PointsMallUserPointsGrantPlan plan) {
try {
grantPlanService.createPlan(plan);
return Result.success();
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "积分发放", msg = "执行发放计划")
@SaCheckPermission("points.mall.pointsgrant")
public Result startPlan(@Param("id") String id) {
try {
return Result.success(grantPlanService.startPlan(id));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
@At
@SLog(tag = "积分发放", msg = "取消发放计划")
@SaCheckPermission("points.mall.pointsgrant")
public Result cancelPlan(@Param("id") String id) {
try {
return Result.success(grantPlanService.cancelPlan(id));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
}
@@ -0,0 +1,71 @@
package com.budwk.app.zhgh.pointsmall.pointsgrant.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.ColDefine;
import org.nutz.dao.entity.annotation.ColType;
import org.nutz.dao.entity.annotation.Column;
import org.nutz.dao.entity.annotation.Comment;
import org.nutz.dao.entity.annotation.Default;
import org.nutz.dao.entity.annotation.Name;
import org.nutz.dao.entity.annotation.Table;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_user_points_grant_plan")
@Comment("积分商城积分发放计划")
public class PointsMallUserPointsGrantPlan extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("计划名称")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String planName;
@Column
@Comment("计划描述")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String planDescription;
@Column
@Comment("发放模式:1手动发放,2定时发放")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String grantMode;
@Column
@Comment("发放时间")
@ColDefine(type = ColType.VARCHAR, width = 50)
private String grantTime;
@Column
@Comment("Cron表达式")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String cronExp;
@Column
@Comment("计划状态")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String status;
@Column
@Comment("发放用户ID列表")
@ColDefine(type = ColType.VARCHAR, customType = "mediumtext")
private String userIdList;
@Column
@Comment("发放积分")
@Default("0")
@ColDefine(customType = "decimal(18,2)")
private BigDecimal grantPoints;
}
@@ -0,0 +1,13 @@
package com.budwk.app.zhgh.pointsmall.pointsgrant.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallUserPointsGrantPlanPageParam extends PageForm {
private String planName;
private String status;
}
@@ -0,0 +1,25 @@
package com.budwk.app.zhgh.pointsmall.pointsgrant.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.pointsgrant.models.PointsMallUserPointsGrantPlan;
import com.budwk.app.zhgh.pointsmall.pointsgrant.param.PointsMallUserPointsGrantPlanPageParam;
public interface PointsMallUserPointsGrantPlanService extends BaseService<PointsMallUserPointsGrantPlan> {
String STATUS_PENDING = "待执行";
String STATUS_IN_PROGRESS = "执行中";
String STATUS_COMPLETED = "已完成";
String STATUS_CANCELLED = "已取消";
String STATUS_FAILED = "已失败";
Pagination<PointsMallUserPointsGrantPlan> page(PointsMallUserPointsGrantPlanPageParam param);
void createPlan(PointsMallUserPointsGrantPlan plan);
boolean startPlan(String id);
boolean cancelPlan(String id);
int scheduledGrantPlan();
}
@@ -0,0 +1,128 @@
package com.budwk.app.zhgh.pointsmall.pointsgrant.service.impl;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.date.DateUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.pointsmall.points.param.PointsMallUserPointsBatchParam;
import com.budwk.app.zhgh.pointsmall.points.service.PointsMallUserPointsService;
import com.budwk.app.zhgh.pointsmall.pointsgrant.models.PointsMallUserPointsGrantPlan;
import com.budwk.app.zhgh.pointsmall.pointsgrant.param.PointsMallUserPointsGrantPlanPageParam;
import com.budwk.app.zhgh.pointsmall.pointsgrant.service.PointsMallUserPointsGrantPlanService;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.util.List;
@IocBean(args = {"refer:dao"})
public class PointsMallUserPointsGrantPlanServiceImpl extends BaseServiceImpl<PointsMallUserPointsGrantPlan> implements PointsMallUserPointsGrantPlanService {
@Inject
private PointsMallUserPointsService pointsMallUserPointsService;
public PointsMallUserPointsGrantPlanServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination<PointsMallUserPointsGrantPlan> page(PointsMallUserPointsGrantPlanPageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getPlanName())) {
cnd.and("planName", "like", "%" + param.getPlanName() + "%");
}
if (StrUtil.isNotBlank(param.getStatus())) {
cnd.and("status", "=", param.getStatus());
}
cnd.desc("createdAt");
return listPage(param.getPageNumber(), param.getPageSize(), PointsMallUserPointsGrantPlan.class, cnd);
}
@Override
public void createPlan(PointsMallUserPointsGrantPlan plan) {
checkPlan(plan);
plan.setStatus(STATUS_PENDING);
insert(plan);
}
@Override
public boolean startPlan(String id) {
if (StrUtil.isBlank(id)) {
throw new IllegalArgumentException("计划ID不能为空");
}
PointsMallUserPointsGrantPlan plan = fetch(id);
if (plan == null || Boolean.TRUE.equals(plan.getDelFlag())) {
throw new IllegalArgumentException("计划不存在");
}
if (!STATUS_PENDING.equals(plan.getStatus())) {
throw new IllegalArgumentException("计划未处于待执行状态,无法启动");
}
update(Chain.make("status", STATUS_IN_PROGRESS), Cnd.where("id", "=", id));
PointsMallUserPointsBatchParam batchParam = new PointsMallUserPointsBatchParam();
batchParam.setUserIds(plan.getUserIdList());
batchParam.setPoints(plan.getGrantPoints());
batchParam.setRemark("积分发放计划:" + plan.getPlanName());
try {
pointsMallUserPointsService.batchGrant(batchParam);
update(Chain.make("status", STATUS_COMPLETED), Cnd.where("id", "=", id));
} catch (RuntimeException e) {
update(Chain.make("status", STATUS_FAILED), Cnd.where("id", "=", id));
throw e;
}
return true;
}
@Override
public boolean cancelPlan(String id) {
if (StrUtil.isBlank(id)) {
throw new IllegalArgumentException("计划ID不能为空");
}
PointsMallUserPointsGrantPlan plan = fetch(id);
if (plan == null || Boolean.TRUE.equals(plan.getDelFlag())) {
throw new IllegalArgumentException("计划不存在");
}
if (!STATUS_PENDING.equals(plan.getStatus())) {
throw new IllegalArgumentException("计划未处于待执行状态,无法取消");
}
update(Chain.make("status", STATUS_CANCELLED), Cnd.where("id", "=", id));
return true;
}
@Override
public int scheduledGrantPlan() {
List<PointsMallUserPointsGrantPlan> plans = query(Cnd.where("delFlag", "=", false)
.and("status", "=", STATUS_PENDING)
.and("grantMode", "=", "2")
.and("grantTime", "<=", DateUtil.now()));
plans.forEach(plan -> startPlan(plan.getId()));
return plans.size();
}
private void checkPlan(PointsMallUserPointsGrantPlan plan) {
if (plan == null) {
throw new IllegalArgumentException("计划信息不能为空");
}
if (StrUtil.isBlank(plan.getPlanName())) {
throw new IllegalArgumentException("计划名称不能为空");
}
if (StrUtil.isBlank(plan.getGrantMode())) {
throw new IllegalArgumentException("发放模式不能为空");
}
if (!"1".equals(plan.getGrantMode()) && !"2".equals(plan.getGrantMode())) {
throw new IllegalArgumentException("发放模式不正确");
}
if ("2".equals(plan.getGrantMode()) && StrUtil.isBlank(plan.getGrantTime())) {
throw new IllegalArgumentException("定时发放时间不能为空");
}
if (StrUtil.isBlank(plan.getUserIdList())) {
throw new IllegalArgumentException("发放用户不能为空");
}
BigDecimal grantPoints = plan.getGrantPoints();
if (grantPoints == null || grantPoints.compareTo(BigDecimal.ZERO) <= 0) {
throw new IllegalArgumentException("发放积分必须大于0");
}
}
}