添加cdn上传,修订商品管理

This commit is contained in:
DB 2023-10-31 18:35:03 +08:00
parent 08693cce12
commit 76f3d1eeef
17 changed files with 373 additions and 12 deletions

2
.gitignore vendored
View File

@ -19,7 +19,7 @@ target/
*.iws
*.iml
*.ipr
*.log
### NetBeans ###
/nbproject/private/
/nbbuild/

View File

@ -97,6 +97,16 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!-- 腾讯云-->
<dependency>
<groupId>com.qcloud</groupId>
<artifactId>cos_api</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,40 @@
package com.cpop.core.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* @author DB
* @createTime 2023/10/31 15:46
* @description 腾讯yun配置
*/
@Data
@ConfigurationProperties(prefix = "tencent.cos")
public class TencentCosProperties {
/**
* id
*/
private String secretId;
/**
* 密钥
*/
private String secretKey;
/**
* 桶名
*/
private String bucketName;
/**
* 加速路径
*/
private String cdnUrl;
/**
* 地区
*/
private String region;
}

View File

@ -0,0 +1,154 @@
package com.cpop.core.handler;
import com.cpop.core.base.exception.UtilException;
import com.cpop.core.config.TencentCosProperties;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.model.PutObjectRequest;
import com.qcloud.cos.model.StorageClass;
import com.qcloud.cos.model.UploadResult;
import com.qcloud.cos.region.Region;
import com.qcloud.cos.transfer.TransferManager;
import com.qcloud.cos.transfer.TransferManagerConfiguration;
import com.qcloud.cos.transfer.Upload;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author DB
* @createTime 2023/10/31 15:03
* @description 腾讯云SDK相关工具类
*/
@Component
@EnableConfigurationProperties(TencentCosProperties.class)
public class TencentCosHandler {
/**
* 腾讯云配置
*/
@Autowired
private TencentCosProperties properties;
/**
* @descriptions 创建 TransferManager 实例这个实例用来后续调用高级接口
* @author DB
* @date 2023/10/31 16:48
* @return: com.qcloud.cos.transfer.TransferManager
*/
private TransferManager createTransferManager() {
// 创建一个 COSClient 实例这是访问 COS 服务的基础实例
// 详细代码参见本页: 简单操作 -> 创建 COSClient
COSClient cosClient = createCosClient();
// 自定义线程池大小建议在客户端与 COS 网络充足例如使用腾讯云的 CVM同地域上传 COS的情况下设置成16或32即可可较充分的利用网络资源
// 对于使用公网传输且网络带宽质量不高的情况建议减小该值避免因网速过慢造成请求超时
ExecutorService threadPool = Executors.newFixedThreadPool(32);
// 传入一个 threadPool, 若不传入线程池默认 TransferManager 中会生成一个单线程的线程池
TransferManager transferManager = new TransferManager(cosClient, threadPool);
// 设置高级接口的配置项
// 分块上传阈值和分块大小分别为 5MB 1MB
TransferManagerConfiguration transferManagerConfiguration = new TransferManagerConfiguration();
transferManagerConfiguration.setMultipartUploadThreshold(5 * 1024 * 1024);
transferManagerConfiguration.setMinimumUploadPartSize(1 * 1024 * 1024);
transferManager.setConfiguration(transferManagerConfiguration);
return transferManager;
}
/**
* @descriptions 创建 COSClient 实例这个实例用来后续调用请求
* @author DB
* @date 2023/10/31 16:47
* @return: com.qcloud.cos.COSClient
*/
private COSClient createCosClient() {
// 设置用户身份信息
// SECRETID SECRETKEY 请登录访问管理控制台 https://console.cloud.tencent.com/cam/capi 进行查看和管理
// 用户的 SecretId建议使用子账号密钥授权遵循最小权限指引降低使用风险子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
String secretId = properties.getSecretId();
// 用户的 SecretKey建议使用子账号密钥授权遵循最小权限指引降低使用风险子账号密钥获取可参见 https://cloud.tencent.com/document/product/598/37140
String secretKey = properties.getSecretKey();
COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
// ClientConfig 中包含了后续请求 COS 的客户端设置
ClientConfig clientConfig = new ClientConfig();
// 设置 bucket 的地域
// COS_REGION 请参见 https://cloud.tencent.com/document/product/436/6224
clientConfig.setRegion(new Region(properties.getRegion()));
// 设置请求协议, http 或者 https
// 5.6.53 及更低的版本建议设置使用 https 协议
// 5.6.54 及更高版本默认使用了 https
clientConfig.setHttpProtocol(HttpProtocol.https);
// 以下的设置是可选的
// 设置 socket 读取超时默认 30s
clientConfig.setSocketTimeout(30 * 1000);
// 设置建立连接超时默认 30s
clientConfig.setConnectionTimeout(30 * 1000);
// 如果需要的话设置 http 代理ip 以及 port
//clientConfig.setHttpProxyIp("httpProxyIp");
//clientConfig.setHttpProxyPort(80);
// 生成 cos 客户端
return new COSClient(cred, clientConfig);
}
/**
* @descriptions 关闭 TransferManager
* @author DB
* @date 2023/10/31 17:09
* @param transferManager 管理器
* @return: void
*/
private void shutdownTransferManager(TransferManager transferManager) {
// 指定参数为 true, 则同时会关闭 transferManager 内部的 COSClient 实例
// 指定参数为 false, 则不会关闭 transferManager 内部的 COSClient 实例
transferManager.shutdownNow(true);
}
/**
* @descriptions cdn上传
* @author DB
* @date 2023/10/31 17:21
* @param file 文件
* @return: com.qcloud.cos.model.UploadResult
*/
public UploadResult cdnUpload(MultipartFile file) {
TransferManager transferManager = createTransferManager();
try {
ObjectMetadata objectMetadata = new ObjectMetadata();
// 上传的流如果能够获取准确的流长度则推荐一定填写 content-length
// 如果确实没办法获取到则下面这行可以省略但同时高级接口也没办法使用分块上传了
objectMetadata.setContentLength(file.getSize());
objectMetadata.setContentType(file.getContentType());
InputStream inputStream = file.getInputStream();
PutObjectRequest putObjectRequest = new PutObjectRequest(properties.getBucketName(), properties.getSecretKey(),inputStream , objectMetadata);
// 设置存储类型如有需要不需要请忽略此行代码, 默认是标准(Standard), 低频(standard_ia)
// 更多存储类型请参见 https://cloud.tencent.com/document/product/436/33417
putObjectRequest.setStorageClass(StorageClass.Standard_IA);
Upload upload = transferManager.upload(putObjectRequest);
inputStream.close();
return upload.waitForUploadResult();
} catch (IOException | InterruptedException e) {
throw new UtilException(e);
} finally {
shutdownTransferManager(transferManager);
}
}
/**
* @descriptions 获取cdn路径
* @author DB
* @date 2023/10/31 17:56
* @return: java.lang.String
*/
public String getCdnUrl(){
return properties.getCdnUrl();
}
}

