diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiPromotionController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiPromotionController.java new file mode 100644 index 0000000..a918aba --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/controller/AppletApiPromotionController.java @@ -0,0 +1,73 @@ +package org.jeecg.modules.applet.controller; + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.extern.slf4j.Slf4j; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.vo.AppletUser; +import org.jeecg.modules.applet.service.AppletApiPromotionService; +import org.jeecg.modules.demo.appletWater.entity.AppletWater; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@Tag(name = "推广官", description = "推广官") +@RestController +@RequestMapping("/appletApi/promotion") +public class AppletApiPromotionController { + + @Autowired + private AppletApiPromotionService appletApiPromotionService; + + @Operation(summary = "获取推广二维码", description = "获取推广二维码") + @GetMapping(value = "/qrCode", produces = MediaType.IMAGE_PNG_VALUE) + public byte[] getInviteCode(@RequestParam(required = false) String type) { + return appletApiPromotionService.getInviteCode(type); + } + + @Operation(summary = "我的团队", description = "我的团队") + @GetMapping(value = "/team") + public Result> team(@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, + @RequestParam(required = false) @Parameter String name) { + Page page = new Page<>(pageNo, pageSize); + IPage UserList = appletApiPromotionService.getTeam(page, name); + return Result.OK(UserList); + } + + @Operation(summary = "佣金流水", description = "佣金流水") + @GetMapping(value = "/water") + public Result> water( + @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo, + @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) { + Page waterPage = new Page<>(pageNo, pageSize); + IPage list = appletApiPromotionService.getWater(waterPage); + return Result.OK(list); + } + +// @Operation(summary = "提现", description = "参数:提现人 提现金额") +// @PostMapping(value = "/withdraw") +// public Result withdraw(AppletWithdrawal appletWithdrawal, @RequestParam(required = false, defaultValue = LoginType.APPLET) String type) { +// AppletWithdrawal With = appletApiPromotionService.getWithdraw(appletWithdrawal, type); +// return Result.OK(With); +// } + +// @Operation(summary = "提现成功", description = "参数:提现id") +// @PostMapping(value = "/withdrawSuccess") +// public Result withdrawSuccess(@RequestParam @Parameter(description = "提现id") String id) { +// appletApiWaterService.withdrawSuccess(id); +// return Result.OK(); +// } + +// @Operation(summary = "推广统计", description = "推广统计") +// @GetMapping(value = "/statistics") +// public Result statistics() { +// return Result.OK(appletApiPromotionService.statistics()); +// } + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiPromotionService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiPromotionService.java new file mode 100644 index 0000000..caee23b --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/AppletApiPromotionService.java @@ -0,0 +1,14 @@ +package org.jeecg.modules.applet.service; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.jeecg.common.system.vo.AppletUser; +import org.jeecg.modules.demo.appletWater.entity.AppletWater; + +public interface AppletApiPromotionService { + byte[] getInviteCode(String type); + + IPage getTeam(Page page, String name); + + IPage getWater(Page waterPage); +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiPromotionServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiPromotionServiceImpl.java new file mode 100644 index 0000000..891cbc8 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/applet/service/impl/AppletApiPromotionServiceImpl.java @@ -0,0 +1,259 @@ +package org.jeecg.modules.applet.service.impl; + +import com.alibaba.fastjson.JSON; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.log4j.Log4j2; +import org.jeecg.common.system.util.AppletUserUtil; +import org.jeecg.common.system.vo.AppletUser; +import org.jeecg.common.util.oss.OssBootUtil; +import org.jeecg.modules.applet.service.AppletApiPromotionService; +import org.jeecg.modules.common.wxUtils.WxHttpUtils; +import org.jeecg.modules.demo.appletConfig.service.IAppletConfigService; +import org.jeecg.modules.demo.appletWater.entity.AppletWater; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Log4j2 +@Service +public class AppletApiPromotionServiceImpl implements AppletApiPromotionService { + + + + @Autowired + private IAppletConfigService appletConfigService; + + @Autowired + private WxHttpUtils wxHttpUtils; + + // 创建线程池用于异步处理 + private final ExecutorService asyncExecutor = Executors.newFixedThreadPool(5); + + @Override + public byte[] getInviteCode(String type){ + AppletUser user = AppletUserUtil.getCurrentAppletUser(); + + // 获取环境配置 + String vsion = appletConfigService.getContentByCode("invite_env_version"); + String trial = "release"; + if("0".equals(vsion)){ + trial= "release"; + }else if("1".equals(vsion)){ + trial= "trial"; + }else{ + trial= "develop"; + } + + // 获取必要的配置信息 + String xcxSharePage = appletConfigService.getContentByCode("xcxSharePage"); + String backgroundImageUrl = appletConfigService.getContentByCode("qr_code_bg"); + String webUrl = appletConfigService.getContentByCode("webUrl"); + int webUrlCode = 0; +// int webUrlCode = (webUrl != null && LoginType.OFFICIAL.equals(type)) ? webUrl.hashCode() : 0; + + // 获取二维码位置配置参数 + int qrCodeX = appletConfigService.getContentByCodeAsInt("qr_code_x"); + int qrCodeY = appletConfigService.getContentByCodeAsInt("qr_code_y"); + + // 优化缓存策略:使用更精确的缓存key,包含所有影响因素(包括背景图片URL和二维码位置配置) +// String cacheKey = String.format("inviteCode:final:%s:%s:%s:%s:%s:%s", +// user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY); + + // 移除图片数据的Redis缓存,避免类型转换错误 + // 直接检查OSS中是否已存在最终图片 + + // 检查OSS中是否已存在最终图片 + String finalPath = String.format("invite/final/%s_%s_%s_%s_%s_%s_%s_%s.jpg", + user.getId(), trial, xcxSharePage.hashCode(), backgroundImageUrl.hashCode(), qrCodeX, qrCodeY, type, webUrlCode); + try { + InputStream ossFile = OssBootUtil.getOssFile(finalPath, null); + if (ossFile != null) { + try { + // 使用ByteArrayOutputStream读取InputStream中的所有字节 + java.io.ByteArrayOutputStream buffer = new java.io.ByteArrayOutputStream(); + int nRead; + byte[] data = new byte[1024]; + while ((nRead = ossFile.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + buffer.flush(); + byte[] ossImageBytes = buffer.toByteArray(); + + // 直接返回OSS中的图片,不再缓存到Redis + log.info("从OSS返回邀请码图片,用户ID: {}", user.getId()); + return ossImageBytes; + } finally { + ossFile.close(); + } + } + } catch (Exception e) { + log.debug("OSS中未找到最终图片,需要重新生成,用户ID: {}", user.getId()); + } + + try { + // 直接生成小程序码,移除Redis缓存避免类型转换错误 + log.info("生成小程序码,用户ID: {}", user.getId()); +// byte[] qrCodeBytes = null; + BufferedImage qrCodeImage = null; + +// if (LoginType.APPLET.equals(type)){ + qrCodeImage = ImageIO.read(new ByteArrayInputStream(generateWxQrCode(user, xcxSharePage, trial))); +// }else { +// qrCodeImage = QRCodeUtil.getBufferedImage(webUrl + "?inviter=" + user.getId()); +// } + + // 生成最终合成图片,传递已获取的配置参数避免重复调用 + + byte[] finalImage = this.generateAndCombineImagesFromUrl2(qrCodeImage, backgroundImageUrl, qrCodeX, qrCodeY); + + // 异步上传到OSS(移除Redis缓存) + uploadToOssAsync(finalImage, finalPath); + + return finalImage; + } catch (Exception e) { + log.error("生成邀请码失败,用户ID: {}", user.getId(), e); + return null; + } + } + + @Override + public IPage getTeam(Page page, String name) { + return null; + } + + @Override + public IPage getWater(Page waterPage) { + return null; + } + + /** + * 生成微信小程序码 + */ + private byte[] generateWxQrCode(AppletUser user, String xcxSharePage, String trial) throws Exception { + // 准备微信API请求参数 + String key = "inviter=" + user.getId(); + Map param = new HashMap<>(); + param.put("path", xcxSharePage + "?" + key); + param.put("scene", user.getId()); + param.put("width", 150); + param.put("auto_color", false); + param.put("env_version", trial); + + Map line_color = new HashMap<>(); + line_color.put("r", 0); + line_color.put("g", 0); + line_color.put("b", 0); + param.put("line_color", line_color); + param.put("is_hyaline", true); + + // 获取微信小程序码 + String accessToken = wxHttpUtils.getAccessToken(); + String url = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + accessToken; + + // 请求微信API获取二维码图片数据 + RestTemplate rest = new RestTemplate(); + MultiValueMap headers = new LinkedMultiValueMap<>(); + HttpEntity requestEntity = new HttpEntity(JSON.toJSONString(param), headers); + ResponseEntity entity = rest.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]); + + return entity.getBody(); + } + + /** + * 异步上传到OSS + */ + private void uploadToOssAsync(byte[] imageBytes, String ossPath) { + // 使用线程池异步执行上传操作 + CompletableFuture.runAsync(() -> { + try { + // 上传到OSS + OssBootUtil.upload(new ByteArrayInputStream(imageBytes), ossPath); + log.info("异步上传OSS完成,路径: {}", ossPath); + } catch (Exception e) { + log.error("异步上传OSS失败,路径: {}", ossPath, e); + } + }, asyncExecutor); + } + + + public byte[] generateAndCombineImagesFromUrl2(BufferedImage qrCodeImage, String backgroundUrl, int qr_code_x, int qr_code_y) { + File file = null; + try { + // 直接从URL加载背景图像,移除Redis缓存避免类型转换问题 + URL backgroundImageUrl = new URL(backgroundUrl); + BufferedImage backgroundImage = ImageIO.read(backgroundImageUrl); + log.debug("从URL加载背景图片: {}", backgroundUrl); + + // 从字节数组加载小程序码图像 +// BufferedImage qrCodeImage = ImageIO.read(new ByteArrayInputStream(qrCodeImageByte)); + + // 使用传入的配置参数,避免重复调用配置服务 + + // 直接使用背景图片作为基础,避免创建新的图像导致黑边 + BufferedImage combinedImage = new BufferedImage( + backgroundImage.getWidth(), + backgroundImage.getHeight(), + backgroundImage.getType() // 使用背景图片的原始类型,保持透明度 + ); + + Graphics2D g2d = combinedImage.createGraphics(); + + // 设置高质量渲染 + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + // 先绘制背景图像,完全覆盖画布 + g2d.drawImage(backgroundImage, 0, 0, backgroundImage.getWidth(), backgroundImage.getHeight(), null); + + int wh = backgroundImage.getWidth() / 3; + + // 计算小程序码放置的位置(这里以中心位置为例) + int qrCodeX = (backgroundImage.getWidth() - wh) / 2; + int qrCodeY = (int) ((backgroundImage.getHeight() - wh) * 0.6); + + // 绘制小程序码图像,使用传入的位置参数 + g2d.drawImage(qrCodeImage, qrCodeX + qr_code_x, qrCodeY + qr_code_y, wh, wh, null); + + // 释放Graphics2D资源 + g2d.dispose(); + + // 将合并后的图像保存到临时文件 + file = File.createTempFile("combined_", ".png"); + ImageIO.write(combinedImage, "png", file); + + // 读取文件字节并返回 + return Files.readAllBytes(file.toPath()); + } catch (Exception e) { + log.error("生成合并图片失败", e); + throw new RuntimeException("生成合并图片失败", e); + } finally { + // 删除临时文件 + if (file != null && file.exists()) { + file.delete(); + } + } + } + + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/controller/AppletConfigController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/controller/AppletConfigController.java index 1f1748b..29f6342 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/controller/AppletConfigController.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/controller/AppletConfigController.java @@ -29,6 +29,7 @@ import org.jeecgframework.poi.excel.entity.ImportParams; import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; import org.jeecg.common.system.base.controller.JeecgController; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartHttpServletRequest; @@ -90,6 +91,7 @@ public class AppletConfigController extends JeecgController add(@RequestBody AppletConfig appletConfig) { appletConfigService.save(appletConfig); @@ -106,6 +108,7 @@ public class AppletConfigController extends JeecgController edit(@RequestBody AppletConfig appletConfig) { appletConfigService.updateById(appletConfig); return Result.OK("编辑成功!"); diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/IAppletConfigService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/IAppletConfigService.java index 7174130..8be653c 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/IAppletConfigService.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/IAppletConfigService.java @@ -11,4 +11,11 @@ import com.baomidou.mybatisplus.extension.service.IService; */ public interface IAppletConfigService extends IService { + + String getContentByCode(String code); + + + Integer getContentByCodeAsInt(String code); + + } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/impl/AppletConfigServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/impl/AppletConfigServiceImpl.java index b0359d2..75ca104 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/impl/AppletConfigServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletConfig/service/impl/AppletConfigServiceImpl.java @@ -3,6 +3,7 @@ package org.jeecg.modules.demo.appletConfig.service.impl; import org.jeecg.modules.demo.appletConfig.entity.AppletConfig; import org.jeecg.modules.demo.appletConfig.mapper.AppletConfigMapper; import org.jeecg.modules.demo.appletConfig.service.IAppletConfigService; +import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @@ -16,4 +17,23 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; @Service public class AppletConfigServiceImpl extends ServiceImpl implements IAppletConfigService { + + @Override + @Cacheable(value = "appletConfig", key = "#code") + public String getContentByCode(String code) { + AppletConfig config = lambdaQuery() + .eq(AppletConfig::getCode, code) + .one(); + return config != null ? config.getContent() : null; + } + + + @Override + @Cacheable(value = "appletConfig", key = "'int_' + #code") + public Integer getContentByCodeAsInt(String code) { + String i = getContentByCode(code); + return i != null ? Integer.parseInt(i) : null; + } + + } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/IAppletProductSpecService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/IAppletProductSpecService.java index b78cd4b..895c655 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/IAppletProductSpecService.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/IAppletProductSpecService.java @@ -29,4 +29,21 @@ public interface IAppletProductSpecService extends IService { * @return boolean true-已存在,false-不存在 */ public boolean checkSpecNameExists(String productId, String specName, String excludeId); + + /** + * 根据产品ID获取所有已存在的规格名称 + * + * @param productId 产品ID + * @return List 已存在的规格名称列表 + */ + List getExistingSpecNames(String productId); + + /** + * 检查是否有上架的规格实体关联了该规格类型或规格项 + * + * @param productId 产品ID + * @param specTitle 规格类型或规格项的标题 + * @return boolean true-存在上架关联,false-不存在 + */ + boolean hasOnShelfAssociatedSpecs(String productId, String specTitle); } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/impl/AppletProductSpecServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/impl/AppletProductSpecServiceImpl.java index b60e3c7..1626f2a 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/impl/AppletProductSpecServiceImpl.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletProduct/service/impl/AppletProductSpecServiceImpl.java @@ -9,6 +9,8 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.springframework.beans.factory.annotation.Autowired; +import java.util.stream.Collectors; + /** * @Description: 产品规格表 * @Author: jeecg-boot @@ -36,4 +38,28 @@ public class AppletProductSpecServiceImpl extends ServiceImpl 0; } + + @Override + public List getExistingSpecNames(String productId) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("product_id", productId); + queryWrapper.select("spec_name"); + return appletProductSpecMapper.selectObjs(queryWrapper).stream() + .map(Object::toString) + .collect(Collectors.toList()); + } + + @Override + public boolean hasOnShelfAssociatedSpecs(String productId, String specTitle) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("product_id", productId); + queryWrapper.and(wrapper -> wrapper.like("spec_name", specTitle + ":%") + .or().like("spec_name", "%:" + specTitle) + .or().eq("spec_name", specTitle) + .or().like("spec_name", specTitle + ",%") + .or().like("spec_name", "%," + specTitle) + .or().like("spec_name", "%," + specTitle + ",%")); + queryWrapper.eq("putaway", "Y"); // 检查是否上架 + return appletProductSpecMapper.selectCount(queryWrapper) > 0; + } } diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/controller/AppletWaterController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/controller/AppletWaterController.java new file mode 100644 index 0000000..277affe --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/controller/AppletWaterController.java @@ -0,0 +1,182 @@ +package org.jeecg.modules.demo.appletWater.controller; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.jeecg.common.api.vo.Result; +import org.jeecg.common.system.query.QueryGenerator; +import org.jeecg.common.system.query.QueryRuleEnum; +import org.jeecg.common.util.oConvertUtils; +import org.jeecg.modules.demo.appletWater.entity.AppletWater; +import org.jeecg.modules.demo.appletWater.service.IAppletWaterService; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.extern.slf4j.Slf4j; + +import org.jeecgframework.poi.excel.ExcelImportUtil; +import org.jeecgframework.poi.excel.def.NormalExcelConstants; +import org.jeecgframework.poi.excel.entity.ExportParams; +import org.jeecgframework.poi.excel.entity.ImportParams; +import org.jeecgframework.poi.excel.view.JeecgEntityExcelView; +import org.jeecg.common.system.base.controller.JeecgController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; +import org.springframework.web.servlet.ModelAndView; +import com.alibaba.fastjson.JSON; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import org.jeecg.common.aspect.annotation.AutoLog; +import org.apache.shiro.authz.annotation.RequiresPermissions; + /** + * @Description: 小程序流水 + * @Author: jeecg-boot + * @Date: 2025-11-04 + * @Version: V1.0 + */ +@Tag(name="小程序流水") +@RestController +@RequestMapping("/appletWater/appletWater") +@Slf4j +public class AppletWaterController extends JeecgController { + @Autowired + private IAppletWaterService appletWaterService; + + /** + * 分页列表查询 + * + * @param appletWater + * @param pageNo + * @param pageSize + * @param req + * @return + */ + //@AutoLog(value = "小程序流水-分页列表查询") + @Operation(summary="小程序流水-分页列表查询") + @GetMapping(value = "/list") + public Result> queryPageList(AppletWater appletWater, + @RequestParam(name="pageNo", defaultValue="1") Integer pageNo, + @RequestParam(name="pageSize", defaultValue="10") Integer pageSize, + HttpServletRequest req) { + + + QueryWrapper queryWrapper = QueryGenerator.initQueryWrapper(appletWater, req.getParameterMap()); + Page page = new Page(pageNo, pageSize); + IPage pageList = appletWaterService.page(page, queryWrapper); + return Result.OK(pageList); + } + + /** + * 添加 + * + * @param appletWater + * @return + */ + @AutoLog(value = "小程序流水-添加") + @Operation(summary="小程序流水-添加") + @RequiresPermissions("appletWater:applet_water:add") + @PostMapping(value = "/add") + public Result add(@RequestBody AppletWater appletWater) { + appletWaterService.save(appletWater); + + return Result.OK("添加成功!"); + } + + /** + * 编辑 + * + * @param appletWater + * @return + */ + @AutoLog(value = "小程序流水-编辑") + @Operation(summary="小程序流水-编辑") + @RequiresPermissions("appletWater:applet_water:edit") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) + public Result edit(@RequestBody AppletWater appletWater) { + appletWaterService.updateById(appletWater); + return Result.OK("编辑成功!"); + } + + /** + * 通过id删除 + * + * @param id + * @return + */ + @AutoLog(value = "小程序流水-通过id删除") + @Operation(summary="小程序流水-通过id删除") + @RequiresPermissions("appletWater:applet_water:delete") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name="id",required=true) String id) { + appletWaterService.removeById(id); + return Result.OK("删除成功!"); + } + + /** + * 批量删除 + * + * @param ids + * @return + */ + @AutoLog(value = "小程序流水-批量删除") + @Operation(summary="小程序流水-批量删除") + @RequiresPermissions("appletWater:applet_water:deleteBatch") + @DeleteMapping(value = "/deleteBatch") + public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) { + this.appletWaterService.removeByIds(Arrays.asList(ids.split(","))); + return Result.OK("批量删除成功!"); + } + + /** + * 通过id查询 + * + * @param id + * @return + */ + //@AutoLog(value = "小程序流水-通过id查询") + @Operation(summary="小程序流水-通过id查询") + @GetMapping(value = "/queryById") + public Result queryById(@RequestParam(name="id",required=true) String id) { + AppletWater appletWater = appletWaterService.getById(id); + if(appletWater==null) { + return Result.error("未找到对应数据"); + } + return Result.OK(appletWater); + } + + /** + * 导出excel + * + * @param request + * @param appletWater + */ + @RequiresPermissions("appletWater:applet_water:exportXls") + @RequestMapping(value = "/exportXls") + public ModelAndView exportXls(HttpServletRequest request, AppletWater appletWater) { + return super.exportXls(request, appletWater, AppletWater.class, "小程序流水"); + } + + /** + * 通过excel导入数据 + * + * @param request + * @param response + * @return + */ + @RequiresPermissions("appletWater:applet_water:importExcel") + @RequestMapping(value = "/importExcel", method = RequestMethod.POST) + public Result importExcel(HttpServletRequest request, HttpServletResponse response) { + return super.importExcel(request, response, AppletWater.class); + } + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/entity/AppletWater.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/entity/AppletWater.java new file mode 100644 index 0000000..8b7a03d --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/entity/AppletWater.java @@ -0,0 +1,83 @@ +package org.jeecg.modules.demo.appletWater.entity; + +import java.io.Serializable; +import java.io.UnsupportedEncodingException; +import java.util.Date; +import java.math.BigDecimal; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.annotation.TableLogic; +import org.jeecg.common.constant.ProvinceCityArea; +import org.jeecg.common.util.SpringContextUtils; +import lombok.Data; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.springframework.format.annotation.DateTimeFormat; +import org.jeecgframework.poi.excel.annotation.Excel; +import org.jeecg.common.aspect.annotation.Dict; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * @Description: 小程序流水 + * @Author: jeecg-boot + * @Date: 2025-11-04 + * @Version: V1.0 + */ +@Data +@TableName("applet_water") +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Schema(description="小程序流水") +public class AppletWater implements Serializable { + private static final long serialVersionUID = 1L; + + /**主键*/ + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "主键") + private java.lang.String id; + /**创建人*/ + @Schema(description = "创建人") + private java.lang.String createBy; + /**创建日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(description = "创建日期") + private java.util.Date createTime; + /**更新人*/ + @Schema(description = "更新人") + private java.lang.String updateBy; + /**更新日期*/ + @JsonFormat(timezone = "GMT+8",pattern = "yyyy-MM-dd HH:mm:ss") + @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") + @Schema(description = "更新日期") + private java.util.Date updateTime; + /**所属部门*/ + @Schema(description = "所属部门") + private java.lang.String sysOrgCode; + /**用户*/ + @Excel(name = "用户", width = 15) + @Schema(description = "用户") + private java.lang.String userId; + /**订单*/ + @Excel(name = "订单", width = 15) + @Schema(description = "订单") + private java.lang.String orderId; + /**金额*/ + @Excel(name = "金额", width = 15) + @Schema(description = "金额") + private java.math.BigDecimal price; + /**原钱包金额*/ + @Excel(name = "原钱包金额", width = 15) + @Schema(description = "原钱包金额") + private java.math.BigDecimal oldMoney; + /**来源用户*/ + @Excel(name = "来源用户", width = 15) + @Schema(description = "来源用户") + private java.lang.String fromId; + /**佣金*/ + @Excel(name = "佣金", width = 15,replace = {"是_Y","否_N"} ) + @Schema(description = "佣金") + private java.lang.String isCommission; +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/mapper/AppletWaterMapper.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/mapper/AppletWaterMapper.java new file mode 100644 index 0000000..be12b23 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/mapper/AppletWaterMapper.java @@ -0,0 +1,17 @@ +package org.jeecg.modules.demo.appletWater.mapper; + +import java.util.List; + +import org.apache.ibatis.annotations.Param; +import org.jeecg.modules.demo.appletWater.entity.AppletWater; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; + +/** + * @Description: 小程序流水 + * @Author: jeecg-boot + * @Date: 2025-11-04 + * @Version: V1.0 + */ +public interface AppletWaterMapper extends BaseMapper { + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/mapper/xml/AppletWaterMapper.xml b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/mapper/xml/AppletWaterMapper.xml new file mode 100644 index 0000000..638484e --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/mapper/xml/AppletWaterMapper.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/service/IAppletWaterService.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/service/IAppletWaterService.java new file mode 100644 index 0000000..c46539d --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/service/IAppletWaterService.java @@ -0,0 +1,14 @@ +package org.jeecg.modules.demo.appletWater.service; + +import org.jeecg.modules.demo.appletWater.entity.AppletWater; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + * @Description: 小程序流水 + * @Author: jeecg-boot + * @Date: 2025-11-04 + * @Version: V1.0 + */ +public interface IAppletWaterService extends IService { + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/service/impl/AppletWaterServiceImpl.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/service/impl/AppletWaterServiceImpl.java new file mode 100644 index 0000000..779d4f0 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/service/impl/AppletWaterServiceImpl.java @@ -0,0 +1,19 @@ +package org.jeecg.modules.demo.appletWater.service.impl; + +import org.jeecg.modules.demo.appletWater.entity.AppletWater; +import org.jeecg.modules.demo.appletWater.mapper.AppletWaterMapper; +import org.jeecg.modules.demo.appletWater.service.IAppletWaterService; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +/** + * @Description: 小程序流水 + * @Author: jeecg-boot + * @Date: 2025-11-04 + * @Version: V1.0 + */ +@Service +public class AppletWaterServiceImpl extends ServiceImpl implements IAppletWaterService { + +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp/AppletWaterForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp/AppletWaterForm.vue new file mode 100644 index 0000000..38a36eb --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp/AppletWaterForm.vue @@ -0,0 +1,113 @@ + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp/AppletWaterList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp/AppletWaterList.vue new file mode 100644 index 0000000..9ac34c4 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp/AppletWaterList.vue @@ -0,0 +1,44 @@ + + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterData.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterData.ts new file mode 100644 index 0000000..b750c40 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterData.ts @@ -0,0 +1,37 @@ +import { render } from '@/common/renderUtils'; +//列表数据 +export const columns = [ + { + title: '用户', + align:"center", + dataIndex: 'userId' + }, + { + title: '订单', + align:"center", + dataIndex: 'orderId' + }, + { + title: '金额', + align:"center", + dataIndex: 'price' + }, + { + title: '原钱包金额', + align:"center", + dataIndex: 'oldMoney' + }, + { + title: '来源用户', + align:"center", + dataIndex: 'fromId' + }, + { + title: '佣金', + align:"center", + dataIndex: 'isCommission', + customRender:({text}) => { + return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}]) + }, + }, +]; \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterForm.vue new file mode 100644 index 0000000..f609001 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterForm.vue @@ -0,0 +1,281 @@ + +{ +layout: 'default', +style: { +navigationStyle: 'custom', +navigationBarTitleText: '小程序流水', +}, +} + + + + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterList.vue new file mode 100644 index 0000000..715304a --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/uniapp3/AppletWaterList.vue @@ -0,0 +1,148 @@ + +{ +layout: 'default', +style: { +navigationBarTitleText: '小程序流水', +navigationStyle: 'custom', +}, +} + + + + + + diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWater.api.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWater.api.ts new file mode 100644 index 0000000..3b1cf2e --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWater.api.ts @@ -0,0 +1,64 @@ +import {defHttp} from '/@/utils/http/axios'; +import { useMessage } from "/@/hooks/web/useMessage"; + +const { createConfirm } = useMessage(); + +enum Api { + list = '/appletWater/appletWater/list', + save='/appletWater/appletWater/add', + edit='/appletWater/appletWater/edit', + deleteOne = '/appletWater/appletWater/delete', + deleteBatch = '/appletWater/appletWater/deleteBatch', + importExcel = '/appletWater/appletWater/importExcel', + exportXls = '/appletWater/appletWater/exportXls', +} +/** + * 导出api + * @param params + */ +export const getExportUrl = Api.exportXls; +/** + * 导入api + */ +export const getImportUrl = Api.importExcel; +/** + * 列表接口 + * @param params + */ +export const list = (params) => + defHttp.get({url: Api.list, params}); + +/** + * 删除单个 + */ +export const deleteOne = (params,handleSuccess) => { + return defHttp.delete({url: Api.deleteOne, params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); +} +/** + * 批量删除 + * @param params + */ +export const batchDelete = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({url: Api.deleteBatch, data: params}, {joinParamsToUrl: true}).then(() => { + handleSuccess(); + }); + } + }); +} +/** + * 保存或者更新 + * @param params + */ +export const saveOrUpdate = (params, isUpdate) => { + let url = isUpdate ? Api.edit : Api.save; + return defHttp.post({url: url, params}); +} diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWater.data.ts b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWater.data.ts new file mode 100644 index 0000000..3b17877 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWater.data.ts @@ -0,0 +1,105 @@ +import {BasicColumn} from '/@/components/Table'; +import {FormSchema} from '/@/components/Table'; +import { rules} from '/@/utils/helper/validator'; +import { render } from '/@/utils/common/renderUtils'; +import { getWeekMonthQuarterYear } from '/@/utils'; +//列表数据 +export const columns: BasicColumn[] = [ + { + title: '用户', + align:"center", + dataIndex: 'userId' + }, + { + title: '订单', + align:"center", + dataIndex: 'orderId' + }, + { + title: '金额', + align:"center", + dataIndex: 'price' + }, + { + title: '原钱包金额', + align:"center", + dataIndex: 'oldMoney' + }, + { + title: '来源用户', + align:"center", + dataIndex: 'fromId' + }, + { + title: '佣金', + align:"center", + dataIndex: 'isCommission', + customRender:({text}) => { + return render.renderSwitch(text, [{text:'是',value:'Y'},{text:'否',value:'N'}]) + }, + }, +]; +//查询数据 +export const searchFormSchema: FormSchema[] = [ +]; +//表单数据 +export const formSchema: FormSchema[] = [ + { + label: '用户', + field: 'userId', + component: 'Input', + }, + { + label: '订单', + field: 'orderId', + component: 'Input', + }, + { + label: '金额', + field: 'price', + component: 'InputNumber', + }, + { + label: '原钱包金额', + field: 'oldMoney', + component: 'InputNumber', + }, + { + label: '来源用户', + field: 'fromId', + component: 'Input', + }, + { + label: '佣金', + field: 'isCommission', + component: 'JSwitch', + componentProps:{ + }, + }, + // TODO 主键隐藏字段,目前写死为ID + { + label: '', + field: 'id', + component: 'Input', + show: false + }, +]; + +// 高级查询数据 +export const superQuerySchema = { + userId: {title: '用户',order: 0,view: 'text', type: 'string',}, + orderId: {title: '订单',order: 1,view: 'text', type: 'string',}, + price: {title: '金额',order: 2,view: 'number', type: 'number',}, + oldMoney: {title: '原钱包金额',order: 3,view: 'number', type: 'number',}, + fromId: {title: '来源用户',order: 4,view: 'text', type: 'string',}, + isCommission: {title: '佣金',order: 5,view: 'switch', type: 'string',}, +}; + +/** +* 流程表单调用这个方法获取formSchema +* @param param +*/ +export function getBpmFormSchema(_formData): FormSchema[]{ + // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema + return formSchema; +} \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWaterList.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWaterList.vue new file mode 100644 index 0000000..f4cb2ce --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/AppletWaterList.vue @@ -0,0 +1,206 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/V20251104_1__menu_insert_AppletWater.sql b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/V20251104_1__menu_insert_AppletWater.sql new file mode 100644 index 0000000..8075c6e --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/V20251104_1__menu_insert_AppletWater.sql @@ -0,0 +1,26 @@ +-- 注意:该页面对应的前台目录为views/appletWater文件夹下 +-- 如果你想更改到其他目录,请修改sql中component字段对应的值 + + +INSERT INTO sys_permission(id, parent_id, name, url, component, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_route, is_leaf, keep_alive, hidden, hide_tab, description, status, del_flag, rule_flag, create_by, create_time, update_by, update_time, internal_or_external) +VALUES ('2025110405506810160', NULL, '小程序流水', '/appletWater/appletWaterList', 'appletWater/AppletWaterList', NULL, NULL, 0, NULL, '1', 0.00, 0, NULL, 1, 0, 0, 0, 0, NULL, '1', 0, 0, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0); + +-- 权限控制sql +-- 新增 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025110405506810161', '2025110405506810160', '添加小程序流水', NULL, NULL, 0, NULL, NULL, 2, 'appletWater:applet_water:add', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0, 0, '1', 0); +-- 编辑 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025110405506810162', '2025110405506810160', '编辑小程序流水', NULL, NULL, 0, NULL, NULL, 2, 'appletWater:applet_water:edit', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0, 0, '1', 0); +-- 删除 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025110405506810163', '2025110405506810160', '删除小程序流水', NULL, NULL, 0, NULL, NULL, 2, 'appletWater:applet_water:delete', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0, 0, '1', 0); +-- 批量删除 +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025110405506810164', '2025110405506810160', '批量删除小程序流水', NULL, NULL, 0, NULL, NULL, 2, 'appletWater:applet_water:deleteBatch', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0, 0, '1', 0); +-- 导出excel +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025110405506810165', '2025110405506810160', '导出excel_小程序流水', NULL, NULL, 0, NULL, NULL, 2, 'appletWater:applet_water:exportXls', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0, 0, '1', 0); +-- 导入excel +INSERT INTO sys_permission(id, parent_id, name, url, component, is_route, component_name, redirect, menu_type, perms, perms_type, sort_no, always_show, icon, is_leaf, keep_alive, hidden, hide_tab, description, create_by, create_time, update_by, update_time, del_flag, rule_flag, status, internal_or_external) +VALUES ('2025110405506810166', '2025110405506810160', '导入excel_小程序流水', NULL, NULL, 0, NULL, NULL, 2, 'appletWater:applet_water:importExcel', '1', NULL, 0, NULL, 1, 0, 0, 0, NULL, 'admin', '2025-11-04 17:50:16', NULL, NULL, 0, 0, '1', 0); \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/components/AppletWaterForm.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/components/AppletWaterForm.vue new file mode 100644 index 0000000..eeb5727 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/components/AppletWaterForm.vue @@ -0,0 +1,70 @@ + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/components/AppletWaterModal.vue b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/components/AppletWaterModal.vue new file mode 100644 index 0000000..fdddb25 --- /dev/null +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletWater/vue3/components/AppletWaterModal.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletproductspectype/controller/AppletproductspectypeController.java b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletproductspectype/controller/AppletproductspectypeController.java index d445f3b..12a71aa 100644 --- a/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletproductspectype/controller/AppletproductspectypeController.java +++ b/jeecg-boot/jeecg-boot-module/jeecgboot-boot-applet/src/main/java/org/jeecg/modules/demo/appletproductspectype/controller/AppletproductspectypeController.java @@ -9,6 +9,8 @@ import java.util.stream.Collectors; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; +import java.util.HashSet; +import java.util.Set; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jeecg.common.api.vo.Result; @@ -90,13 +92,36 @@ public class AppletproductspectypeController extends JeecgController add(@RequestBody Appletproductspectype appletproductspectype) { - appletproductspectypeService.save(appletproductspectype); +// @RequiresPermissions("appletproductspectype:appletproductspectype:add") + @PostMapping(value = "/add") + public Result add(@RequestBody Appletproductspectype appletproductspectype) { + // 参数校验:产品ID、类型必填;并约束二级分类的父子关系 + if (oConvertUtils.isEmpty(appletproductspectype.getProductId()) || oConvertUtils.isEmpty(appletproductspectype.getType())) { + return Result.error("缺少产品ID或类型"); + } - return Result.OK("添加成功!"); - } + // type=0 -> 规格类型,父ID强制为"0" + if ("0".equals(appletproductspectype.getType())) { + appletproductspectype.setPid("0"); + } + + // type=1 -> 规格项,必须有有效的父规格类型(同一产品) + if ("1".equals(appletproductspectype.getType())) { + String pid = appletproductspectype.getPid(); + if (oConvertUtils.isEmpty(pid)) { + return Result.error("规格项必须关联父规格类型"); + } + QueryWrapper parentQuery = new QueryWrapper<>(); + parentQuery.eq("id", pid).eq("product_id", appletproductspectype.getProductId()).eq("type", "0"); + Appletproductspectype parentType = appletproductspectypeService.getOne(parentQuery); + if (parentType == null) { + return Result.error("父规格类型不存在或不匹配该产品"); + } + } + + appletproductspectypeService.save(appletproductspectype); + return Result.OK("添加成功!"); + } /** * 编辑 @@ -106,12 +131,48 @@ public class AppletproductspectypeController extends JeecgController edit(@RequestBody Appletproductspectype appletproductspectype) { - appletproductspectypeService.updateById(appletproductspectype); - return Result.OK("编辑成功!"); - } +// @RequiresPermissions("appletproductspectype:appletproductspectype:edit") + @RequestMapping(value = "/edit", method = {RequestMethod.PUT,RequestMethod.POST}) + public Result edit(@RequestBody Appletproductspectype appletproductspectype) { + if (oConvertUtils.isEmpty(appletproductspectype.getId())) { + return Result.error("缺少ID"); + } + if (oConvertUtils.isEmpty(appletproductspectype.getProductId()) || oConvertUtils.isEmpty(appletproductspectype.getType())) { + return Result.error("缺少产品ID或类型"); + } + + // 获取旧的规格类型或规格项信息 + Appletproductspectype oldEntity = appletproductspectypeService.getById(appletproductspectype.getId()); + if (oldEntity == null) { + return Result.error("未找到对应数据"); + } + + // 如果名称发生变化,需要检查是否关联了上架的规格实体 + if (!oldEntity.getTitle().equals(appletproductspectype.getTitle())) { + if (appletProductSpecService.hasOnShelfAssociatedSpecs(appletproductspectype.getProductId(), oldEntity.getTitle())) { + return Result.error("该规格类型或规格项已关联上架的规格实体,无法修改名称!"); + } + } + + // 约束父子关系 + if ("0".equals(appletproductspectype.getType())) { + appletproductspectype.setPid("0"); + } else if ("1".equals(appletproductspectype.getType())) { + String pid = appletproductspectype.getPid(); + if (oConvertUtils.isEmpty(pid)) { + return Result.error("规格项必须关联父规格类型"); + } + QueryWrapper parentQuery = new QueryWrapper<>(); + parentQuery.eq("id", pid).eq("product_id", appletproductspectype.getProductId()).eq("type", "0"); + Appletproductspectype parentType = appletproductspectypeService.getOne(parentQuery); + if (parentType == null) { + return Result.error("父规格类型不存在或不匹配该产品"); + } + } + + appletproductspectypeService.updateById(appletproductspectype); + return Result.OK("编辑成功!"); + } /** * 通过id删除 @@ -121,12 +182,38 @@ public class AppletproductspectypeController extends JeecgController delete(@RequestParam(name="id",required=true) String id) { - appletproductspectypeService.removeById(id); - return Result.OK("删除成功!"); - } +// @RequiresPermissions("appletproductspectype:appletproductspectype:delete") + @DeleteMapping(value = "/delete") + public Result delete(@RequestParam(name="id",required=true) String id) { + Appletproductspectype entity = appletproductspectypeService.getById(id); + if (entity == null) { + return Result.error("未找到对应数据"); + } + + // 获取产品ID + String productId = entity.getProductId(); + + // 检查是否有上架的规格实体关联 + if (appletProductSpecService.hasOnShelfAssociatedSpecs(productId, entity.getTitle())) { + return Result.error("该规格类型或规格项已关联上架的规格实体,无法删除!"); + } + + if ("0".equals(entity.getType())) { + // 如果是规格类型,需要检查其下的所有规格项 + QueryWrapper childQuery = new QueryWrapper<>(); + childQuery.eq("pid", id); + List childItems = appletproductspectypeService.list(childQuery); + for (Appletproductspectype item : childItems) { + if (appletProductSpecService.hasOnShelfAssociatedSpecs(productId, item.getTitle())) { + return Result.error("该规格类型下的规格项 \"" + item.getTitle() + "\" 已关联上架的规格实体,无法删除!"); + } + } + // 删除子规格项 + appletproductspectypeService.remove(childQuery); + } + appletproductspectypeService.removeById(id); + return Result.OK("删除成功!"); + } /** * 批量删除 @@ -136,12 +223,22 @@ public class AppletproductspectypeController extends JeecgController deleteBatch(@RequestParam(name="ids",required=true) String ids) { - this.appletproductspectypeService.removeByIds(Arrays.asList(ids.split(","))); - return Result.OK("批量删除成功!"); - } +// @RequiresPermissions("appletproductspectype:appletproductspectype:deleteBatch") + @DeleteMapping(value = "/deleteBatch") + public Result deleteBatch(@RequestParam(name="ids",required=true) String ids) { + List idList = Arrays.asList(ids.split(",")); + // 批量级联删除:先处理各自子项,再删除本体 + for (String id : idList) { + Appletproductspectype entity = appletproductspectypeService.getById(id); + if (entity != null && "0".equals(entity.getType())) { + QueryWrapper childQuery = new QueryWrapper<>(); + childQuery.eq("pid", id); + appletproductspectypeService.remove(childQuery); + } + } + this.appletproductspectypeService.removeByIds(idList); + return Result.OK("批量删除成功!"); + } /** * 通过id查询 @@ -203,17 +300,29 @@ public class AppletproductspectypeController extends JeecgController generateSpecs(@RequestParam(name="productId",required=true) String productId) { - try { - // 查询该产品的所有规格类型和规格项 - QueryWrapper queryWrapper = new QueryWrapper<>(); - queryWrapper.eq("product_id", productId); - queryWrapper.orderByAsc("type", "create_time"); - + @PostMapping(value = "/generateSpecs") + public Result generateSpecs(String productId) { + try { + // 查询该产品的所有规格类型和规格项 + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("product_id", productId); + queryWrapper.orderByAsc("type", "create_time"); + List allSpecs = appletproductspectypeService.list(queryWrapper); - - // 分离规格类型和规格项 + + // 获取所有已存在的规格名称,用于后续快速判断 + List existingSpecNamesList = appletProductSpecService.getExistingSpecNames(productId); + Set existingSpecNames = new HashSet<>(existingSpecNamesList); + + // 获取所有已存在的规格实体,用于后续清理 + QueryWrapper allExistingSpecsQuery = new QueryWrapper<>(); + allExistingSpecsQuery.eq("product_id", productId); + List allExistingSpecs = appletProductSpecService.list(allExistingSpecsQuery); + + // 用于存储新生成的规格名称,以便与现有规格进行比较 + Set newGeneratedSpecNames = new HashSet<>(); + + // 分离规格类型和规格项 List specTypes = allSpecs.stream() .filter(spec -> "0".equals(spec.getType())) .collect(Collectors.toList()); @@ -240,9 +349,10 @@ public class AppletproductspectypeController extends JeecgController addSpec(AppletProductSpec spec) { + if (oConvertUtils.isEmpty(spec.getProductId()) || oConvertUtils.isEmpty(spec.getSpecName())) { + return Result.error("缺少产品ID或规格名称"); + } + // 规格名称唯一性校验(同一产品下) + if (appletProductSpecService.checkSpecNameExists(spec.getProductId(), spec.getSpecName(), null)) { + return Result.error("规格名称已存在"); + } + // 默认值兜底 + if (spec.getPutaway() == null) { + spec.setPutaway("N"); + } + if (spec.getInventory() == null) { + spec.setInventory(0); + } + if (spec.getPrice() == null) { + spec.setPrice(new java.math.BigDecimal("0.00")); + } + if (spec.getSortOrder() == null) { + spec.setSortOrder(0); + } + appletProductSpecService.save(spec); + return Result.OK("添加成功!"); + } + + /** + * 编辑规格实体 + * + * 不使用@RequestBody,直接接收表单/查询参数 + * @param spec 规格实体 + * @return 操作结果 + */ + @Operation(summary="编辑规格实体") + @RequestMapping(value = "/spec/edit", method = {RequestMethod.PUT, RequestMethod.POST}) + public Result editSpec(AppletProductSpec spec) { + if (oConvertUtils.isEmpty(spec.getId())) { + return Result.error("缺少ID"); + } + if (oConvertUtils.isEmpty(spec.getProductId()) || oConvertUtils.isEmpty(spec.getSpecName())) { + return Result.error("缺少产品ID或规格名称"); + } + // 规格名称唯一性校验(排除自身ID) + if (appletProductSpecService.checkSpecNameExists(spec.getProductId(), spec.getSpecName(), spec.getId())) { + return Result.error("规格名称已存在"); + } + appletProductSpecService.updateById(spec); + return Result.OK("编辑成功!"); + } + + /** + * 删除规格实体 + * @param id 规格实体ID + * @return 操作结果 + */ + @Operation(summary="删除规格实体") + @DeleteMapping(value = "/spec/delete") + public Result deleteSpec(@RequestParam(name = "id", required = true) String id) { + appletProductSpecService.removeById(id); + return Result.OK("删除成功!"); + } + + /** + * 批量删除规格实体 + * @param ids 逗号分隔的ID集合 + * @return 操作结果 + */ + @Operation(summary="批量删除规格实体") + @DeleteMapping(value = "/spec/deleteBatch") + public Result deleteBatchSpec(@RequestParam(name = "ids", required = true) String ids) { + List idList = Arrays.asList(ids.split(",")); + appletProductSpecService.removeBatchByIds(idList); + return Result.OK("批量删除成功!"); + } /** * 生成规格组合 diff --git a/jeecgboot-vue3/src/views/applet/product/AppletProduct.api.ts b/jeecgboot-vue3/src/views/applet/product/AppletProduct.api.ts index 5c9a505..aae295e 100644 --- a/jeecgboot-vue3/src/views/applet/product/AppletProduct.api.ts +++ b/jeecgboot-vue3/src/views/applet/product/AppletProduct.api.ts @@ -5,8 +5,8 @@ const { createConfirm } = useMessage(); enum Api { list = '/appletProduct/appletProduct/list', - save='/appletProduct/appletProduct/add', - edit='/appletProduct/appletProduct/edit', + save = '/appletProduct/appletProduct/add', + edit = '/appletProduct/appletProduct/edit', deleteOne = '/appletProduct/appletProduct/delete', deleteBatch = '/appletProduct/appletProduct/deleteBatch', importExcel = '/appletProduct/appletProduct/importExcel', diff --git a/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.api.ts b/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.api.ts index 0f62f21..1e1eb43 100644 --- a/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.api.ts +++ b/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.api.ts @@ -16,6 +16,11 @@ enum Api { queryByProductId = '/appletproductspectype/appletproductspectype/queryByProductId', // 生成规格实体 generateSpecs = '/appletproductspectype/appletproductspectype/generateSpecs', + // 规格实体管理 + addSpec = '/appletproductspectype/appletproductspectype/spec/add', + editSpec = '/appletproductspectype/appletproductspectype/spec/edit', + deleteSpec = '/appletproductspectype/appletproductspectype/spec/delete', + deleteSpecBatch = '/appletproductspectype/appletproductspectype/spec/deleteBatch', } /** @@ -72,6 +77,14 @@ export const saveOrUpdate = (params, isUpdate) => { return defHttp.post({url: url, params}, {isTransformResponse: false}); } +/** + * 保存或更新规格实体(避免@RequestBody,参数拼接到URL) + */ +export const saveOrUpdateSpecEntity = (params, isUpdate) => { + const url = isUpdate ? Api.editSpec : Api.addSpec; + return defHttp.post({ url, params }, { joinParamsToUrl: true }); +} + /** * 根据ID查询 */ @@ -91,4 +104,31 @@ export const queryByProductId = (params) => { */ export const generateSpecs = (params) => { return defHttp.post({url: Api.generateSpecs, params}); +} + +/** + * 删除规格实体 + */ +export const deleteSpecEntity = (params, handleSuccess) => { + return defHttp.delete({ url: Api.deleteSpec, params }, { joinParamsToUrl: true }).then(() => { + handleSuccess && handleSuccess(); + }); +} + +/** + * 批量删除规格实体 + */ +export const batchDeleteSpecEntity = (params, handleSuccess) => { + createConfirm({ + iconType: 'warning', + title: '确认删除', + content: '是否删除选中数据', + okText: '确认', + cancelText: '取消', + onOk: () => { + return defHttp.delete({ url: Api.deleteSpecBatch, params }, { joinParamsToUrl: true }).then(() => { + handleSuccess && handleSuccess(); + }); + } + }); } \ No newline at end of file diff --git a/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.data.ts b/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.data.ts index 30366ea..6a2278c 100644 --- a/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.data.ts +++ b/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.data.ts @@ -166,10 +166,16 @@ export const specEntityFormSchema: FormSchema[] = [ { label: '规格名称', field: 'specName', - component: 'Input', + component: 'Select', required: true, componentProps: { - disabled: true, + options: [], + showSearch: true, + placeholder: '请选择规格名称', + allowClear: false, + filterOption: (input: string, option: any) => { + return (option?.label ?? '').toLowerCase().includes((input || '').toLowerCase()); + }, }, }, { diff --git a/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.vue b/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.vue index 1f9d8f3..78926d9 100644 --- a/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.vue +++ b/jeecgboot-vue3/src/views/applet/product/AppletProductSpecConfig.vue @@ -40,7 +40,7 @@ -
+
规格项 @@ -48,6 +48,14 @@ 添加规格项
+ +
- -
+ +
@@ -84,9 +92,10 @@