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.jarBinary files differ
ruoyi-system/lib/icbc-api-sdk-cop-io.jarBinary files differ
ruoyi-system/lib/icbc-api-sdk-cop.jarBinary files differ
ruoyi-system/lib/icbc-ca.jarBinary 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 < 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(