View File

@ -0,0 +1,12 @@
tencent:
cos:
#id
secretId: AKIDFK8hz0kDRP6XjdGciX5LK3VfYBWaMs7V
#密钥
secretKey: 92g38wUlkSt50e17wUllUw71pMcaIjtl
#
bucketName: dataresource-1302318474
#cdn地址
cdnUrl: .cos.accelerate.myqcloud.com/
#地区
region: ap-guangzhou

View File

@ -19,13 +19,13 @@ spring:
#redis配置
redis:
#地址
host: localhost
host: 106.52.49.102
#端口
port: 6379
port: 6333
#数据库
database: 0
database: 10
#密码
password:
password: Jambox.123*
#连接超时
timeout: 5000
jedis:
@ -55,9 +55,9 @@ mybatis-flex:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
datasource:
mall:
url: jdbc:mysql://localhost:3306/cpop-dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://sh-cynosdbmysql-grp-fggo83js.sql.tencentcdb.com:20965/cpop_test?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: root
password: root
password: Customer0401
jambox:
url: jdbc:mysql://sh-cynosdbmysql-grp-fggo83js.sql.tencentcdb.com:20965/jambox_test?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true
username: root
@ -105,4 +105,6 @@ logging:
wx:
pay:
#通知地址
notifyUrl: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/order
notifyUrl: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/order
#支付成功
notifyRefund: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/refund

