“mitao”
2025-12-12 0434bb26bedded4de281f0a23c8e6ca6f83a27b0
小程序首页接口
12个文件已添加
19个文件已修改
749 ■■■■■ 已修改文件
ruoyi-applet/pom.xml 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/icbcpay/icbc-config-dev.yml 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/icbcpay/icbc-config-prod.yml 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/src/main/resources/icbcpay/icbc-config-test.yml 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/lib/hsm-software-share-1.0.5.jar 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/lib/icbc-api-sdk-cop-io.jar 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/lib/icbc-api-sdk-cop.jar 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/lib/icbc-ca.jar 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/pom.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/config/IcbcConfig.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/enums/ICBCRespOrderErrorEnum.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TAppUser.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackage.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/model/TPhysicalGoods.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/query/AppletPhysicalGoodsQuery.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TAppUserService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TAppUserServiceImpl.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/SupervisorUserVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInfoVo.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-applet/pom.xml
@@ -186,6 +186,7 @@
                <version>2.5.15</version>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                    <includeSystemScope>true</includeSystemScope> <!-- 本地jar包打包-->
                </configuration>
                <executions>
                    <execution>
ruoyi-applet/src/main/java/com/ruoyi/web/controller/api/IndexController.java
@@ -1,29 +1,84 @@
package com.ruoyi.web.controller.api;
import cn.hutool.core.util.PhoneUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.system.model.TAppUser;
import com.ruoyi.system.model.TBanner;
import com.ruoyi.system.service.TBannerService;
import com.ruoyi.system.model.TBigGiftPackage;
import com.ruoyi.system.model.TPhysicalGoods;
import com.ruoyi.system.query.AppletPhysicalGoodsQuery;
import com.ruoyi.system.service.*;
import com.ruoyi.system.vo.SupervisorUserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Objects;
@Slf4j
@RestController
@RequestMapping("/index")
@Api(tags = "首页模块")
@Validated
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class IndexController {
    private final TBannerService tBannerService;
    private final TAppUserService tAppUserService;
    private final TokenService tokenService;
    private final ISysUserService sysUserService;
    private final TBigGiftPackageService bigGiftPackageService;
    private final TPhysicalGoodsService physicalGoodsService;
    @ApiOperation("根据推广码获取上级用户信息")
    @GetMapping("/getSuperiorInfo")
    public R<SupervisorUserVO> getSuperiorInfo(@ApiParam(name = "superiorType", value = "上级类型 1:股东;2:营销员")
                                               @NotNull(message = "上级类型不能为空") @RequestParam Integer superiorType,
                                               @ApiParam(name = "spreadCode", value = "推广码")
                                               @NotNull(message = "推广码不能为空") @RequestParam String spreadCode) {
        SupervisorUserVO supervisorUserVO = new SupervisorUserVO();
        //根据上级类型获取上级用户信息
        if (superiorType.equals(1)) {
            SysUser sysUser = sysUserService.selectByCode(spreadCode);
            if (Objects.nonNull(sysUser)) {
                supervisorUserVO.setId(sysUser.getUserId().intValue());
                supervisorUserVO.setPhone((String) PhoneUtil.hideBetween(sysUser.getPhonenumber()));
                supervisorUserVO.setNickName(sysUser.getNickName());
                supervisorUserVO.setAvatar(""); //todo 设置默认头像
            }
        } else {
            tAppUserService.lambdaQuery().eq(TAppUser::getCode, spreadCode).ne(TAppUser::getStatus, 3).last("LIMIT 1").oneOpt()
                    .ifPresent(appUser -> {
                        supervisorUserVO.setId(appUser.getId());
                        supervisorUserVO.setPhone((String) PhoneUtil.hideBetween(appUser.getPhone()));
                        supervisorUserVO.setAvatar(appUser.getProfilePhoto());
                        supervisorUserVO.setNickName(appUser.getName());
                    });
        }
        return R.ok(supervisorUserVO);
    }
    @ApiOperation("确认上级用户-接受邀请")
    @GetMapping("/confirmSuperior")
    public R<?> confirmSuperior(@ApiParam(name = "superiorType", value = "上级类型 1:股东;2:营销员")
                                @NotNull(message = "上级类型不能为空") @RequestParam Integer superiorType,
                                @ApiParam(name = "superiorId", value = "上级用户ID")
                                @NotNull(message = "上级ID不能为空") @RequestParam Integer superiorId) {
        LoginUserApplet loginUserApplet = tokenService.getLoginUserApplet();
        tAppUserService.confirmSuperior(superiorType, superiorId, loginUserApplet);
        return R.ok();
    }
    @ApiOperation(value = "首页模块--轮播图",response = TBanner.class)
    @GetMapping(value = "/getBannerList")
@@ -38,5 +93,26 @@
        TBanner tBanner = tBannerService.getById(id);
        return R.ok(tBanner);
    }
    @ApiOperation("获取大礼包列表")
    @GetMapping("/big-gift/list")
    public R<List<TBigGiftPackage>> getBigGiftList() {
        return R.ok(bigGiftPackageService.list());
    }
    @ApiOperation("大礼包商品详情")
    @GetMapping("/big-gift/detail/{id}")
    public R<TBigGiftPackage> getBigGiftDetail(@ApiParam(name = "id", value = "大礼包ID") @PathVariable Integer id) {
        return R.ok(bigGiftPackageService.getById(id));
    }
    @ApiOperation("实物商品分页列表")
    @PostMapping("/physical-goods/page")
    public R<IPage<TPhysicalGoods>> getPhysicalGoodsPageList(@RequestBody AppletPhysicalGoodsQuery query) {
        return R.ok(physicalGoodsService.getPageList(query));
    }
    @ApiOperation("实物商品详情")
    @GetMapping("/physical-goods/detail/{id}")
    public R<TPhysicalGoods> getPhysicalGoodsDetail(@ApiParam(name = "id", value = "实物商品ID")@PathVariable Integer id) {
        return  R.ok(physicalGoodsService.getById(id));
    }
}
ruoyi-applet/src/main/java/com/ruoyi/web/controller/system/SysLoginController.java
@@ -140,7 +140,7 @@
        return ajax;
    }
    @ApiOperation(value = "通过code获得openid,获取用户信息",tags = {"微信小程序登录"})
    @ApiOperation(value = "通过code获得openid,获取用户信息")
    @PostMapping("/openIdByJsCode")
    public R<Map<String, Object>> openIdByJsCode(@RequestBody AppletUserEncrypteData data) {
        log.info("<<<<<<<<换取openid开始<<<<<<<<:{}", data.getCode());
ruoyi-applet/src/main/java/com/ruoyi/web/core/config/SwaggerConfig.java
@@ -1,26 +1,22 @@
package com.ruoyi.web.core.config;
import java.util.ArrayList;
import java.util.List;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.config.RuoYiConfig;
import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.AuthorizationScope;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityReference;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.ArrayList;
import java.util.List;
/**
 * Swagger2的接口配置
@@ -113,9 +109,9 @@
        // 用ApiInfoBuilder进行定制
        return new ApiInfoBuilder()
                // 设置标题
                .title("标题:移动社区管理系统_接口文档")
                .title("嘉视达小程序接口文档")
                // 描述
                .description("移动社区接口文档")
                .description("嘉视达小程序接口文档")
                // 作者信息
                .contact(new Contact(ruoyiConfig.getName(), null, null))
                // 版本
ruoyi-applet/src/main/resources/icbcpay/icbc-config-dev.yml
New file
@@ -0,0 +1,55 @@
icbc:
  api:
    # 聚合支付B2C线上消费下单接口url (无界面)
    pay-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/consumepurchase/V1"
    # 订单拆分请求url
    suborder-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/sersuborder/V1"
    # 订单拆分前退款url
    refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/merrefund/V1"
    # 订单拆分后退款url
    sub-order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/sersuborder/V1"
    # 订单支付查询url
    order-pay-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/orderqry/V1"
    # 订单退款查询url
    order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/refundqry/V1"
    # 完整的回调通知地址,支付完成后通知商户的URL
    notify-url: ""
    # 回调方法的路径
    notify-url-method: "/icbc/icbcNotify"
  keys:
    # 我方私钥,用于签名请求
    my-private-key: ""
    # 对方(即工行)网关公钥,用于验证返回的签名
    api-gw-public-key: ""
  # APP的编号,应用在API开放平台注册时生成
  app-id: ""
  # 商户编号,用于标识商户账户
  mer-id: ""
  # 收单产品协议编号,商户与银行的协议编号
  mer-prtcl-no: ""
  # 设备号,设备的唯一标识符
  decive-info: ""
  # 微信APP的编号,便于与微信支付接口对接
  wx-app-id: ""
  # 微信公众号的APP编号,供公众号支付使用
  wx-gzh-app-id: ""
  # 订单超时时间,单位为分钟
  expire-time: "300"
ruoyi-applet/src/main/resources/icbcpay/icbc-config-prod.yml
New file
@@ -0,0 +1,56 @@
icbc:
  api:
    # 聚合支付B2C线上消费下单接口url (无界面)
    pay-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/consumepurchase/V1"
    # 订单拆分请求url
    suborder-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/sersuborder/V1"
    # 订单拆分前退款url
    refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/merrefund/V1"
    # 订单拆分后退款url
    sub-order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/sersuborder/V1"
    # 订单支付查询url
    order-pay-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/orderqry/V1"
    # 订单退款查询url
    order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/refundqry/V1"
    # 完整的回调通知地址,支付完成后通知商户的URL
    notify-url: ""
    # 回调方法的路径
    notify-url-method: "/icbc/icbcNotify"
  keys:
    # 我方私钥,用于签名请求
    my-private-key: ""
    # 对方(即工行)网关公钥,用于验证返回的签名
    api-gw-public-key: ""
  # APP的编号,应用在API开放平台注册时生成
  app-id: ""
  # 商户编号,用于标识商户账户
  mer-id: ""
  # 收单产品协议编号,商户与银行的协议编号
  mer-prtcl-no: ""
  # 设备号,设备的唯一标识符
  decive-info: ""
  # 微信APP的编号,便于与微信支付接口对接
  wx-app-id: ""
  # 微信公众号的APP编号,供公众号支付使用
  wx-gzh-app-id: ""
  # 订单超时时间,单位为分钟
  expire-time: "300"
ruoyi-applet/src/main/resources/icbcpay/icbc-config-test.yml
New file
@@ -0,0 +1,56 @@
icbc:
  api:
    # 聚合支付B2C线上消费下单接口url (无界面)
    pay-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/consumepurchase/V1"
    # 订单拆分请求url
    suborder-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/sersuborder/V1"
    # 订单拆分前退款url
    refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/merrefund/V1"
    # 订单拆分后退款url
    sub-order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/sersuborder/V1"
    # 订单支付查询url
    order-pay-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/orderqry/V1"
    # 订单退款查询url
    order-refund-url: "https://gw.open.icbc.com.cn/api/cardbusiness/aggregatepay/b2c/online/refundqry/V1"
    # 完整的回调通知地址,支付完成后通知商户的URL
    notify-url: ""
    # 回调方法的路径
    notify-url-method: "/icbc/icbcNotify"
  keys:
    # 我方私钥,用于签名请求
    my-private-key: ""
    # 对方(即工行)网关公钥,用于验证返回的签名
    api-gw-public-key: ""
  # APP的编号,应用在API开放平台注册时生成
  app-id: ""
  # 商户编号,用于标识商户账户
  mer-id: ""
  # 收单产品协议编号,商户与银行的协议编号
  mer-prtcl-no: ""
  # 设备号,设备的唯一标识符
  decive-info: ""
  # 微信APP的编号,便于与微信支付接口对接
  wx-app-id: ""
  # 微信公众号的APP编号,供公众号支付使用
  wx-gzh-app-id: ""
  # 订单超时时间,单位为分钟
  expire-time: "300"
ruoyi-common/src/main/java/com/ruoyi/common/utils/DateUtils.java
@@ -38,6 +38,7 @@
            "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy-MM", 
            "yyyy/MM/dd", "yyyy/MM/dd HH:mm:ss", "yyyy/MM/dd HH:mm", "yyyy/MM",
            "yyyy.MM.dd", "yyyy.MM.dd HH:mm:ss", "yyyy.MM.dd HH:mm", "yyyy.MM"};
    private static final DateTimeFormatter FORMATTER_WITH_T = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss");
    /**
     * 获取当前Date型日期
@@ -408,4 +409,8 @@
        return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
    }
    public static String getCurrentDateTimeWithT() {
        LocalDateTime now = LocalDateTime.now();
        return now.format(FORMATTER_WITH_T);
    }
}
ruoyi-framework/src/main/java/com/ruoyi/framework/web/service/SysLoginService.java
@@ -24,6 +24,7 @@
import com.ruoyi.system.service.TAppUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@@ -59,6 +60,9 @@
    private SysPermissionService permissionService;
    @Autowired
    private TAppUserService tAppUserService;
    @Value("${spring.profiles.active}")
    private String env;
    /**
     * 登录验证
@@ -203,12 +207,20 @@
            log.info("登录用户:{} 已被删除.", username);
            throw new ServiceException(MessageUtils.message("user.password.delete"));
        }
        if ("test".equals(env) || "dev".equals(env)) {
            if (!code.equals("111111")) {
                log.info("登录用户:{} 短信验证码错误{}", username,code);
                throw new ServiceException("短信验证码错误");
            }
        } else {
        // 校验验证码
        Object cacheObject = redisCache.getCacheObject(user.getPhone());
        if(!code.equals(String.valueOf(cacheObject))){
            log.info("登录用户:{} 短信验证码错误{}", username,code);
            throw new ServiceException("短信验证码错误");
        }
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUserApplet loginUser = new LoginUserApplet(user.getId(), null, null, null);
        recordAppLoginInfo(loginUser.getUserId().toString());
ruoyi-system/lib/hsm-software-share-1.0.5.jar
Binary files differ
ruoyi-system/lib/icbc-api-sdk-cop-io.jar
Binary files differ
ruoyi-system/lib/icbc-api-sdk-cop.jar
Binary files differ
ruoyi-system/lib/icbc-ca.jar
Binary files differ
ruoyi-system/pom.xml
@@ -99,6 +99,20 @@
            <artifactId>wechatpay-apache-httpclient</artifactId>
            <version>0.4.9</version>
        </dependency>
        <dependency>
            <groupId>icbc-api-sdk-cop</groupId>
            <artifactId>com.icbc</artifactId>
            <version>1.0.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/icbc-api-sdk-cop.jar</systemPath>
        </dependency>
        <dependency>
            <groupId>icbc-api-sdk-cop-io</groupId>
            <artifactId>com.icbc</artifactId>
            <version>1.0.0</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/icbc-api-sdk-cop-io.jar</systemPath>
        </dependency>
    </dependencies>
</project>
ruoyi-system/src/main/java/com/ruoyi/system/config/IcbcConfig.java
New file
@@ -0,0 +1,136 @@
package com.ruoyi.system.config;
import com.icbc.api.DefaultIcbcClient;
import com.icbc.api.IcbcConstants;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
 * @description:
 * @author: yangchuan
 * @create: 2024-11-18 17:10
 */
@Component
@Data
public class IcbcConfig {
    /**
     * 聚合支付B2C线上消费下单接口url (无界面)
     */
    @Value("${icbc.api.pay-url}")
    private String apiPayUrl;
    /**
     * 订单拆分请求url
     */
    @Value("${icbc.api.suborder-url}")
    private String apiSubOrderUrl;
    /**
     * 订单拆分前退款url
     */
    @Value("${icbc.api.refund-url}")
    private String refundUrl;
    /**
     * 订单拆分后退款url
     */
    @Value("${icbc.api.sub-order-refund-url}")
    private String subOrderRefundUrl;
    /**
     * 订单支付查询url
     */
    @Value("${icbc.api.order-pay-url}")
    private String selectOrderPayUrl;
    /**
     * 订单退款查询url
     */
    @Value("${icbc.api.order-refund-url}")
    private String selectOrderRefundUrl;
    /**
     * 我方私钥,用于签名请求
     */
    @Value("${icbc.keys.my-private-key}")
    private String myPrivateKey;
    /**
     * 对方(即工行)网关公钥,用于验证返回的签名
     */
    @Value("${icbc.keys.api-gw-public-key}")
    private String apiGwPublicKey;
    /**
     * APP编号
     */
    @Value("${icbc.app-id}")
    private String appId;
    /**
     * 商户编号
     */
    @Value("${icbc.mer-id}")
    private String merId;
    /**
     * 收单产品协议编号
     */
    @Value("${icbc.mer-prtcl-no}")
    private String merPrtclNo;
    /**
     * 设备号
     */
    @Value("${icbc.decive-info}")
    private String deciveInfo;
    /**
     * 完整回调通知地址
     */
    @Value("${icbc.api.notify-url}")
    private String notifyUrl;
    /**
     * 完整回调通知地址
     */
    @Value("${icbc.api.notify-url-method}")
    private String notifyUrlMethod;
    /**
     * 微信APP编号
     */
    @Value("${icbc.wx-app-id}")
    private String wxAppId;
    /**
     * 微信公众号APP编号
     */
    @Value("${icbc.wx-gzh-app-id}")
    private String wxGzhAppId;
    /**
     * 订单超时时间,单位为分钟
     */
    @Value("${icbc.expire-time}")
    private String expireTime;
    private volatile static DefaultIcbcClient instance = null;
    public static DefaultIcbcClient getInstance(IcbcConfig icbcConfig) {
        if (instance == null) {
            synchronized (IcbcConfig.class) {
                if (instance == null) {
                    instance = new DefaultIcbcClient(icbcConfig.appId, IcbcConstants.SIGN_TYPE_RSA2, icbcConfig.myPrivateKey, icbcConfig.apiGwPublicKey);
                }
            }
        }
        return instance;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/enums/ICBCRespOrderErrorEnum.java
New file
@@ -0,0 +1,52 @@
package com.ruoyi.system.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@AllArgsConstructor
@Getter
public enum ICBCRespOrderErrorEnum {
    SUCCESS("0", "成功"),
    INVALID_PARAMETER("400011", "参数非法,原因可能为app id为空、app id非法、签名为空、应用系统时间与API平台系统时间不在限定差值以内、时间戳非法"),
    API_NOT_FOUND("500018", "访问的API不存在"),
    ILLEGAL_CALL("500020", "非法调用"),
    MISSING_APP_PUBLIC_KEY("400016", "app公钥未维护"),
    SIGNATURE_VERIFICATION_FAILED("400017", "签名验证失败"),
    AUTHORIZATION_FAILED("400019", "授权验证失败"),
    RATE_LIMIT_EXCEEDED("500031", "速率超限"),
    CONCURRENCY_LIMIT_EXCEEDED("500032", "并发超限"),
    PROXY_EXCEPTION("-500041", "代理异常"),
    PROXY_TIMEOUT("-500042", "代理超时"),
    GATEWAY_SIGNATURE_FAILED("-500044", "网关签名失败"),
    GATEWAY_CONFIG_ERROR("500043", "网关配置文件错误,无法从配置文件中读取配置"),
    GATEWAY_INTERNAL_ERROR("-500099", "网关内部异常"),
    PARAMS_ERROR("20000000", "输入参数检查失败"),
    SERVICE_UNKNOWN_ERROR("20000001", "Service层未知的服务异常"),
    UNKNOWN_ERROR("-100000", "未知异常");
    // 异常码
    private String errorCode;
    // 错误信息
    private String errorMsg;
    // 根据错误码查找错误信息
    public static String getMessageByCode(String errorCode) {
        for (ICBCRespOrderErrorEnum error : values()) {
            if (error.getErrorCode().equals(errorCode)) {
                return error.getErrorMsg();
            }
        }
        return "未知错误";
    }
    // 根据错误码获取对应的枚举
    public static ICBCRespOrderErrorEnum fromCode(String errorCode) {
        for (ICBCRespOrderErrorEnum error : values()) {
            if (error.getErrorCode().equals(errorCode)) {
                return error;
            }
        }
        return UNKNOWN_ERROR;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysUserMapper.java
@@ -189,4 +189,11 @@
    int deleteUserByOldUserId(@Param("oldUserId")Long oldUserId);
    void updateUserInherit(SysUser user);
    /**
     * 根据推广码获取股东用户信息
     * @param spreadCode
     * @return
     */
    SysUser selectByCode(@Param("spreadCode") String spreadCode);
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TAppUser.java
@@ -1,17 +1,18 @@
package com.ruoyi.system.model;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import java.time.LocalDate;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
 * <p>
@@ -105,5 +106,8 @@
    @TableField("status")
    private Integer status;
    @ApiModelProperty(value = "股东ID")
    @TableField("referrer_sys_user_id")
    private Integer referrerSysUserId;
}
ruoyi-system/src/main/java/com/ruoyi/system/model/TBigGiftPackage.java
@@ -1,16 +1,14 @@
package com.ruoyi.system.model;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
@@ -90,6 +88,7 @@
    @ApiModelProperty(value = "状态 1=使用中 0=删除")
    @TableField("status")
    @TableLogic
    private Integer status;
ruoyi-system/src/main/java/com/ruoyi/system/model/TPhysicalGoods.java
@@ -1,16 +1,14 @@
package com.ruoyi.system.model;
import java.math.BigDecimal;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * <p>
@@ -74,6 +72,7 @@
    @ApiModelProperty(value = "删除状态:1-未删除,0-已删除")
    @TableField("status")
    @TableLogic
    private Integer status;
    @ApiModelProperty(value = "销量")
ruoyi-system/src/main/java/com/ruoyi/system/query/AppletPhysicalGoodsQuery.java
New file
@@ -0,0 +1,17 @@
package com.ruoyi.system.query;
import com.ruoyi.common.core.domain.BasePage;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
@ApiModel("实物商品查询对象")
public class AppletPhysicalGoodsQuery extends BasePage {
    @ApiModelProperty(value = "商品名称")
    private String name;
}
ruoyi-system/src/main/java/com/ruoyi/system/service/ISysUserService.java
@@ -5,6 +5,7 @@
import com.ruoyi.system.query.SysUserQuery;
import com.ruoyi.system.vo.SysUserVO;
import javax.validation.constraints.NotNull;
import java.util.List;
/**
@@ -289,4 +290,6 @@
    int deleteUserByOldUserId(Long oldUserId);
    void updateUserInherit(SysUser user);
    SysUser selectByCode(@NotNull(message = "推广码不能为空") String spreadCode);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TAppUserService.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.system.model.TAppUser;
import com.ruoyi.system.query.AppUserListQuery;
@@ -17,4 +18,12 @@
    PageInfo<TAppUser> pageList(AppUserListQuery query);
    /**
     * 确认上级用户
     *
     * @param superiorType
     * @param superiorId
     * @param loginUserApplet
     */
    void confirmSuperior(Integer superiorType, Integer superiorId, LoginUserApplet loginUserApplet);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/TPhysicalGoodsService.java
@@ -1,7 +1,9 @@
package com.ruoyi.system.service;
import com.ruoyi.system.model.TPhysicalGoods;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.system.model.TPhysicalGoods;
import com.ruoyi.system.query.AppletPhysicalGoodsQuery;
/**
 * <p>
@@ -12,5 +14,10 @@
 * @since 2025-12-08
 */
public interface TPhysicalGoodsService extends IService<TPhysicalGoods> {
    /**
     * 分页查询实物商品
     * @param query
     * @return
     */
    IPage<TPhysicalGoods> getPageList(AppletPhysicalGoodsQuery query);
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysUserServiceImpl.java
@@ -722,4 +722,9 @@
    public SysUser selectByPhone(String phonenumber) {
        return userMapper.selectByPhone(phonenumber);
    }
    @Override
    public SysUser selectByCode(String spreadCode) {
        return userMapper.selectByCode(spreadCode);
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TAppUserServiceImpl.java
@@ -2,13 +2,21 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.basic.PageInfo;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.model.LoginUserApplet;
import com.ruoyi.system.mapper.TAppUserMapper;
import com.ruoyi.system.model.TAppUser;
import com.ruoyi.system.query.AppUserListQuery;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.TAppUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
/**
 * <p>
@@ -18,8 +26,11 @@
 * @author xiaochen
 * @since 2025-11-06
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class TAppUserServiceImpl extends ServiceImpl<TAppUserMapper, TAppUser> implements TAppUserService {
    private final ISysUserService sysUserService;
    @Override
    public PageInfo<TAppUser> pageList(AppUserListQuery query) {
@@ -28,4 +39,27 @@
        pageInfo.setRecords( list);
        return pageInfo;
    }
    @Override
    public void confirmSuperior(Integer superiorType, Integer superiorId, LoginUserApplet loginUserApplet) {
        TAppUser appUser = this.lambdaQuery().eq(TAppUser::getId, loginUserApplet.getUserId()).ne(TAppUser::getStatus,3)
                .last("LIMIT 1").one();
        if (Objects.nonNull(appUser)) {
            if (superiorType.equals(1)) {
                //查询上级用户所属公司ID
                SysUser sysUser = sysUserService.selectUserById(superiorId.longValue());
                if (Objects.nonNull(sysUser)) {
                    appUser.setReferrerSysUserId(superiorId);
                } else {
                    log.info("推广员不存在,绑定superiorId={}", superiorId);
                }
            } else {
                if (appUser.getReferrerStatus().equals(1)) {
                    appUser.setReferrerUserId(superiorId);
                    appUser.setReferrerTime(LocalDateTime.now());
                }
            }
            this.updateById(appUser);
        }
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/TPhysicalGoodsServiceImpl.java
@@ -1,9 +1,12 @@
package com.ruoyi.system.service.impl;
import com.ruoyi.system.model.TPhysicalGoods;
import com.ruoyi.system.mapper.TPhysicalGoodsMapper;
import com.ruoyi.system.service.TPhysicalGoodsService;
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.ruoyi.system.mapper.TPhysicalGoodsMapper;
import com.ruoyi.system.model.TPhysicalGoods;
import com.ruoyi.system.query.AppletPhysicalGoodsQuery;
import com.ruoyi.system.service.TPhysicalGoodsService;
import org.springframework.stereotype.Service;
/**
@@ -16,5 +19,9 @@
 */
@Service
public class TPhysicalGoodsServiceImpl extends ServiceImpl<TPhysicalGoodsMapper, TPhysicalGoods> implements TPhysicalGoodsService {
    @Override
    public IPage<TPhysicalGoods> getPageList(AppletPhysicalGoodsQuery query) {
        return this.lambdaQuery().like(TPhysicalGoods::getName, query.getName())
                .page(new Page<>(query.getPageNum(), query.getPageSize()));
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/utils/ICBCPayUtil.java
New file
@@ -0,0 +1,80 @@
package com.ruoyi.system.utils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.icbc.api.DefaultIcbcClient;
import com.icbc.api.IcbcApiException;
import com.icbc.api.request.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1;
import com.icbc.api.response.CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.WebUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.system.config.IcbcConfig;
import com.ruoyi.system.enums.ICBCRespOrderErrorEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
@Slf4j
@Component
public class ICBCPayUtil {
    @Autowired
    private IcbcConfig icbcConfig;
    public JSONObject wechatPay(String outTradeNo, BigDecimal totalPrice, String goodsName,String openid){
        CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz bizContent = new
                CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1.CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1Biz();
        bizContent.setMer_id(icbcConfig.getMerId());
        bizContent.setMer_prtcl_no(icbcConfig.getMerPrtclNo());
        bizContent.setDecive_info(icbcConfig.getDeciveInfo());
        bizContent.setOut_trade_no(outTradeNo);
        bizContent.setOrig_date_time(DateUtils.getCurrentDateTimeWithT());
        //交易币种,目前工行只支持使用人民币(001)支付
        bizContent.setFee_type("001");
        bizContent.setSpbill_create_ip(IpUtils.getIpAddr(WebUtils.request()));
        //金额,单位分
        bizContent.setTotal_fee(String.valueOf(totalPrice.multiply(new BigDecimal(100)).intValue()));
        //支付成功后回调url
        bizContent.setMer_url(icbcConfig.getNotifyUrl());
        bizContent.setBody(goodsName);
        //收单接入方式,5-APP,7-微信公众号,8-支付宝生活号,9-微信小程序
        bizContent.setAccess_type("9");
        //支付方式,9-微信;10-支付宝;
        bizContent.setPay_mode("9");
        //商户在微信开放平台注册的APPID,支付方式为微信时不能为空
        bizContent.setShop_appid(icbcConfig.getWxAppId());
        //第三方用户标识,商户在微信公众号内或微信小程序内接入时必送,即access_type为7或9时,上送用户在商户APPID下的唯一标识;商户通过支付宝生活号接入时不送
        bizContent.setOpen_id(openid);
        bizContent.setIcbc_appid(icbcConfig.getAppId());
        bizContent.setExpire_time(icbcConfig.getExpireTime());
        //通知类型,表示在交易处理完成后把交易结果通知商户的处理模式。取值“HS”:在交易完成后将通知信息,主动发送给商户,发送地址为notify_url指定地址; 取值“AG”:在交易完成后不通知商户。不送或送空,默认为"HS"
        bizContent.setNotify_type("HS");
        //结果发送类型,通知方式为HS时有效。取值“0”:无论支付成功或者失败,银行都向商户发送交易通知信息;取值“1”,银行只向商户发送交易成功的通知信息。默认是"0"
        bizContent.setResult_type("0");
        CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1 request = new CardbusinessAggregatepayB2cOnlineConsumepurchaseRequestV1();
        request.setServiceUrl(icbcConfig.getApiPayUrl());
        request.setBizContent(bizContent);
        DefaultIcbcClient client = IcbcConfig.getInstance(icbcConfig);
        CardbusinessAggregatepayB2cOnlineConsumepurchaseResponseV1 response;
        JSONObject responsePackage = new JSONObject();
        try {
            log.info("准备调用工行支付成功,请求参数:{}", JSON.toJSONString(request));
            response = client.execute(request);
            if (response.isSuccess()) {
                log.info("调用工行支付成功,返回信息:{}", JSON.toJSONString(response));
                responsePackage = JSONObject.parseObject(response.getWx_data_package());
            } else {
                log.info("调用工行支付失败,返回信息:{}", JSON.toJSONString(response));
                throw new ServiceException(ICBCRespOrderErrorEnum.fromCode(String.valueOf(response.getReturnCode())).getErrorMsg());
            }
        } catch (IcbcApiException e) {
            log.error(e.toString(), e);
        }
        return responsePackage;
    }
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/SupervisorUserVO.java
New file
@@ -0,0 +1,22 @@
package com.ruoyi.system.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
@ApiModel("上级用户详情视图对象")
public class SupervisorUserVO {
    @ApiModelProperty("ID")
    private Integer id;
    @ApiModelProperty("昵称")
    private String nickName;
    @ApiModelProperty("头像")
    private String avatar;
    @ApiModelProperty("手机号码")
    private String phone;
}
ruoyi-system/src/main/java/com/ruoyi/system/vo/UserInfoVo.java
@@ -6,7 +6,6 @@
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
 * 用户对象 sys_user
ruoyi-system/src/main/resources/mapper/system/SysUserMapper.xml
@@ -287,6 +287,12 @@
               u.login_date AS loginDate, u.create_by AS createBy, u.create_time AS createTime, u.remark AS remark
        from sys_user u where user_id &lt; 0 and u.status = 0 and u.del_flag = 0 order by user_id desc
    </select>
    <select id="selectByCode" resultType="com.ruoyi.common.core.domain.entity.SysUser">
        select * from sys_user
        <where>
            role_type = 3 and del_flag = 0 and code = #{spreadCode}
        </where>
    </select>
    <insert id="insertUser" parameterType="SysUser" useGeneratedKeys="true" keyProperty="userId">
         insert into sys_user(