From 88afd149c325d9fb0299596aab4d41e0d0c29011 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Thu, 5 Feb 2026 18:51:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(glt):=20=E6=B7=BB=E5=8A=A0=E9=80=81?= =?UTF-8?q?=E6=B0=B4=E8=AE=A2=E5=8D=95=E6=A8=A1=E5=9D=97=E5=B9=B6=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=BB=8F=E9=94=80=E5=95=86=E7=BB=93=E7=AE=97=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增送水订单实体类 GltTicketOrder 及其相关控制器、服务、映射器 - 添加送水订单参数类 GltTicketOrderParam 和 XML 映射配置 - 实现送水订单的增删改查、分页查询等完整 CRUD 功能 - 在经销商结算任务中引入分销设置功能,支持按级别控制分佣 - 更新总经销商分润计算逻辑,使用动态费率替代固定值 - 删除不再使用的中文字体修复脚本文件 - 重构经销商推荐佣金结算逻辑,支持最多三级分佣 - 优化订单状态检查逻辑,在退款流程中排除已完成订单 --- scripts/fix-chinese-font.sh | 125 ------------- .../common/core/web}/ImportParams.java | 3 +- .../controller/GltTicketOrderController.java | 127 +++++++++++++ .../gxwebsoft/glt/entity/GltTicketOrder.java | 84 +++++++++ .../glt/mapper/GltTicketOrderMapper.java | 37 ++++ .../glt/mapper/xml/GltTicketOrderMapper.xml | 81 +++++++++ .../glt/param/GltTicketOrderParam.java | 82 +++++++++ .../glt/service/GltTicketOrderService.java | 42 +++++ .../impl/GltTicketOrderServiceImpl.java | 47 +++++ .../task/DealerOrderSettlement10584Task.java | 170 +++++++++++++----- .../shop/controller/ShopOrderController.java | 2 +- 11 files changed, 627 insertions(+), 173 deletions(-) delete mode 100644 scripts/fix-chinese-font.sh rename {cn/afterturn/easypoi/excel/entity => src/main/java/com/gxwebsoft/common/core/web}/ImportParams.java (97%) create mode 100644 src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java create mode 100644 src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java create mode 100644 src/main/java/com/gxwebsoft/glt/mapper/GltTicketOrderMapper.java create mode 100644 src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml create mode 100644 src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java create mode 100644 src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java create mode 100644 src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java diff --git a/scripts/fix-chinese-font.sh b/scripts/fix-chinese-font.sh deleted file mode 100644 index 1deac8b..0000000 --- a/scripts/fix-chinese-font.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash - -############################################################################### -# 捐款证书中文乱码修复脚本 -# 用途:在运行中的Docker容器内安装中文字体 -# 适用于:无法重新构建镜像的紧急情况 -############################################################################### - -set -e # 遇到错误立即退出 - -# 颜色输出 -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# 容器名称(可根据实际情况修改) -CONTAINER_NAME="websoft-api-container" - -echo -e "${GREEN}================================${NC}" -echo -e "${GREEN}中文字体修复脚本${NC}" -echo -e "${GREEN}================================${NC}" -echo "" - -# 检查容器是否存在 -echo -e "${YELLOW}步骤 1/6: 检查容器状态...${NC}" -if ! docker ps | grep -q "$CONTAINER_NAME"; then - echo -e "${RED}错误:容器 $CONTAINER_NAME 未运行!${NC}" - echo "当前运行的容器:" - docker ps --format "table {{.Names}}\t{{.Status}}" - echo "" - read -p "请输入正确的容器名称: " CONTAINER_NAME - if [ -z "$CONTAINER_NAME" ]; then - echo -e "${RED}容器名称不能为空,退出。${NC}" - exit 1 - fi -fi -echo -e "${GREEN}✓ 容器正在运行${NC}" -echo "" - -# 检查是否已安装字体 -echo -e "${YELLOW}步骤 2/6: 检查是否已安装中文字体...${NC}" -if docker exec "$CONTAINER_NAME" fc-list :lang=zh 2>/dev/null | grep -q "WenQuanYi"; then - echo -e "${GREEN}✓ 中文字体已安装${NC}" - docker exec "$CONTAINER_NAME" fc-list :lang=zh - echo "" - read -p "是否重新安装?(y/N): " REINSTALL - if [[ ! "$REINSTALL" =~ ^[Yy]$ ]]; then - echo "跳过安装,退出。" - exit 0 - fi -else - echo -e "${YELLOW}未检测到中文字体,开始安装...${NC}" -fi -echo "" - -# 安装字体工具 -echo -e "${YELLOW}步骤 3/6: 安装字体工具...${NC}" -docker exec -u root "$CONTAINER_NAME" sh -c "apk add --no-cache fontconfig ttf-dejavu wget" || { - echo -e "${RED}✗ 字体工具安装失败${NC}" - exit 1 -} -echo -e "${GREEN}✓ 字体工具安装成功${NC}" -echo "" - -# 下载中文字体 -echo -e "${YELLOW}步骤 4/6: 下载文泉驿微米黑字体...${NC}" -echo "正在从GitHub下载(约10MB),请稍候..." -docker exec -u root "$CONTAINER_NAME" sh -c " - wget -O /tmp/wqy-microhei.ttc https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc 2>&1 | grep -E 'Connecting|Length|saved' || true -" || { - echo -e "${YELLOW}GitHub下载失败,尝试使用代理...${NC}" - docker exec -u root "$CONTAINER_NAME" sh -c " - wget -O /tmp/wqy-microhei.ttc https://ghproxy.com/https://github.com/anthonyfok/fonts-wqy-microhei/raw/master/wqy-microhei.ttc - " || { - echo -e "${RED}✗ 字体下载失败${NC}" - echo "请检查网络连接或手动下载字体文件。" - exit 1 - } -} -echo -e "${GREEN}✓ 字体下载成功${NC}" -echo "" - -# 安装字体 -echo -e "${YELLOW}步骤 5/6: 安装字体文件...${NC}" -docker exec -u root "$CONTAINER_NAME" sh -c " - mkdir -p /usr/share/fonts/truetype/wqy && \ - mv /tmp/wqy-microhei.ttc /usr/share/fonts/truetype/wqy/ && \ - fc-cache -fv -" || { - echo -e "${RED}✗ 字体安装失败${NC}" - exit 1 -} -echo -e "${GREEN}✓ 字体安装成功${NC}" -echo "" - -# 验证安装 -echo -e "${YELLOW}步骤 6/6: 验证字体安装...${NC}" -FONT_COUNT=$(docker exec "$CONTAINER_NAME" fc-list :lang=zh | wc -l) -if [ "$FONT_COUNT" -gt 0 ]; then - echo -e "${GREEN}✓ 中文字体验证成功!${NC}" - echo "已安装的中文字体:" - docker exec "$CONTAINER_NAME" fc-list :lang=zh -else - echo -e "${RED}✗ 字体验证失败${NC}" - exit 1 -fi -echo "" - -# 完成提示 -echo -e "${GREEN}================================${NC}" -echo -e "${GREEN}修复完成!${NC}" -echo -e "${GREEN}================================${NC}" -echo "" -echo "后续步骤:" -echo "1. 不需要重启容器,字体已生效" -echo "2. 重新生成捐款证书即可看到效果" -echo "3. 如果仍有问题,请查看容器日志:" -echo " docker logs -f $CONTAINER_NAME" -echo "" -echo -e "${YELLOW}注意:${NC}" -echo "- 此修复方法在容器重启后会失效" -echo "- 建议后续使用更新后的Dockerfile重新构建镜像" -echo "- 详细文档请参考:docs/chinese-font-fix-guide.md" -echo "" diff --git a/cn/afterturn/easypoi/excel/entity/ImportParams.java b/src/main/java/com/gxwebsoft/common/core/web/ImportParams.java similarity index 97% rename from cn/afterturn/easypoi/excel/entity/ImportParams.java rename to src/main/java/com/gxwebsoft/common/core/web/ImportParams.java index f6cad32..e059889 100644 --- a/cn/afterturn/easypoi/excel/entity/ImportParams.java +++ b/src/main/java/com/gxwebsoft/common/core/web/ImportParams.java @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package cn.afterturn.easypoi.excel.entity; +package com.gxwebsoft.common.core.web; +import cn.afterturn.easypoi.excel.entity.ExcelBaseParams; import cn.afterturn.easypoi.handler.inter.IExcelVerifyHandler; import lombok.Data; diff --git a/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java b/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java new file mode 100644 index 0000000..bd30f72 --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/controller/GltTicketOrderController.java @@ -0,0 +1,127 @@ +package com.gxwebsoft.glt.controller; + +import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.common.core.web.ApiResult; +import com.gxwebsoft.common.core.web.BaseController; +import com.gxwebsoft.common.core.web.BatchParam; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.glt.entity.GltTicketOrder; +import com.gxwebsoft.glt.param.GltTicketOrderParam; +import com.gxwebsoft.glt.service.GltTicketOrderService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 送水订单控制器 + * + * @author 科技小王子 + * @since 2026-02-05 18:50:21 + */ +@Tag(name = "送水订单管理") +@RestController +@RequestMapping("/api/glt/glt-ticket-order") +public class GltTicketOrderController extends BaseController { + @Resource + private GltTicketOrderService gltTicketOrderService; + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:list')") + @Operation(summary = "分页查询送水订单") + @GetMapping("/page") + public ApiResult> page(GltTicketOrderParam param) { + // 使用关联查询 + return success(gltTicketOrderService.pageRel(param)); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:list')") + @Operation(summary = "查询全部送水订单") + @GetMapping() + public ApiResult> list(GltTicketOrderParam param) { + // 使用关联查询 + return success(gltTicketOrderService.listRel(param)); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:list')") + @Operation(summary = "根据id查询送水订单") + @GetMapping("/{id}") + public ApiResult get(@PathVariable("id") Integer id) { + // 使用关联查询 + return success(gltTicketOrderService.getByIdRel(id)); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:save')") + @OperationLog + @Operation(summary = "添加送水订单") + @PostMapping() + public ApiResult save(@RequestBody GltTicketOrder gltTicketOrder) { + // 记录当前登录用户id + // User loginUser = getLoginUser(); + // if (loginUser != null) { + // gltTicketOrder.setUserId(loginUser.getUserId()); + // } + if (gltTicketOrderService.save(gltTicketOrder)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:update')") + @OperationLog + @Operation(summary = "修改送水订单") + @PutMapping() + public ApiResult update(@RequestBody GltTicketOrder gltTicketOrder) { + if (gltTicketOrderService.updateById(gltTicketOrder)) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:remove')") + @OperationLog + @Operation(summary = "删除送水订单") + @DeleteMapping("/{id}") + public ApiResult remove(@PathVariable("id") Integer id) { + if (gltTicketOrderService.removeById(id)) { + return success("删除成功"); + } + return fail("删除失败"); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:save')") + @OperationLog + @Operation(summary = "批量添加送水订单") + @PostMapping("/batch") + public ApiResult saveBatch(@RequestBody List list) { + if (gltTicketOrderService.saveBatch(list)) { + return success("添加成功"); + } + return fail("添加失败"); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:update')") + @OperationLog + @Operation(summary = "批量修改送水订单") + @PutMapping("/batch") + public ApiResult removeBatch(@RequestBody BatchParam batchParam) { + if (batchParam.update(gltTicketOrderService, "id")) { + return success("修改成功"); + } + return fail("修改失败"); + } + + @PreAuthorize("hasAuthority('glt:gltTicketOrder:remove')") + @OperationLog + @Operation(summary = "批量删除送水订单") + @DeleteMapping("/batch") + public ApiResult removeBatch(@RequestBody List ids) { + if (gltTicketOrderService.removeByIds(ids)) { + return success("删除成功"); + } + return fail("删除失败"); + } + +} diff --git a/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java b/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java new file mode 100644 index 0000000..7ad8f18 --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/entity/GltTicketOrder.java @@ -0,0 +1,84 @@ +package com.gxwebsoft.glt.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableLogic; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.time.LocalDateTime; + +/** + * 送水订单 + * + * @author 科技小王子 + * @since 2026-02-05 18:50:20 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Schema(name = "GltTicketOrder对象", description = "送水订单") +public class GltTicketOrder implements Serializable { + private static final long serialVersionUID = 1L; + + @TableId(value = "id", type = IdType.AUTO) + private Integer id; + + @Schema(description = "用户水票ID") + private Integer userTicketId; + + @Schema(description = "门店ID") + private Integer storeId; + + @Schema(description = "配送员") + private Integer riderId; + + @Schema(description = "仓库ID") + private Integer warehouseId; + + @Schema(description = "关联收货地址") + private Integer addressId; + + @Schema(description = "收货地址") + private String address; + + @Schema(description = "买家留言") + private String buyerRemarks; + + @Schema(description = "用于统计") + private BigDecimal price; + + @Schema(description = "购买数量") + private Integer totalNum; + + @Schema(description = "用户ID") + private Integer userId; + + @Schema(description = "排序(数字越小越靠前)") + private Integer sortNumber; + + @Schema(description = "备注") + private String comments; + + @Schema(description = "状态, 0正常, 1冻结") + private Integer status; + + @Schema(description = "是否删除, 0否, 1是") + @TableLogic + private Integer deleted; + + @Schema(description = "租户id") + private Integer tenantId; + + @Schema(description = "创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime createTime; + + @Schema(description = "修改时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private LocalDateTime updateTime; + +} diff --git a/src/main/java/com/gxwebsoft/glt/mapper/GltTicketOrderMapper.java b/src/main/java/com/gxwebsoft/glt/mapper/GltTicketOrderMapper.java new file mode 100644 index 0000000..8af4a07 --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/mapper/GltTicketOrderMapper.java @@ -0,0 +1,37 @@ +package com.gxwebsoft.glt.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.gxwebsoft.glt.entity.GltTicketOrder; +import com.gxwebsoft.glt.param.GltTicketOrderParam; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 送水订单Mapper + * + * @author 科技小王子 + * @since 2026-02-05 18:50:20 + */ +public interface GltTicketOrderMapper extends BaseMapper { + + /** + * 分页查询 + * + * @param page 分页对象 + * @param param 查询参数 + * @return List + */ + List selectPageRel(@Param("page") IPage page, + @Param("param") GltTicketOrderParam param); + + /** + * 查询全部 + * + * @param param 查询参数 + * @return List + */ + List selectListRel(@Param("param") GltTicketOrderParam param); + +} diff --git a/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml new file mode 100644 index 0000000..da3ee1d --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/mapper/xml/GltTicketOrderMapper.xml @@ -0,0 +1,81 @@ + + + + + + + SELECT a.* + FROM glt_ticket_order a + + + AND a.id = #{param.id} + + + AND a.user_ticket_id = #{param.userTicketId} + + + AND a.store_id = #{param.storeId} + + + AND a.rider_id = #{param.riderId} + + + AND a.warehouse_id = #{param.warehouseId} + + + AND a.address_id = #{param.addressId} + + + AND a.address LIKE CONCAT('%', #{param.address}, '%') + + + AND a.buyer_remarks LIKE CONCAT('%', #{param.buyerRemarks}, '%') + + + AND a.price = #{param.price} + + + AND a.total_num = #{param.totalNum} + + + AND a.user_id = #{param.userId} + + + AND a.sort_number = #{param.sortNumber} + + + AND a.comments LIKE CONCAT('%', #{param.comments}, '%') + + + AND a.status = #{param.status} + + + AND a.deleted = #{param.deleted} + + + AND a.deleted = 0 + + + AND a.create_time >= #{param.createTimeStart} + + + AND a.create_time <= #{param.createTimeEnd} + + + AND (a.comments LIKE CONCAT('%', #{param.keywords}, '%') + ) + + + + + + + + + + + diff --git a/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java b/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java new file mode 100644 index 0000000..771be89 --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/param/GltTicketOrderParam.java @@ -0,0 +1,82 @@ +package com.gxwebsoft.glt.param; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.gxwebsoft.common.core.annotation.QueryField; +import com.gxwebsoft.common.core.annotation.QueryType; +import com.gxwebsoft.common.core.web.BaseParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; + +/** + * 送水订单查询参数 + * + * @author 科技小王子 + * @since 2026-02-05 18:50:19 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@JsonInclude(JsonInclude.Include.NON_NULL) +@Schema(name = "GltTicketOrderParam对象", description = "送水订单查询参数") +public class GltTicketOrderParam extends BaseParam { + private static final long serialVersionUID = 1L; + + @QueryField(type = QueryType.EQ) + private Integer id; + + @Schema(description = "用户水票ID") + @QueryField(type = QueryType.EQ) + private Integer userTicketId; + + @Schema(description = "门店ID") + @QueryField(type = QueryType.EQ) + private Integer storeId; + + @Schema(description = "配送员") + @QueryField(type = QueryType.EQ) + private Integer riderId; + + @Schema(description = "仓库ID") + @QueryField(type = QueryType.EQ) + private Integer warehouseId; + + @Schema(description = "关联收货地址") + @QueryField(type = QueryType.EQ) + private Integer addressId; + + @Schema(description = "收货地址") + private String address; + + @Schema(description = "买家留言") + private String buyerRemarks; + + @Schema(description = "用于统计") + @QueryField(type = QueryType.EQ) + private BigDecimal price; + + @Schema(description = "购买数量") + @QueryField(type = QueryType.EQ) + private Integer totalNum; + + @Schema(description = "用户ID") + @QueryField(type = QueryType.EQ) + private Integer userId; + + @Schema(description = "排序(数字越小越靠前)") + @QueryField(type = QueryType.EQ) + private Integer sortNumber; + + @Schema(description = "备注") + private String comments; + + @Schema(description = "状态, 0正常, 1冻结") + @QueryField(type = QueryType.EQ) + private Integer status; + + @Schema(description = "是否删除, 0否, 1是") + @QueryField(type = QueryType.EQ) + private Integer deleted; + +} diff --git a/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java b/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java new file mode 100644 index 0000000..53acf91 --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/service/GltTicketOrderService.java @@ -0,0 +1,42 @@ +package com.gxwebsoft.glt.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.glt.entity.GltTicketOrder; +import com.gxwebsoft.glt.param.GltTicketOrderParam; + +import java.util.List; + +/** + * 送水订单Service + * + * @author 科技小王子 + * @since 2026-02-05 18:50:20 + */ +public interface GltTicketOrderService extends IService { + + /** + * 分页关联查询 + * + * @param param 查询参数 + * @return PageResult + */ + PageResult pageRel(GltTicketOrderParam param); + + /** + * 关联查询全部 + * + * @param param 查询参数 + * @return List + */ + List listRel(GltTicketOrderParam param); + + /** + * 根据id查询 + * + * @param id + * @return GltTicketOrder + */ + GltTicketOrder getByIdRel(Integer id); + +} diff --git a/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java b/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java new file mode 100644 index 0000000..a47e5eb --- /dev/null +++ b/src/main/java/com/gxwebsoft/glt/service/impl/GltTicketOrderServiceImpl.java @@ -0,0 +1,47 @@ +package com.gxwebsoft.glt.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.gxwebsoft.common.core.web.PageParam; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.glt.entity.GltTicketOrder; +import com.gxwebsoft.glt.mapper.GltTicketOrderMapper; +import com.gxwebsoft.glt.param.GltTicketOrderParam; +import com.gxwebsoft.glt.service.GltTicketOrderService; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 送水订单Service实现 + * + * @author 科技小王子 + * @since 2026-02-05 18:50:20 + */ +@Service +public class GltTicketOrderServiceImpl extends ServiceImpl implements GltTicketOrderService { + + @Override + public PageResult pageRel(GltTicketOrderParam param) { + PageParam page = new PageParam<>(param); + page.setDefaultOrder("sort_number asc, create_time desc"); + List list = baseMapper.selectPageRel(page, param); + return new PageResult<>(list, page.getTotal()); + } + + @Override + public List listRel(GltTicketOrderParam param) { + List list = baseMapper.selectListRel(param); + // 排序 + PageParam page = new PageParam<>(); + page.setDefaultOrder("sort_number asc, create_time desc"); + return page.sortRecords(list); + } + + @Override + public GltTicketOrder getByIdRel(Integer id) { + GltTicketOrderParam param = new GltTicketOrderParam(); + param.setId(id); + return param.getOne(baseMapper.selectListRel(param)); + } + +} diff --git a/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java b/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java index 4335a0c..97b8e1d 100644 --- a/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java +++ b/src/main/java/com/gxwebsoft/glt/task/DealerOrderSettlement10584Task.java @@ -6,6 +6,7 @@ import com.gxwebsoft.common.core.annotation.IgnoreTenant; import com.gxwebsoft.shop.entity.ShopDealerCapital; import com.gxwebsoft.shop.entity.ShopDealerOrder; import com.gxwebsoft.shop.entity.ShopDealerReferee; +import com.gxwebsoft.shop.entity.ShopDealerSetting; import com.gxwebsoft.shop.entity.ShopDealerUser; import com.gxwebsoft.shop.entity.ShopGoods; import com.gxwebsoft.shop.entity.ShopOrder; @@ -15,11 +16,13 @@ import com.gxwebsoft.common.system.mapper.UserMapper; import com.gxwebsoft.shop.service.ShopDealerCapitalService; import com.gxwebsoft.shop.service.ShopDealerOrderService; import com.gxwebsoft.shop.service.ShopDealerRefereeService; +import com.gxwebsoft.shop.service.ShopDealerSettingService; import com.gxwebsoft.shop.service.ShopDealerUserService; import com.gxwebsoft.shop.service.ShopGoodsService; import com.gxwebsoft.shop.service.ShopOrderService; import com.gxwebsoft.shop.service.ShopOrderGoodsService; import com.gxwebsoft.shop.util.UpstreamUserFinder; +import com.alibaba.fastjson.JSONObject; import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @@ -72,6 +75,9 @@ public class DealerOrderSettlement10584Task { @Resource private ShopDealerOrderService shopDealerOrderService; + @Resource + private ShopDealerSettingService shopDealerSettingService; + @Resource private ShopGoodsService shopGoodsService; @@ -96,10 +102,12 @@ public class DealerOrderSettlement10584Task { // Per-run caches to reduce DB chatter across orders. Map level1ParentCache = new HashMap<>(); Map shopRoleCache = new HashMap<>(); - Integer totalDealerUserId = findTotalDealerUserId(); - if (totalDealerUserId == null) { + DealerBasicSetting dealerBasicSetting = findDealerBasicSetting(); + ShopDealerUser totalDealerUser = findTotalDealerUser(); + if (totalDealerUser == null || totalDealerUser.getUserId() == null) { log.warn("未找到总经销商账号,订单仍可结算但不会发放总经销商分润 - tenantId={}", TENANT_ID); } + log.debug("租户{}分销设置 - level={}", TENANT_ID, dealerBasicSetting.level); log.info("租户{}待结算订单数: {}, orderNos(sample)={}", TENANT_ID, @@ -113,7 +121,7 @@ public class DealerOrderSettlement10584Task { if (!claimOrderToSettle(order.getOrderId())) { return; } - settleOneOrder(order, level1ParentCache, shopRoleCache, totalDealerUserId); + settleOneOrder(order, level1ParentCache, shopRoleCache, totalDealerUser, dealerBasicSetting.level); }); } catch (Exception e) { log.error("订单结算失败,将回滚本订单并在下次任务重试 - orderId={}, orderNo={}", order.getOrderId(), order.getOrderNo(), e); @@ -150,7 +158,8 @@ public class DealerOrderSettlement10584Task { ShopOrder order, Map level1ParentCache, Map shopRoleCache, - Integer totalDealerUserId + ShopDealerUser totalDealerUser, + int dealerLevel ) { if (order.getUserId() == null || order.getOrderNo() == null) { throw new IllegalStateException("订单关键信息缺失,无法结算 - orderId=" + order.getOrderId()); @@ -188,13 +197,13 @@ public class DealerOrderSettlement10584Task { commissionConfig.storeSimpleValue); // 1) 直推/间推(shop_dealer_referee) - DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount, goodsQty, commissionConfig); + DealerRefereeCommission dealerRefereeCommission = settleDealerRefereeCommission(order, baseAmount, goodsQty, commissionConfig, dealerLevel); // 2) 门店分红上级:从下单用户开始逐级向上找,命中 ShopDealerUser.type=1 的最近两级(直推门店/间推门店)。 ShopRoleCommission shopRoleCommission = settleShopRoleRefereeCommission(order, baseAmount, goodsQty, commissionConfig, level1ParentCache, shopRoleCache); // 3) 总经销商分润:固定比率,每个订单都分。 - TotalDealerCommission totalDealerCommission = settleTotalDealerCommission(order, baseAmount, goodsQty, totalDealerUserId); + TotalDealerCommission totalDealerCommission = settleTotalDealerCommission(order, baseAmount, goodsQty, totalDealerUser); // 4) 写入分销订单记录(用于排查/统计;详细分佣以 ShopDealerCapital 为准) createDealerOrderRecord(order, baseAmount, dealerRefereeCommission, shopRoleCommission, totalDealerCommission); @@ -202,18 +211,38 @@ public class DealerOrderSettlement10584Task { log.info("订单结算完成 - orderId={}, orderNo={}, baseAmount={}", order.getOrderId(), order.getOrderNo(), baseAmount); } - private DealerRefereeCommission settleDealerRefereeCommission(ShopOrder order, BigDecimal baseAmount, int goodsQty, CommissionConfig commissionConfig) { + private DealerRefereeCommission settleDealerRefereeCommission( + ShopOrder order, + BigDecimal baseAmount, + int goodsQty, + CommissionConfig commissionConfig, + int dealerLevel + ) { // 兼容两种数据形态: - // 1) 同一 userId 下有 level=1/2 的多级关系(直接按 level 取); + // 1) 同一 userId 下有 level=1/2/3 的多级关系(直接按 level 取); // 2) 仅维护 level=1(用“查两次”回退获取上级)。 - Integer directDealerId = getDealerRefereeId(order.getUserId(), 1); - Integer simpleDealerId = getDealerRefereeId(order.getUserId(), 2); - if (simpleDealerId == null && directDealerId != null) { - simpleDealerId = getDealerRefereeId(directDealerId, 1); + // + // 严格按“分销设置 level”决定发放到第几级,避免 level=2 时仍触发第3级发放逻辑。 + int normalizedLevel = normalizeDealerLevel(dealerLevel); + + Integer directDealerId = null; + Integer simpleDealerId = null; + Integer thirdDealerId = null; + + if (normalizedLevel >= 1) { + directDealerId = getDealerRefereeId(order.getUserId(), 1); } - Integer thirdDealerId = getDealerRefereeId(order.getUserId(), 3); - if (thirdDealerId == null && simpleDealerId != null) { - thirdDealerId = getDealerRefereeId(simpleDealerId, 1); + if (normalizedLevel >= 2) { + simpleDealerId = getDealerRefereeId(order.getUserId(), 2); + if (simpleDealerId == null && directDealerId != null) { + simpleDealerId = getDealerRefereeId(directDealerId, 1); + } + } + if (normalizedLevel >= 3) { + thirdDealerId = getDealerRefereeId(order.getUserId(), 3); + if (thirdDealerId == null && simpleDealerId != null) { + thirdDealerId = getDealerRefereeId(simpleDealerId, 1); + } } BigDecimal directMoney = @@ -228,27 +257,33 @@ public class DealerOrderSettlement10584Task { order.getOrderNo(), order.getUserId(), directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney); // 直推:对方=买家;推荐奖(5%):对方=直推分销商(便于在资金明细中看出“来自哪个下级分销商/团队订单”) - creditDealerCommission( - directDealerId, - directMoney, - order, - order.getUserId(), - buildCommissionComment("直推佣金", commissionConfig.commissionType, commissionConfig.dealerDirectValue, goodsQty) - ); - creditDealerCommission( - simpleDealerId, - simpleMoney, - order, - directDealerId, - buildCommissionComment("推荐奖", commissionConfig.commissionType, commissionConfig.dealerSimpleValue, goodsQty) - ); - creditDealerCommission( - thirdDealerId, - thirdMoney, - order, - simpleDealerId, - buildCommissionComment("分润收入", commissionConfig.commissionType, commissionConfig.dealerThirdValue, goodsQty) - ); + if (normalizedLevel >= 1) { + creditDealerCommission( + directDealerId, + directMoney, + order, + order.getUserId(), + buildCommissionComment("直推佣金", commissionConfig.commissionType, commissionConfig.dealerDirectValue, goodsQty) + ); + } + if (normalizedLevel >= 2) { + creditDealerCommission( + simpleDealerId, + simpleMoney, + order, + directDealerId, + buildCommissionComment("推荐奖", commissionConfig.commissionType, commissionConfig.dealerSimpleValue, goodsQty) + ); + } + if (normalizedLevel >= 3) { + creditDealerCommission( + thirdDealerId, + thirdMoney, + order, + simpleDealerId, + buildCommissionComment("分润收入", commissionConfig.commissionType, commissionConfig.dealerThirdValue, goodsQty) + ); + } return new DealerRefereeCommission(directDealerId, directMoney, simpleDealerId, simpleMoney, thirdDealerId, thirdMoney); } @@ -340,26 +375,30 @@ public class DealerOrderSettlement10584Task { ShopOrder order, BigDecimal baseAmount, int goodsQty, - Integer totalDealerUserId + ShopDealerUser totalDealerUser ) { - if (totalDealerUserId == null) { + if (totalDealerUser == null || totalDealerUser.getUserId() == null) { return TotalDealerCommission.empty(); } - BigDecimal money = calcMoneyByCommissionType(baseAmount, TOTAL_DEALER_DIVIDEND_RATE, goodsQty, DIVIDEND_SCALE, 20); + BigDecimal rate = safePositive(totalDealerUser.getRate()); + if (rate.signum() <= 0) { + rate = TOTAL_DEALER_DIVIDEND_RATE; + } + BigDecimal money = calcMoneyByCommissionType(baseAmount, rate, goodsQty, DIVIDEND_SCALE, 20); log.info("总经销商分润发放 - orderNo={}, totalDealerUserId={}, rate={}, money={}", - order.getOrderNo(), totalDealerUserId, TOTAL_DEALER_DIVIDEND_RATE, money); + order.getOrderNo(), totalDealerUser.getUserId(), rate, money); creditDealerCommission( - totalDealerUserId, + totalDealerUser.getUserId(), money, order, order.getUserId(), - buildCommissionComment("总经销商分润", 20, TOTAL_DEALER_DIVIDEND_RATE, goodsQty) + buildCommissionComment("总经销商分润", 20, rate, goodsQty) ); - return new TotalDealerCommission(totalDealerUserId, money); + return new TotalDealerCommission(totalDealerUser.getUserId(), money); } - private Integer findTotalDealerUserId() { - ShopDealerUser dealerUser = shopDealerUserService.getOne( + private ShopDealerUser findTotalDealerUser() { + return shopDealerUserService.getOne( new LambdaQueryWrapper() .eq(ShopDealerUser::getTenantId, TENANT_ID) .eq(ShopDealerUser::getType, 2) @@ -367,7 +406,46 @@ public class DealerOrderSettlement10584Task { .orderByAsc(ShopDealerUser::getId) .last("limit 1") ); - return dealerUser != null ? dealerUser.getUserId() : null; + } + + private DealerBasicSetting findDealerBasicSetting() { + int level = 2; + ShopDealerSetting setting = shopDealerSettingService.getOne( + new LambdaQueryWrapper() + .eq(ShopDealerSetting::getTenantId, TENANT_ID) + .eq(ShopDealerSetting::getKey, "basic") + .last("limit 1") + ); + if (setting != null && setting.getValues() != null && !setting.getValues().isBlank()) { + try { + JSONObject json = JSONObject.parseObject(setting.getValues()); + Integer levelVal = json.getInteger("level"); + if (levelVal != null && levelVal > 0) { + level = Math.min(levelVal, 3); + } + } catch (Exception e) { + log.warn("解析分销设置失败,将使用默认等级 - tenantId={}, values={}", TENANT_ID, setting.getValues(), e); + } + } + return new DealerBasicSetting(level); + } + + private int normalizeDealerLevel(int dealerLevel) { + if (dealerLevel <= 0) { + return 2; + } + return Math.min(dealerLevel, 3); + } + + /** + * shop_dealer_setting(key=basic) 的关键配置(仅取结算任务需要的字段)。 + */ + private static class DealerBasicSetting { + private final int level; + + private DealerBasicSetting(int level) { + this.level = level; + } } /** diff --git a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java index 1670772..9456ebb 100644 --- a/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java +++ b/src/main/java/com/gxwebsoft/shop/controller/ShopOrderController.java @@ -265,7 +265,7 @@ public class ShopOrderController extends BaseController { return fail("订单不存在"); } // 退款相关操作单独走退款接口,便于做财务权限隔离 - if (Objects.equals(shopOrder.getOrderStatus(), 4) || Objects.equals(shopOrder.getOrderStatus(), 6)) { + if (Objects.equals(shopOrder.getOrderStatus(), 6)) { return fail("退款相关操作请使用退款接口: PUT /api/shop/shop-order/refund"); } ShopOrder shopOrderNow = shopOrderService.getById(shopOrder.getOrderId());