View File

@ -31,7 +31,7 @@ spring:
max-file-size: 1024MB
max-request-size: 300MB
profiles:
active: test,mall,system
active: dev,core,mall,system
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver

View File

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Logback configuration. See http://logback.qos.ch/manual/index.html -->
<configuration scan="true" scanPeriod="10 seconds">
<!--<include resource="org/springframework/boot/logging/logback/base.xml" />-->
<!--定义日志文件的存储地址和前缀名-->
<property name="LOG_HOME" value="."/>
<property name="LOG_PREFIX" value="Cpop-Mall"/>
<!--控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<!--格式化输出:%d表示日期%-5level级别%thread表示线程名%file输出文件名%line文件行数%msg日志消息%n是换行符-->
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [%thread] %file:%line - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="SYS_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_HOME}/${LOG_PREFIX}-info.log</File>
<append>true</append>
<!--过滤器,只打INFO级别的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${LOG_PREFIX}-info-%d{yyyyMMdd}.log.%d</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [%thread] %file:%line - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="SYS_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<File>${LOG_HOME}/${LOG_PREFIX}-error.log</File>
<append>true</append>
<!--过滤器,只打ERROR级别的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/${LOG_PREFIX}-error-%d{yyyyMMdd}.log.%d</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %level [%thread] %file:%line - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--info和error分开打印-->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="SYS_INFO"/>
<appender-ref ref="SYS_ERROR"/>
</root>
<logger name="com.cpop.mall" level="DEBUG"/>
</configuration>

View File

@ -89,6 +89,12 @@ public class ProductBo implements Serializable {
@ApiModelProperty("购买限制(0:会员限制;1:新客限定;2:用户限购)")
private Integer buyRestrict;
/**
* 限制数量
*/
@ApiModelProperty("限制数量")
private Long limitNum;
/**
* 规格集合
*/

View File

@ -55,4 +55,16 @@ public class ProductPageBo implements Serializable {
@ApiModelProperty("产品id")
private String productId;
/**
* 上下架
*/
@ApiModelProperty("上下架")
private Boolean isUp;
/**
* 授权校区
*/
@ApiModelProperty("授权校区")
private String authorizedStoreId;
}

View File

@ -49,4 +49,10 @@ public class ProductRecordBo implements Serializable {
*/
@ApiModelProperty("记录消耗积分")
private Integer recordPoints;
/**
* 乐观锁标记
*/
@ApiModelProperty("乐观锁标记")
private Integer version;
}

View File

@ -170,6 +170,20 @@ public class BackstageProductController {
return R.ok();
}
/**
* @descriptions 商城商品置顶
* @author DB
* @date 2023/10/23 12:15
* @param productId 商城-商品id
* @return: com.cpop.core.base.R<java.lang.Void>
*/
@PutMapping("/productIsTop")
@ApiOperation("商城商品置顶")
public R<Void> productIsTop(@RequestParam("productId") @ApiParam(value = "商城-商品Id",required = true) String productId) {
productService.updateChain().setRaw(PRODUCT.IS_TOP, "if(is_top = 0, 1, 0)").where(PRODUCT.ID.eq(productId)).update();
return R.ok();
}
}

View File

