feat(recommendation): 实现推荐客户管理全流程功能
- 新增推荐记录、推荐人信息、推荐费结算实体及数据库表结构 - 实现推荐记录小程序端接口,包括新增报备、获取推荐码、查询我的推荐及统计 - 实现后台管理端推荐记录分页查询、确认有效、作废、结算及批量结算接口 - 实现推荐人信息查询及推荐统计汇总逻辑 - 实现推荐记录导出功能,支持导出推荐详情数据 - 完成推荐状态变更时推荐人统计数据同步更新 - 添加相关Mapper接口和Service接口及实现 - 使用MyBatis Plus分页插件支持分页查询 - 集成Spring Security权限校验和操作日志注解 - 优化推荐码生成和唯一性校验机制
This commit is contained in:
17
.workbuddy/expert-history.json
Normal file
17
.workbuddy/expert-history.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"sessions": {
|
||||||
|
"8b8cdf8cb3404efeac18615f9f419cb1": [
|
||||||
|
{
|
||||||
|
"expertId": "SeniorDeveloper",
|
||||||
|
"name": "吴八哥",
|
||||||
|
"profession": "高级开发工程师",
|
||||||
|
"avatarUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/avatars/02-Engineering/SeniorDeveloper/SeniorDeveloper.png",
|
||||||
|
"promptUrl": "https://acc-1258344699.cos.accelerate.myqcloud.com/workbuddy/experts/experts/02-Engineering/SeniorDeveloper/SeniorDeveloper_zh.md",
|
||||||
|
"usedAt": 1776323986694,
|
||||||
|
"industryId": "02-Engineering"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lastUpdated": 1776324781452
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.app.recommendation.param.LeadReferralParam;
|
||||||
|
import com.gxwebsoft.app.recommendation.service.LeadReferralService;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import com.gxwebsoft.common.core.annotation.OperationLog;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录管理控制器(后台管理端)
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/app/lead/referral/admin")
|
||||||
|
@Tag(name = "推荐客户管理", description = "后台管理端推荐记录相关接口")
|
||||||
|
public class LeadReferralAdminController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LeadReferralService leadReferralService;
|
||||||
|
|
||||||
|
@Operation(summary = "分页查询推荐记录")
|
||||||
|
@GetMapping("/page")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:page') or hasAuthority('app:referral:view')")
|
||||||
|
public ApiResult<Map<String, Object>> getReferralPage(LeadReferralParam param) {
|
||||||
|
return success(leadReferralService.getReferralPage(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取推荐统计汇总")
|
||||||
|
@GetMapping("/statistics")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:statistics')")
|
||||||
|
public ApiResult<Map<String, Object>> getStatistics(LeadReferralParam param) {
|
||||||
|
return success(leadReferralService.getStatistics(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "确认推荐有效")
|
||||||
|
@PutMapping("/confirm/{id}")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:confirm')")
|
||||||
|
@OperationLog
|
||||||
|
public ApiResult<?> confirmReferral(
|
||||||
|
@Parameter(description = "推荐ID") @PathVariable Long id) {
|
||||||
|
if (leadReferralService.confirmReferral(id)) {
|
||||||
|
return success("确认成功");
|
||||||
|
}
|
||||||
|
return fail("确认失败,状态已变更");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "作废推荐")
|
||||||
|
@PutMapping("/invalid/{id}")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:invalid')")
|
||||||
|
@OperationLog
|
||||||
|
public ApiResult<?> invalidateReferral(
|
||||||
|
@Parameter(description = "推荐ID") @PathVariable Long id,
|
||||||
|
@Parameter(description = "作废原因") @RequestParam(required = false) String reason) {
|
||||||
|
if (leadReferralService.invalidateReferral(id, reason)) {
|
||||||
|
return success("已作废");
|
||||||
|
}
|
||||||
|
return fail("操作失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "结算推荐费")
|
||||||
|
@PutMapping("/settle/{id}")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:settle')")
|
||||||
|
@OperationLog
|
||||||
|
public ApiResult<?> settleReferral(
|
||||||
|
@Parameter(description = "推荐ID") @PathVariable Long id,
|
||||||
|
@Parameter(description = "结算金额(为空则用默认推荐费)") @RequestParam(required = false) BigDecimal amount,
|
||||||
|
@Parameter(description = "备注") @RequestParam(required = false) String remarks) {
|
||||||
|
if (leadReferralService.settleReferral(id, amount, remarks)) {
|
||||||
|
return success("结算成功");
|
||||||
|
}
|
||||||
|
return fail("结算失败,请确认状态为「有效」");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "批量结算推荐费")
|
||||||
|
@PutMapping("/settle/batch")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:settle')")
|
||||||
|
@OperationLog
|
||||||
|
public ApiResult<?> batchSettleReferrals(
|
||||||
|
@Parameter(description = "推荐ID列表") @RequestBody List<Long> ids) {
|
||||||
|
int count = leadReferralService.batchSettleReferrals(ids);
|
||||||
|
return success("成功结算 " + count + " 条");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "导出推荐数据")
|
||||||
|
@GetMapping("/export")
|
||||||
|
@PreAuthorize("hasAuthority('app:referral:export')")
|
||||||
|
public ApiResult<List<Map<String, Object>>> exportReferrals(LeadReferralParam param) {
|
||||||
|
return success(leadReferralService.exportReferrals(param));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.controller;
|
||||||
|
|
||||||
|
import com.gxwebsoft.app.recommendation.param.LeadReferralParam;
|
||||||
|
import com.gxwebsoft.app.recommendation.service.LeadReferralService;
|
||||||
|
import com.gxwebsoft.common.core.web.ApiResult;
|
||||||
|
import com.gxwebsoft.common.core.web.BaseController;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录控制器(小程序端)
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/app/lead/referral")
|
||||||
|
@Tag(name = "推荐客户", description = "小程序端推荐报备相关接口")
|
||||||
|
public class LeadReferralController extends BaseController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LeadReferralService leadReferralService;
|
||||||
|
|
||||||
|
@Operation(summary = "报备客户")
|
||||||
|
@PostMapping("/add")
|
||||||
|
public ApiResult<?> addReferral(@RequestBody LeadReferralParam param) {
|
||||||
|
return success(leadReferralService.addReferral(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的推荐码(无则自动生成)")
|
||||||
|
@GetMapping("/my/code")
|
||||||
|
public ApiResult<?> getMyReferralCode() {
|
||||||
|
String code = leadReferralService.getMyReferralCode();
|
||||||
|
return success(code != null ? code : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的推荐记录(分页)")
|
||||||
|
@GetMapping("/my")
|
||||||
|
public ApiResult<Map<String, Object>> getMyReferrals(LeadReferralParam param) {
|
||||||
|
return success(leadReferralService.getMyReferrals(param));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "获取我的推荐统计")
|
||||||
|
@GetMapping("/my/stats")
|
||||||
|
public ApiResult<Map<String, Object>> getMyStats() {
|
||||||
|
return success(leadReferralService.getMyStats());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "根据推荐码获取推荐人信息")
|
||||||
|
@GetMapping("/referrer/{code}")
|
||||||
|
public ApiResult<Map<String, Object>> getReferrerByCode(
|
||||||
|
@Parameter(description = "推荐码") @PathVariable String code) {
|
||||||
|
return success(leadReferralService.getReferrerByCode(code));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录表
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("app_lead_referral")
|
||||||
|
@Schema(name = "LeadReferral", description = "推荐记录")
|
||||||
|
public class LeadReferral implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "主键ID")
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "推荐码(推荐人分享给客户的码)")
|
||||||
|
private String referralCode;
|
||||||
|
|
||||||
|
@Schema(description = "推荐人用户ID(NULL=匿名推荐)")
|
||||||
|
private Integer referrerId;
|
||||||
|
|
||||||
|
@Schema(description = "推荐人姓名")
|
||||||
|
private String referrerName;
|
||||||
|
|
||||||
|
@Schema(description = "推荐人手机号")
|
||||||
|
private String referrerPhone;
|
||||||
|
|
||||||
|
@Schema(description = "客户姓名")
|
||||||
|
private String customerName;
|
||||||
|
|
||||||
|
@Schema(description = "客户手机号")
|
||||||
|
private String customerPhone;
|
||||||
|
|
||||||
|
@Schema(description = "客户公司名称")
|
||||||
|
private String customerCompany;
|
||||||
|
|
||||||
|
@Schema(description = "客户需求描述")
|
||||||
|
private String requirement;
|
||||||
|
|
||||||
|
@Schema(description = "预约联系时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
|
||||||
|
private LocalDateTime appointmentTime;
|
||||||
|
|
||||||
|
@Schema(description = "推荐人备注")
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Schema(description = "推荐费(元)")
|
||||||
|
private BigDecimal referralFee;
|
||||||
|
|
||||||
|
@Schema(description = "推荐状态: 0=待确认, 1=有效, 2=无效, 3=已结算")
|
||||||
|
private Integer referralStatus;
|
||||||
|
|
||||||
|
@Schema(description = "作废原因")
|
||||||
|
private String invalidReason;
|
||||||
|
|
||||||
|
@Schema(description = "作废时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime invalidTime;
|
||||||
|
|
||||||
|
@Schema(description = "确认有效时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime confirmedTime;
|
||||||
|
|
||||||
|
@Schema(description = "结算时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime settledTime;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "创建人ID")
|
||||||
|
private Integer createBy;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "更新人ID")
|
||||||
|
private Integer updateBy;
|
||||||
|
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "删除标记: 0=未删, 1=已删")
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐费结算记录表
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("app_referral_settlement")
|
||||||
|
@Schema(name = "ReferralSettlement", description = "推荐费结算记录")
|
||||||
|
public class ReferralSettlement implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "主键ID")
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "关联推荐记录ID")
|
||||||
|
private Long referralId;
|
||||||
|
|
||||||
|
@Schema(description = "推荐人用户ID")
|
||||||
|
private Integer referrerId;
|
||||||
|
|
||||||
|
@Schema(description = "结算金额(元)")
|
||||||
|
private BigDecimal amount;
|
||||||
|
|
||||||
|
@Schema(description = "结算单号")
|
||||||
|
private String settlementNo;
|
||||||
|
|
||||||
|
@Schema(description = "结算备注")
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "结算操作人ID")
|
||||||
|
private Integer createBy;
|
||||||
|
|
||||||
|
@Schema(description = "结算时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "删除标记: 0=未删, 1=已删")
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.entity;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableLogic;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐人信息表
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("app_referrer_info")
|
||||||
|
@Schema(name = "ReferrerInfo", description = "推荐人信息")
|
||||||
|
public class ReferrerInfo implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@Schema(description = "主键ID")
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@Schema(description = "用户ID")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@Schema(description = "推荐码(唯一)")
|
||||||
|
private String referralCode;
|
||||||
|
|
||||||
|
@Schema(description = "累计推荐人数")
|
||||||
|
private Integer totalReferrals;
|
||||||
|
|
||||||
|
@Schema(description = "有效推荐数")
|
||||||
|
private Integer validReferrals;
|
||||||
|
|
||||||
|
@Schema(description = "已结算数")
|
||||||
|
private Integer settledCount;
|
||||||
|
|
||||||
|
@Schema(description = "累计获得推荐费(元)")
|
||||||
|
private BigDecimal totalEarned;
|
||||||
|
|
||||||
|
@Schema(description = "待结算金额(元)")
|
||||||
|
private BigDecimal pendingAmount;
|
||||||
|
|
||||||
|
@Schema(description = "租户ID")
|
||||||
|
private Integer tenantId;
|
||||||
|
|
||||||
|
@Schema(description = "创建人ID")
|
||||||
|
private Integer createBy;
|
||||||
|
|
||||||
|
@Schema(description = "创建时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime createTime;
|
||||||
|
|
||||||
|
@Schema(description = "更新人ID")
|
||||||
|
private Integer updateBy;
|
||||||
|
|
||||||
|
@Schema(description = "更新时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private LocalDateTime updateTime;
|
||||||
|
|
||||||
|
@Schema(description = "删除标记: 0=未删, 1=已删")
|
||||||
|
@TableLogic
|
||||||
|
private Integer deleted;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.LeadReferral;
|
||||||
|
import com.gxwebsoft.app.recommendation.param.LeadReferralParam;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录 Mapper
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
public interface LeadReferralMapper extends BaseMapper<LeadReferral> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询推荐记录(关联推荐人信息)
|
||||||
|
*/
|
||||||
|
IPage<LeadReferral> selectReferralPage(Page<?> page, @Param("param") LeadReferralParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询用户的所有推荐记录
|
||||||
|
*/
|
||||||
|
IPage<LeadReferral> selectByReferrerId(Page<?> page, @Param("referrerId") Integer referrerId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.ReferralSettlement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐费结算记录 Mapper
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
public interface ReferralSettlementMapper extends BaseMapper<ReferralSettlement> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.mapper;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.ReferrerInfo;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
import org.apache.ibatis.annotations.Select;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐人信息 Mapper
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
public interface ReferrerInfoMapper extends BaseMapper<ReferrerInfo> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据用户ID查询推荐人信息
|
||||||
|
*/
|
||||||
|
@Select("SELECT * FROM app_referrer_info WHERE user_id = #{userId} AND deleted = 0 LIMIT 1")
|
||||||
|
ReferrerInfo selectByUserId(@Param("userId") Integer userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据推荐码查询推荐人信息
|
||||||
|
*/
|
||||||
|
@Select("SELECT * FROM app_referrer_info WHERE referral_code = #{referralCode} AND deleted = 0 LIMIT 1")
|
||||||
|
ReferrerInfo selectByReferralCode(@Param("referralCode") String referralCode);
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.param;
|
||||||
|
|
||||||
|
import com.gxwebsoft.common.core.web.PageParam;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录查询参数
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Schema(name = "LeadReferralParam", description = "推荐记录查询参数")
|
||||||
|
public class LeadReferralParam extends PageParam {
|
||||||
|
|
||||||
|
@Schema(description = "推荐状态: 0=待确认, 1=有效, 2=无效, 3=已结算")
|
||||||
|
private Integer referralStatus;
|
||||||
|
|
||||||
|
@Schema(description = "推荐人用户ID")
|
||||||
|
private Integer referrerId;
|
||||||
|
|
||||||
|
@Schema(description = "客户姓名(模糊)")
|
||||||
|
private String customerName;
|
||||||
|
|
||||||
|
@Schema(description = "客户手机号")
|
||||||
|
private String customerPhone;
|
||||||
|
|
||||||
|
@Schema(description = "客户公司")
|
||||||
|
private String customerCompany;
|
||||||
|
|
||||||
|
@Schema(description = "推荐需求")
|
||||||
|
private String requirement;
|
||||||
|
|
||||||
|
@Schema(description = "预约时间")
|
||||||
|
private LocalDateTime appointmentTime;
|
||||||
|
|
||||||
|
@Schema(description = "备注")
|
||||||
|
private String remarks;
|
||||||
|
|
||||||
|
@Schema(description = "推荐码")
|
||||||
|
private String referralCode;
|
||||||
|
|
||||||
|
@Schema(description = "开始日期")
|
||||||
|
private String startDate;
|
||||||
|
|
||||||
|
@Schema(description = "结束日期")
|
||||||
|
private String endDate;
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.LeadReferral;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.ReferralSettlement;
|
||||||
|
import com.gxwebsoft.app.recommendation.param.LeadReferralParam;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录 Service
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
public interface LeadReferralService extends IService<LeadReferral> {
|
||||||
|
|
||||||
|
// ========== 小程序端 API ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户报备客户
|
||||||
|
*/
|
||||||
|
LeadReferral addReferral(LeadReferralParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的推荐码(无则生成)
|
||||||
|
*/
|
||||||
|
String getMyReferralCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的推荐记录(分页)
|
||||||
|
*/
|
||||||
|
Map<String, Object> getMyReferrals(LeadReferralParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我的推荐统计
|
||||||
|
*/
|
||||||
|
Map<String, Object> getMyStats();
|
||||||
|
|
||||||
|
// ========== 后台管理 API ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询推荐记录
|
||||||
|
*/
|
||||||
|
Map<String, Object> getReferralPage(LeadReferralParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确认推荐有效
|
||||||
|
*/
|
||||||
|
boolean confirmReferral(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 作废推荐
|
||||||
|
*/
|
||||||
|
boolean invalidateReferral(Long id, String reason);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结算推荐费
|
||||||
|
*/
|
||||||
|
boolean settleReferral(Long id, BigDecimal amount, String remarks);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量结算推荐费
|
||||||
|
*/
|
||||||
|
int batchSettleReferrals(List<Long> ids);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据推荐码获取推荐人信息
|
||||||
|
*/
|
||||||
|
Map<String, Object> getReferrerByCode(String code);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取推荐统计汇总
|
||||||
|
*/
|
||||||
|
Map<String, Object> getStatistics(LeadReferralParam param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出推荐数据
|
||||||
|
*/
|
||||||
|
List<Map<String, Object>> exportReferrals(LeadReferralParam param);
|
||||||
|
}
|
||||||
@@ -0,0 +1,423 @@
|
|||||||
|
package com.gxwebsoft.app.recommendation.service.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.IdUtil;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import com.gxwebsoft.app.recommendation.mapper.ReferrerInfoMapper;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.LeadReferral;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.ReferralSettlement;
|
||||||
|
import com.gxwebsoft.app.recommendation.entity.ReferrerInfo;
|
||||||
|
import com.gxwebsoft.app.recommendation.mapper.LeadReferralMapper;
|
||||||
|
import com.gxwebsoft.app.recommendation.mapper.ReferralSettlementMapper;
|
||||||
|
import com.gxwebsoft.app.recommendation.param.LeadReferralParam;
|
||||||
|
import com.gxwebsoft.app.recommendation.service.LeadReferralService;
|
||||||
|
import com.gxwebsoft.common.system.entity.User;
|
||||||
|
import com.gxwebsoft.common.system.service.UserService;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 推荐记录 Service 实现
|
||||||
|
*
|
||||||
|
* @author 科技小王子
|
||||||
|
* @since 2026-04-16
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class LeadReferralServiceImpl extends ServiceImpl<LeadReferralMapper, LeadReferral>
|
||||||
|
implements LeadReferralService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LeadReferralMapper leadReferralMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReferrerInfoMapper referrerInfoMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ReferralSettlementMapper settlementMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UserService userService;
|
||||||
|
|
||||||
|
/** 默认推荐费(元),可配置化 */
|
||||||
|
private static final BigDecimal DEFAULT_REFERRAL_FEE = new BigDecimal("100.00");
|
||||||
|
|
||||||
|
// ========== 私有方法 ==========
|
||||||
|
|
||||||
|
private User getLoginUser() {
|
||||||
|
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
|
||||||
|
if (principal instanceof User) {
|
||||||
|
return (User) principal;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Integer getTenantId() {
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
if (loginUser != null) {
|
||||||
|
return loginUser.getTenantId();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String generateReferralCode() {
|
||||||
|
return "RC" + IdUtil.fastSimpleUUID().substring(0, 8).toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getOrCreateReferralCode(Integer userId, String userName, String phone, Integer tenantId) {
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByUserId(userId);
|
||||||
|
if (info != null && StrUtil.isNotBlank(info.getReferralCode())) {
|
||||||
|
return info.getReferralCode();
|
||||||
|
}
|
||||||
|
// 创建新记录
|
||||||
|
String code;
|
||||||
|
do {
|
||||||
|
code = generateReferralCode();
|
||||||
|
} while (referrerInfoMapper.selectByReferralCode(code) != null);
|
||||||
|
|
||||||
|
info = new ReferrerInfo();
|
||||||
|
info.setUserId(userId);
|
||||||
|
info.setReferralCode(code);
|
||||||
|
info.setTotalReferrals(0);
|
||||||
|
info.setValidReferrals(0);
|
||||||
|
info.setSettledCount(0);
|
||||||
|
info.setTotalEarned(BigDecimal.ZERO);
|
||||||
|
info.setPendingAmount(BigDecimal.ZERO);
|
||||||
|
info.setTenantId(tenantId);
|
||||||
|
referrerInfoMapper.insert(info);
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> buildPageResult(IPage<LeadReferral> page) {
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("list", page.getRecords());
|
||||||
|
result.put("total", page.getTotal());
|
||||||
|
result.put("pageNum", page.getCurrent());
|
||||||
|
result.put("pageSize", page.getSize());
|
||||||
|
result.put("pages", page.getPages());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 小程序端 API ==========
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public LeadReferral addReferral(LeadReferralParam param) {
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
Integer tenantId = getTenantId();
|
||||||
|
|
||||||
|
LeadReferral referral = new LeadReferral();
|
||||||
|
referral.setCustomerName(param.getCustomerName());
|
||||||
|
referral.setCustomerPhone(param.getCustomerPhone());
|
||||||
|
referral.setCustomerCompany(param.getCustomerCompany());
|
||||||
|
referral.setRequirement(param.getRequirement());
|
||||||
|
referral.setAppointmentTime(param.getAppointmentTime());
|
||||||
|
referral.setRemarks(param.getRemarks());
|
||||||
|
referral.setReferralFee(DEFAULT_REFERRAL_FEE);
|
||||||
|
referral.setReferralStatus(0); // 待确认
|
||||||
|
referral.setTenantId(tenantId);
|
||||||
|
referral.setCreateBy(loginUser != null ? loginUser.getUserId() : null);
|
||||||
|
referral.setCreateTime(LocalDateTime.now());
|
||||||
|
|
||||||
|
if (loginUser != null) {
|
||||||
|
referral.setReferrerId(loginUser.getUserId());
|
||||||
|
referral.setReferrerName(loginUser.getUsername());
|
||||||
|
referral.setReferrerPhone(loginUser.getPhone());
|
||||||
|
// 生成推荐码
|
||||||
|
String code = getOrCreateReferralCode(
|
||||||
|
loginUser.getUserId(),
|
||||||
|
loginUser.getUsername(),
|
||||||
|
loginUser.getPhone(),
|
||||||
|
tenantId
|
||||||
|
);
|
||||||
|
referral.setReferralCode(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
baseMapper.insert(referral);
|
||||||
|
|
||||||
|
// 更新推荐人统计
|
||||||
|
if (referral.getReferrerId() != null) {
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByUserId(referral.getReferrerId());
|
||||||
|
if (info != null) {
|
||||||
|
info.setTotalReferrals(info.getTotalReferrals() + 1);
|
||||||
|
referrerInfoMapper.updateById(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return referral;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMyReferralCode() {
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Integer tenantId = getTenantId();
|
||||||
|
return getOrCreateReferralCode(
|
||||||
|
loginUser.getUserId(),
|
||||||
|
loginUser.getUsername(),
|
||||||
|
loginUser.getPhone(),
|
||||||
|
tenantId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getMyReferrals(LeadReferralParam param) {
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
if (loginUser == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
Page<LeadReferral> page = new Page<>(
|
||||||
|
param.getCurrent() != null ? param.getCurrent() : 1,
|
||||||
|
param.getSize() != null ? param.getSize() : 10
|
||||||
|
);
|
||||||
|
I
|
||||||
|
return buildPageResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getMyStats() {
|
||||||
|
User loginUser = getLoginUser();
|
||||||
|
Map<String, Object> stats = new HashMap<>();
|
||||||
|
|
||||||
|
if (loginUser == null) {
|
||||||
|
stats.put("totalCount", 0);
|
||||||
|
stats.put("pendingCount", 0);
|
||||||
|
stats.put("validCount", 0);
|
||||||
|
stats.put("settledCount", 0);
|
||||||
|
stats.put("pendingAmount", "0.00");
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByUserId(loginUser.getUserId());
|
||||||
|
if (info == null) {
|
||||||
|
stats.put("totalCount", 0);
|
||||||
|
stats.put("pendingCount", 0);
|
||||||
|
stats.put("validCount", 0);
|
||||||
|
stats.put("settledCount", 0);
|
||||||
|
stats.put("pendingAmount", "0.00");
|
||||||
|
} else {
|
||||||
|
stats.put("totalCount", info.getTotalReferrals());
|
||||||
|
stats.put("validCount", info.getValidReferrals());
|
||||||
|
stats.put("settledCount", info.getSettledCount());
|
||||||
|
stats.put("pendingAmount", info.getPendingAmount().toString());
|
||||||
|
// 待确认 = 总数 - 有效 - 已结算 - 无效
|
||||||
|
int invalidCount = info.getTotalReferrals() - info.getValidReferrals()
|
||||||
|
- info.getSettledCount();
|
||||||
|
LambdaQueryWrapper<LeadReferral> qw = new LambdaQueryWrapper<>();
|
||||||
|
qw.eq(LeadReferral::getReferrerId, loginUser.getUserId())
|
||||||
|
.eq(LeadReferral::getReferralStatus, 0);
|
||||||
|
stats.put("pendingCount", baseMapper.selectCount(qw));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 加上推荐码
|
||||||
|
stats.put("referralCode", getMyReferralCode());
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 后台管理 API ==========
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getReferralPage(LeadReferralParam param) {
|
||||||
|
Page<LeadReferral> page = new Page<>(
|
||||||
|
param.getPageNum() != null ? param.getPageNum() : 1,
|
||||||
|
param.getPageSize() != null ? param.getPageSize() : 10
|
||||||
|
);
|
||||||
|
IPage<LeadReferral> result = leadReferralMapper.selectReferralPage(page, param);
|
||||||
|
return buildPageResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean confirmReferral(Long id) {
|
||||||
|
LeadReferral referral = baseMapper.selectById(id);
|
||||||
|
if (referral == null || referral.getReferralStatus() != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
referral.setReferralStatus(1); // 有效
|
||||||
|
referral.setConfirmedTime(LocalDateTime.now());
|
||||||
|
referral.setUpdateTime(LocalDateTime.now());
|
||||||
|
baseMapper.updateById(referral);
|
||||||
|
|
||||||
|
// 更新推荐人统计
|
||||||
|
if (referral.getReferrerId() != null) {
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByUserId(referral.getReferrerId());
|
||||||
|
if (info != null) {
|
||||||
|
info.setValidReferrals(info.getValidReferrals() + 1);
|
||||||
|
info.setPendingAmount(info.getPendingAmount().add(referral.getReferralFee()));
|
||||||
|
referrerInfoMapper.updateById(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean invalidateReferral(Long id, String reason) {
|
||||||
|
LeadReferral referral = baseMapper.selectById(id);
|
||||||
|
if (referral == null || referral.getReferralStatus() == 2
|
||||||
|
|| referral.getReferralStatus() == 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
referral.setReferralStatus(2); // 无效
|
||||||
|
referral.setInvalidReason(reason);
|
||||||
|
referral.setInvalidTime(LocalDateTime.now());
|
||||||
|
referral.setUpdateTime(LocalDateTime.now());
|
||||||
|
baseMapper.updateById(referral);
|
||||||
|
|
||||||
|
// 若之前是有效状态,需回退推荐人统计
|
||||||
|
if (referral.getReferralStatus() == 1 && referral.getReferrerId() != null) {
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByUserId(referral.getReferrerId());
|
||||||
|
if (info != null) {
|
||||||
|
info.setValidReferrals(Math.max(0, info.getValidReferrals() - 1));
|
||||||
|
info.setPendingAmount(info.getPendingAmount().subtract(referral.getReferralFee()));
|
||||||
|
referrerInfoMapper.updateById(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public boolean settleReferral(Long id, BigDecimal amount, String remarks) {
|
||||||
|
LeadReferral referral = baseMapper.selectById(id);
|
||||||
|
if (referral == null || referral.getReferralStatus() != 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal settleAmount = amount != null ? amount : referral.getReferralFee();
|
||||||
|
|
||||||
|
referral.setReferralStatus(3); // 已结算
|
||||||
|
referral.setSettledTime(LocalDateTime.now());
|
||||||
|
referral.setUpdateTime(LocalDateTime.now());
|
||||||
|
baseMapper.updateById(referral);
|
||||||
|
|
||||||
|
// 写入结算记录
|
||||||
|
ReferralSettlement settlement = new ReferralSettlement();
|
||||||
|
settlement.setReferralId(id);
|
||||||
|
settlement.setReferrerId(referral.getReferrerId());
|
||||||
|
settlement.setAmount(settleAmount);
|
||||||
|
settlement.setSettlementNo("ST" + System.currentTimeMillis());
|
||||||
|
settlement.setRemarks(remarks);
|
||||||
|
settlement.setTenantId(referral.getTenantId());
|
||||||
|
settlement.setCreateBy(getLoginUser() != null ? getLoginUser().getUserId() : null);
|
||||||
|
settlement.setCreateTime(LocalDateTime.now());
|
||||||
|
settlementMapper.insert(settlement);
|
||||||
|
|
||||||
|
// 更新推荐人统计
|
||||||
|
if (referral.getReferrerId() != null) {
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByUserId(referral.getReferrerId());
|
||||||
|
if (info != null) {
|
||||||
|
info.setSettledCount(info.getSettledCount() + 1);
|
||||||
|
info.setPendingAmount(info.getPendingAmount().subtract(settleAmount));
|
||||||
|
info.setTotalEarned(info.getTotalEarned().add(settleAmount));
|
||||||
|
referrerInfoMapper.updateById(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(rollbackFor = Exception.class)
|
||||||
|
public int batchSettleReferrals(List<Long> ids) {
|
||||||
|
int count = 0;
|
||||||
|
for (Long id : ids) {
|
||||||
|
if (settleReferral(id, null, null)) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getReferrerByCode(String code) {
|
||||||
|
ReferrerInfo info = referrerInfoMapper.selectByReferralCode(code);
|
||||||
|
if (info == null) {
|
||||||
|
return Collections.emptyMap();
|
||||||
|
}
|
||||||
|
Map<String, Object> result = new HashMap<>();
|
||||||
|
result.put("referrerId", info.getUserId());
|
||||||
|
result.put("referrerName", "");
|
||||||
|
result.put("referralCode", info.getReferralCode());
|
||||||
|
if (info.getUserId() != null) {
|
||||||
|
User user = userService.getByIdRel(info.getUserId());
|
||||||
|
if (user != null) {
|
||||||
|
result.put("referrerName", user.getUsername());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getStatistics(LeadReferralParam param) {
|
||||||
|
Map<String, Object> stats = new HashMap<>();
|
||||||
|
|
||||||
|
LambdaQueryWrapper<LeadReferral> qw = new LambdaQueryWrapper<>();
|
||||||
|
if (param.getStartDate() != null) {
|
||||||
|
qw.ge(LeadReferral::getCreateTime,
|
||||||
|
LocalDateTime.parse(param.getStartDate() + " 00:00:00",
|
||||||
|
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
}
|
||||||
|
if (param.getEndDate() != null) {
|
||||||
|
qw.le(LeadReferral::getCreateTime,
|
||||||
|
LocalDateTime.parse(param.getEndDate() + " 23:59:59",
|
||||||
|
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
|
||||||
|
}
|
||||||
|
|
||||||
|
stats.put("totalCount", baseMapper.selectCount(qw.clone()));
|
||||||
|
|
||||||
|
qw.eq(LeadReferral::getReferralStatus, 0);
|
||||||
|
stats.put("pendingCount", baseMapper.selectCount(qw.clone()));
|
||||||
|
|
||||||
|
qw.eq(LeadReferral::getReferralStatus, 1);
|
||||||
|
stats.put("validCount", baseMapper.selectCount(qw.clone()));
|
||||||
|
|
||||||
|
qw.eq(LeadReferral::getReferralStatus, 3);
|
||||||
|
stats.put("settledCount", baseMapper.selectCount(qw));
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Map<String, Object>> exportReferrals(LeadReferralParam param) {
|
||||||
|
Page<LeadReferral> page = new Page<>(1, 10000);
|
||||||
|
IPage<LeadReferral> result = leadReferralMapper.selectReferralPage(page, param);
|
||||||
|
return result.getRecords().stream().map(r -> {
|
||||||
|
Map<String, Object> map = new LinkedHashMap<>();
|
||||||
|
map.put("推荐码", r.getReferralCode());
|
||||||
|
map.put("推荐人", r.getReferrerName());
|
||||||
|
map.put("推荐人手机", r.getReferrerPhone());
|
||||||
|
map.put("客户姓名", r.getCustomerName());
|
||||||
|
map.put("客户电话", r.getCustomerPhone());
|
||||||
|
map.put("客户公司", r.getCustomerCompany());
|
||||||
|
map.put("推荐费", r.getReferralFee());
|
||||||
|
map.put("状态", getStatusText(r.getReferralStatus()));
|
||||||
|
map.put("推荐时间", r.getCreateTime());
|
||||||
|
return map;
|
||||||
|
}).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getStatusText(Integer status) {
|
||||||
|
if (status == null) return "";
|
||||||
|
switch (status) {
|
||||||
|
case 0: return "待确认";
|
||||||
|
case 1: return "有效";
|
||||||
|
case 2: return "无效";
|
||||||
|
case 3: return "已结算";
|
||||||
|
default: return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
src/main/resources/db/sql/app_lead_referral.sql
Normal file
81
src/main/resources/db/sql/app_lead_referral.sql
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 推荐客户模块 SQL
|
||||||
|
-- 包: app
|
||||||
|
-- 创建时间: 2026-04-16
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 1. 推荐记录表
|
||||||
|
CREATE TABLE `app_lead_referral` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`referral_code` VARCHAR(32) DEFAULT NULL COMMENT '推荐码(推荐人分享给客户的码)',
|
||||||
|
`referrer_id` INT DEFAULT NULL COMMENT '推荐人用户ID(NULL=匿名推荐)',
|
||||||
|
`referrer_name` VARCHAR(64) DEFAULT NULL COMMENT '推荐人姓名',
|
||||||
|
`referrer_phone` VARCHAR(20) DEFAULT NULL COMMENT '推荐人手机号',
|
||||||
|
|
||||||
|
`customer_name` VARCHAR(64) NOT NULL COMMENT '客户姓名',
|
||||||
|
`customer_phone` VARCHAR(20) NOT NULL COMMENT '客户手机号',
|
||||||
|
`customer_company` VARCHAR(255) DEFAULT NULL COMMENT '客户公司名称',
|
||||||
|
`requirement` TEXT DEFAULT NULL COMMENT '客户需求描述',
|
||||||
|
`appointment_time` DATETIME DEFAULT NULL COMMENT '预约联系时间',
|
||||||
|
`remarks` VARCHAR(500) DEFAULT NULL COMMENT '推荐人备注',
|
||||||
|
|
||||||
|
`referral_fee` DECIMAL(10,2) DEFAULT '0.00' COMMENT '推荐费(元)',
|
||||||
|
`referral_status` TINYINT NOT NULL DEFAULT '0' COMMENT '推荐状态: 0=待确认, 1=有效, 2=无效/作废, 3=已结算',
|
||||||
|
`invalid_reason` VARCHAR(255) DEFAULT NULL COMMENT '作废原因',
|
||||||
|
`invalid_time` DATETIME DEFAULT NULL COMMENT '作废时间',
|
||||||
|
`confirmed_time` DATETIME DEFAULT NULL COMMENT '确认有效时间',
|
||||||
|
`settled_time` DATETIME DEFAULT NULL COMMENT '结算时间',
|
||||||
|
|
||||||
|
`tenant_id` INT DEFAULT NULL COMMENT '租户ID',
|
||||||
|
`create_by` INT DEFAULT NULL COMMENT '创建人ID',
|
||||||
|
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_by` INT DEFAULT NULL COMMENT '更新人ID',
|
||||||
|
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`deleted` TINYINT NOT NULL DEFAULT '0' COMMENT '删除标记: 0=未删, 1=已删',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_referrer_id` (`referrer_id`),
|
||||||
|
KEY `idx_customer_phone` (`customer_phone`),
|
||||||
|
KEY `idx_referral_status` (`referral_status`),
|
||||||
|
KEY `idx_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐记录表';
|
||||||
|
|
||||||
|
-- 2. 推荐人信息表
|
||||||
|
CREATE TABLE `app_referrer_info` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`user_id` INT NOT NULL COMMENT '用户ID',
|
||||||
|
`referral_code` VARCHAR(32) NOT NULL COMMENT '推荐码(唯一)',
|
||||||
|
`total_referrals` INT NOT NULL DEFAULT '0' COMMENT '累计推荐人数',
|
||||||
|
`valid_referrals` INT NOT NULL DEFAULT '0' COMMENT '有效推荐数',
|
||||||
|
`settled_count` INT NOT NULL DEFAULT '0' COMMENT '已结算数',
|
||||||
|
`total_earned` DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '累计获得推荐费(元)',
|
||||||
|
`pending_amount` DECIMAL(10,2) NOT NULL DEFAULT '0.00' COMMENT '待结算金额(元)',
|
||||||
|
|
||||||
|
`tenant_id` INT DEFAULT NULL COMMENT '租户ID',
|
||||||
|
`create_by` INT DEFAULT NULL COMMENT '创建人ID',
|
||||||
|
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`update_by` INT DEFAULT NULL COMMENT '更新人ID',
|
||||||
|
`update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
|
||||||
|
`deleted` TINYINT NOT NULL DEFAULT '0' COMMENT '删除标记: 0=未删, 1=已删',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `uk_user_id` (`user_id`),
|
||||||
|
UNIQUE KEY `uk_referral_code` (`referral_code`),
|
||||||
|
KEY `idx_create_time` (`create_time`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐人信息表';
|
||||||
|
|
||||||
|
-- 3. 推荐费结算记录表
|
||||||
|
CREATE TABLE `app_referral_settlement` (
|
||||||
|
`id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID',
|
||||||
|
`referral_id` BIGINT NOT NULL COMMENT '关联推荐记录ID',
|
||||||
|
`referrer_id` INT NOT NULL COMMENT '推荐人用户ID',
|
||||||
|
`amount` DECIMAL(10,2) NOT NULL COMMENT '结算金额(元)',
|
||||||
|
`settlement_no` VARCHAR(32) DEFAULT NULL COMMENT '结算单号',
|
||||||
|
`remarks` VARCHAR(500) DEFAULT NULL COMMENT '结算备注',
|
||||||
|
|
||||||
|
`tenant_id` INT DEFAULT NULL COMMENT '租户ID',
|
||||||
|
`create_by` INT DEFAULT NULL COMMENT '结算操作人ID',
|
||||||
|
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '结算时间',
|
||||||
|
`deleted` TINYINT NOT NULL DEFAULT '0' COMMENT '删除标记: 0=未删, 1=已删',
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_referral_id` (`referral_id`),
|
||||||
|
KEY `idx_referrer_id` (`referrer_id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='推荐费结算记录表';
|
||||||
Reference in New Issue
Block a user