@ -90,6 +90,11 @@ public class Product extends BaseEntity implements Serializable {
*/
private Boolean isUp;
/**
* 是否置顶
*/
private Boolean isTop;
/**
* 最高价
*/

View File

@ -89,6 +89,10 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
.and(PRODUCT.PRODUCT_NAME.like(bo.getProductName()))
.and(PRODUCT.PRODUCT_TYPE.eq(bo.getProductType()))
.and(PRODUCT.BUY_RESTRICT.eq(bo.getBuyRestrict()))
.and(PRODUCT.IS_UP.eq(bo.getIsUp()))
.and(PRODUCT.STORE_IDS.like(bo.getAuthorizedStoreId()))
//置顶
.orderBy(PRODUCT.IS_TOP.desc())
.orderBy(PRODUCT.CREATE_TIME.desc())
.orderBy(pageDomain.getOrderByColumn()),
ProductPageVo.class,
@ -244,15 +248,14 @@ public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> impl
productSpecifications.forEach(item -> {
item.setProductId(product.getId());
});
productSpecificationService.saveBatch(productSpecifications);
productSpecificationService.updateBatch(productSpecifications);
//存商品记录详情
List<ProductRecord> recordList = BeanUtils.mapToList(bo.getRecordList(), ProductRecord.class);
ProductRecordService specificationRecordService = SpringUtils.getBean(ProductRecordService.class);
specificationRecordService.updateChain().where(PRODUCT_RECORD.PRODUCT_ID.eq(product.getId())).remove();
recordList.forEach(item -> {
item.setProductId(product.getId());
});
specificationRecordService.saveBatch(recordList);
specificationRecordService.updateBatch(recordList);
}
/**

View File

@ -1,5 +1,6 @@
package com.cpop.mall.business.vo;
import com.cpop.core.annontation.StringArrayConvert;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.mybatisflex.annotation.RelationOneToMany;
import io.swagger.annotations.ApiModel;
@ -49,6 +50,7 @@ public class ProductPageVo implements Serializable {
/**
* 商店(校区)集合
*/
@StringArrayConvert
@ApiModelProperty("商店(校区)集合")
private String storeIds;

View File

@ -56,4 +56,10 @@ public class ProductRecordVo implements Serializable {
*/
@ApiModelProperty("记录消耗积分")
private Integer recordPoints;
/**
* 标记
*/
@ApiModelProperty("标记")
private Integer version;
}

View File

@ -3,9 +3,11 @@ package com.cpop.system.business.controller;
import com.cpop.core.base.R;
import com.cpop.core.config.CpopConfig;
import com.cpop.core.config.ServerConfig;
import com.cpop.core.handler.TencentCosHandler;
import com.cpop.core.utils.file.FileUploadUtils;
import com.cpop.core.utils.file.FileUtils;
import com.cpop.system.business.vo.SysFileVo;
import com.qcloud.cos.model.UploadResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@ -32,6 +34,9 @@ public class SysCommonController {
@Autowired
private CpopConfig cpopConfig;
@Autowired
private TencentCosHandler tencentCosHandler;
/**
* 通用上传请求单个
*/
@ -56,4 +61,24 @@ public class SysCommonController {
return R.fail(e.getMessage());
}
}
/**
* @descriptions cdn加速上传
* @author DB
* @date 2023/10/31 17:24
* @param file 文件
* @return: com.cpop.core.base.R<com.qcloud.cos.model.UploadResult>
*/
@ApiOperation("cdn加速上传")
@PostMapping("/cdnUpload")
public R<SysFileVo> cdnUpload(MultipartFile file) {
UploadResult uploadResult = tencentCosHandler.cdnUpload(file);
String filename = FileUploadUtils.getInstance().extractFilename(file);
SysFileVo sysFileVo = new SysFileVo();
sysFileVo.setUrl( "https://" + uploadResult.getBucketName() + tencentCosHandler.getCdnUrl() + uploadResult.getKey())
.setFileName(filename)
.setNewFileName(FileUtils.getInstance().getName(filename))
.setOriginalFilename(file.getOriginalFilename());
return R.ok(sysFileVo);
}
}